[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/devtools/met-driver/4.4/common/ondiemet_log.c b/src/devtools/met-driver/4.4/common/ondiemet_log.c
new file mode 100644
index 0000000..4f3ad69
--- /dev/null
+++ b/src/devtools/met-driver/4.4/common/ondiemet_log.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/completion.h>
+
+#include "ondiemet_log.h"
+
+#define ONDIEMET_LOG_REQ 1
+/* TODO: abandon this constatnt */
+#define ONDIEMET_LOG_STOP 2
+
+#define PID_NONE (-1)
+
+#define ONDIEMET_LOG_STOP_MODE 0
+#define ONDIEMET_LOG_RUN_MODE 1
+#define ONDIEMET_LOG_DEBUG_MODE 2
+
+static int ondiemet_trace_run;
+static struct dentry *dbgfs_met_dir;
+
+struct mutex lock_tracef;
+struct ondiemet_log_req_q_t {
+	struct list_head listq;
+	struct mutex lockq;
+	/* struct semaphore new_evt_sema; */
+	struct completion new_evt_comp;
+	int closeq_flag;
+} ondiemet_log_req_q;
+
+struct ondiemet_log_req {
+	struct list_head list;
+	int cmd_type;
+	const char *src;
+	size_t num;
+
+	void (*on_fini_cb)(const void *p);
+	const void *param;
+};
+
+#define __ondiemet_log_req_init(req, cmd, s, n, pf, p)	\
+	do {						\
+		INIT_LIST_HEAD(&req->list);		\
+		req->cmd_type = cmd;			\
+		req->src = s;				\
+		req->num = n;				\
+		req->on_fini_cb = pf;			\
+		req->param = p;				\
+	} while (0)
+
+#define __ondiemet_log_req_fini(req)		        \
+	do {					        \
+		if (req->on_fini_cb)			\
+			req->on_fini_cb(req->param);	\
+		kfree(req);				\
+	} while (0)
+
+static void __ondiemet_log_req_q_init(struct ondiemet_log_req_q_t *q)
+{
+	INIT_LIST_HEAD(&q->listq);
+	mutex_init(&q->lockq);
+	/* sema_init(&q->new_evt_sema, 0); */
+	init_completion(&q->new_evt_comp);
+	q->closeq_flag = 1;
+}
+
+/* undequeue is seen as a roll-back operation, so it can be done even when the queue is closed */
+static void __ondiemet_log_req_undeq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	list_add(&req->list, &ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+}
+
+static int __ondiemet_log_req_enq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	if (ondiemet_log_req_q.closeq_flag) {
+		mutex_unlock(&ondiemet_log_req_q.lockq);
+		return -EBUSY;
+	}
+
+	list_add_tail(&req->list, &ondiemet_log_req_q.listq);
+	if (req->cmd_type == ONDIEMET_LOG_STOP)
+		ondiemet_log_req_q.closeq_flag = 1;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+
+	return 0;
+}
+
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb)(const void *p), const void *param)
+{
+	struct ondiemet_log_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_REQ, src, num, on_fini_cb, param);
+	return __ondiemet_log_req_enq(req);
+}
+
+/*int down_freezable_interruptible(struct semaphore *sem) */
+int down_freezable_interruptible(struct completion *comp)
+{
+
+	int ret;
+
+	freezer_do_not_count();
+	/* ret = down_interruptible(sem); */
+	ret = wait_for_completion_interruptible(comp);
+	freezer_count();
+
+	return ret;
+}
+
+struct ondiemet_log_req *__ondiemet_log_req_deq(void)
+{
+	struct ondiemet_log_req *ret_req;
+
+	/*if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_sema))*/
+	if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_comp))
+		return NULL;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret_req = list_entry(ondiemet_log_req_q.listq.next, struct ondiemet_log_req, list);
+	list_del_init(&ret_req->list);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret_req;
+}
+
+void __ondiemet_log_req_open(void)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ondiemet_log_req_q.closeq_flag = 0;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+}
+
+int __ondiemet_log_req_closed(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = ondiemet_log_req_q.closeq_flag && list_empty(&ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+int __ondiemet_log_req_working(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = !ondiemet_log_req_q.closeq_flag;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+static void *__ondiemet_trace_seq_next(struct seq_file *seqf, loff_t *offset)
+{
+	struct ondiemet_log_req *next_req;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] __ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	if (__ondiemet_log_req_closed())
+		return NULL;
+
+	next_req = __ondiemet_log_req_deq();
+
+	if (next_req == NULL)
+		return NULL;
+
+	if (next_req->cmd_type == ONDIEMET_LOG_STOP) {
+		__ondiemet_log_req_fini(next_req);
+		return NULL;
+	}
+
+	return (void *) next_req;
+}
+
+struct mutex lock_trace_owner_pid;
+pid_t trace_owner_pid = PID_NONE;
+static void *ondiemet_trace_seq_start(struct seq_file *seqf, loff_t *offset)
+{
+	void *ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE) {
+		pr_debug("[met] ondiemet_trace_seq_start: locked_pid: %d, pid: %d, offset: %llu\n",
+			 trace_owner_pid, current->pid, *offset);
+	}
+
+	if (!mutex_trylock(&lock_tracef))
+		return NULL;
+
+	mutex_lock(&lock_trace_owner_pid);
+	trace_owner_pid = current->pid;
+	mutex_unlock(&lock_trace_owner_pid);
+
+	ret = __ondiemet_trace_seq_next(seqf, offset);
+
+	return ret;
+}
+
+static void *ondiemet_trace_seq_next(struct seq_file *seqf, void *p, loff_t *offset)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	(*offset)++;
+	return __ondiemet_trace_seq_next(seqf, offset);
+}
+
+static int ondiemet_trace_seq_show(struct seq_file *seqf, void *p)
+{
+	struct ondiemet_log_req *req = (struct ondiemet_log_req *) p;
+	size_t l_sz;
+	size_t r_sz;
+	struct ondiemet_log_req *l_req;
+	struct ondiemet_log_req *r_req;
+	int ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_show: pid: %d\n", current->pid);
+
+	if (req->num >= seqf->size) {
+		l_req = kmalloc(sizeof(*req), GFP_KERNEL);
+		r_req = req;
+
+		l_sz = seqf->size >> 1;
+		r_sz = req->num - l_sz;
+		__ondiemet_log_req_init(l_req, ONDIEMET_LOG_REQ, req->src, l_sz, NULL, NULL);
+		__ondiemet_log_req_init(r_req, ONDIEMET_LOG_REQ, req->src + l_sz,
+					r_sz, req->on_fini_cb, req->param);
+
+		__ondiemet_log_req_undeq(r_req);
+		req = l_req;
+
+		if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+			pr_debug("[met] ondiemet_trace_seq_show: split request\n");
+	}
+
+	ret = seq_write(seqf, req->src, req->num);
+
+	if (ret) {
+		/* check if seq_file buffer overflows */
+		if (seqf->count == seqf->size) {
+			__ondiemet_log_req_undeq(req);
+		} else {
+			if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+				pr_debug("[met] ondiemet_trace_seq_show: reading trace record failed, some data may be lost or corrupted\n");
+			__ondiemet_log_req_fini(req);
+		}
+		return 0;
+	}
+
+	__ondiemet_log_req_fini(req);
+	return 0;
+}
+
+static void ondiemet_trace_seq_stop(struct seq_file *seqf, void *p)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_stop: pid: %d\n", current->pid);
+
+	mutex_lock(&lock_trace_owner_pid);
+	if (current->pid == trace_owner_pid) {
+		trace_owner_pid = PID_NONE;
+		mutex_unlock(&lock_tracef);
+	}
+	mutex_unlock(&lock_trace_owner_pid);
+}
+
+static const struct seq_operations ondiemet_trace_seq_ops = {
+	.start = ondiemet_trace_seq_start,
+	.next = ondiemet_trace_seq_next,
+	.stop = ondiemet_trace_seq_stop,
+	.show = ondiemet_trace_seq_show
+};
+
+static int ondiemet_trace_open(struct inode *inode, struct file *fp)
+{
+	return seq_open(fp, &ondiemet_trace_seq_ops);
+}
+
+static const struct file_operations ondiemet_trace_fops = {
+	.owner = THIS_MODULE,
+	.open = ondiemet_trace_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release
+};
+
+/*struct semaphore log_start_sema;*/
+struct completion log_start_comp;
+int ondiemet_log_manager_start(void)
+{
+	int ret;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_working())
+		return -EINVAL;
+
+	if (!__ondiemet_log_req_closed()) {
+		/*ret = down_killable(&log_start_sema);*/
+		ret = wait_for_completion_killable(&log_start_comp);
+		if (ret)
+			return ret;
+	}
+
+	__ondiemet_log_req_open();
+
+	return 0;
+}
+
+/*struct semaphore log_stop_sema;*/
+struct completion log_stop_comp;
+static void __log_stop_cb(const void *p)
+{
+	/* up(&log_start_sema); */
+	/* up(&log_stop_sema); */
+	complete(&log_start_comp);
+	complete(&log_stop_comp);
+}
+
+int ondiemet_log_manager_stop(void)
+{
+	int ret;
+	struct ondiemet_log_req *req;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_closed())
+		return -EINVAL;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_STOP, NULL, 0, __log_stop_cb, NULL);
+	/*sema_init(&log_start_sema, 0); */
+	/*sema_init(&log_stop_sema, 0); */
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	ret = __ondiemet_log_req_enq(req);
+	if (ret)
+		return ret;
+
+	/* XXX: blocking may be break by SIGKILL */
+	/*return down_killable(&log_stop_sema);*/
+	return wait_for_completion_killable(&log_stop_comp);
+}
+
+int ondiemet_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+	    ((str[0] == '0') &&
+	     ((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtouint(str, 16, value);
+	} else {
+		ret = kstrtouint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+/* XXX: seq_file will output only when a page is filled */
+static ssize_t ondiemet_log_write_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	char *plog = NULL;
+
+	plog = kmalloc_array(count, sizeof(*plog), GFP_KERNEL);
+	if (!plog) {
+		/* TODO: use a better error code */
+		return -EINVAL;
+	}
+
+	memcpy(plog, buf, count);
+
+	mutex_lock(&dev->mutex);
+	ondiemet_log_req_enq(plog, strnlen(plog, count), kfree, plog);
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_write, 0664, NULL, ondiemet_log_write_store);
+
+static ssize_t ondiemet_log_run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int sz;
+
+	mutex_lock(&dev->mutex);
+	sz = snprintf(buf, PAGE_SIZE, "%d\n", ondiemet_trace_run);
+	mutex_unlock(&dev->mutex);
+	return sz;
+}
+
+static ssize_t ondiemet_log_run_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	int prev_run_state;
+
+	mutex_lock(&dev->mutex);
+
+	prev_run_state = ondiemet_trace_run;
+
+	if (kstrtoint(buf, 10, &ondiemet_trace_run) != 0)
+		return -EINVAL;
+
+	if (ondiemet_trace_run <= ONDIEMET_LOG_STOP_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_STOP_MODE;
+		ondiemet_log_manager_stop();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else if (ondiemet_trace_run == ONDIEMET_LOG_RUN_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_RUN_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else {
+		ondiemet_trace_run = ONDIEMET_LOG_DEBUG_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state != ONDIEMET_LOG_DEBUG_MODE) {
+			ret = device_create_file(dev, &dev_attr_ondiemet_log_write);
+			if (ret != 0)
+				pr_debug("[met] can not create device node: ondiemet_log_write\n");
+		}
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_run, 0660, ondiemet_log_run_show, ondiemet_log_run_store);
+
+int ondiemet_log_manager_init(struct device *dev)
+{
+	int ret;
+	struct dentry *d;
+
+	mutex_init(&lock_tracef);
+
+	__ondiemet_log_req_q_init(&ondiemet_log_req_q);
+
+	/*sema_init(&log_start_sema, 0);*/
+	/*sema_init(&log_stop_sema, 0);*/
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	dbgfs_met_dir = debugfs_create_dir("ondiemet", NULL);
+	if (!dbgfs_met_dir) {
+		pr_debug("[met] can not create debugfs directory: met\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&lock_trace_owner_pid);
+
+	d = debugfs_create_file("trace", 0644, dbgfs_met_dir, NULL, &ondiemet_trace_fops);
+	if (!d) {
+		pr_debug("[met] can not create devide node in debugfs: ondiemet_trace\n");
+		return -ENOMEM;
+	}
+
+	ondiemet_trace_run = __ondiemet_log_req_working();
+	ret = device_create_file(dev, &dev_attr_ondiemet_log_run);
+	if (ret != 0) {
+		pr_debug("[met] can not create device node: ondiemet_log_run\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int ondiemet_log_manager_uninit(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_ondiemet_log_run);
+	debugfs_remove_recursive(dbgfs_met_dir);
+	return 0;
+}