Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3c1b2a7b authored by Pushkar Joshi's avatar Pushkar Joshi
Browse files

soc: qcom: Add infrastructure to test service notifier



Add debugfs node based mechanism to test the service notifier
driver functionality.

Change-Id: I9dc7a6f0f51c57da7f783c7c60aae9018462079e
Signed-off-by: default avatarPushkar Joshi <pushkarj@codeaurora.org>
parent 998e63e0
Loading
Loading
Loading
Loading
+179 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>

#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
@@ -60,6 +62,7 @@
#define QMI_STATE_MAX_VAL QMI_SERVREG_NOTIF_SERVICE_STATE_ENUM_TYPE_MAX_VAL_V01

#define SERVER_TIMEOUT				500
#define MAX_STRING_LEN				100

/*
 * Per user service data structure
@@ -659,3 +662,179 @@ int service_notif_unregister_notifier(void *service_notif_handle,
				&service_notif->service_notif_rcvr_list, nb);
}
EXPORT_SYMBOL(service_notif_unregister_notifier);

struct service_notifier_test_data {
	char service_path[MAX_STRING_LEN];
	int instance_id;
	struct notifier_block nb;
	void *service_notif_handle;
};

static struct service_notifier_test_data test_data;

static void print_service_provider_state(int notification, char *type)
{
	if (notification == SERVREG_NOTIF_SERVICE_STATE_DOWN_V01)
		pr_info("%s: Service %s down!\n", type, test_data.service_path);
	else if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
		pr_info("%s: Service %s up!\n", type, test_data.service_path);
	else if (notification == SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01)
		pr_info("%s: Service %s state uninit!\n", type,
						test_data.service_path);
	else
		pr_info("%s: Service %s state Unknown 0x%x!\n", type,
					test_data.service_path, notification);
}

static int nb_callback(struct notifier_block *nb,
				  unsigned long notification,
				  void *data)
{
	print_service_provider_state((int)notification, "Notification:");
	return 0;
}

static ssize_t show_service_path(struct seq_file *f, void *unused)
{
	if (test_data.service_notif_handle)
		seq_printf(f, "Service Path: %s\n", test_data.service_path);
	else
		seq_puts(f, "No existing notifier\n");
	return 0;
}


static ssize_t set_service_notifier_register(struct file *fp,
						const char __user *buf,
						size_t count, loff_t *ppos)
{
	int curr_state = INT_MAX, rc;

	if (!buf)
		return -EIO;
	if (test_data.service_notif_handle) {
		service_notif_unregister_notifier(
						test_data.service_notif_handle,
						&test_data.nb);
		test_data.service_notif_handle = NULL;
		pr_info("Unregistering existing notifier for %s\n",
							test_data.service_path);
	}
	rc = simple_write_to_buffer(test_data.service_path, MAX_STRING_LEN,
							ppos, buf, count - 1);
	if (rc != count - 1) {
		pr_err("Unable to read data into kernel buffer\n");
		goto err;
	}
	test_data.nb.notifier_call = nb_callback;
	test_data.service_notif_handle = service_notif_register_notifier(
					test_data.service_path,
					test_data.instance_id, &test_data.nb,
					&curr_state);
	if (!IS_ERR(test_data.service_notif_handle)) {
		pr_info("Notifier Registered for service %s\n",
						test_data.service_path);
		print_service_provider_state(curr_state, "Initial State");
		return count;
	}
err:
	test_data.service_notif_handle = NULL;
	pr_err("Unable to register notifier for %s\n", test_data.service_path);
	return -EIO;
}

static int open_service_notifier_register(struct inode *inode, struct file *f)
{
	return single_open(f, (void *) show_service_path,
							inode->i_private);
}

static const struct file_operations service_notifier_register_fops = {
	.open = open_service_notifier_register,
	.read = seq_read,
	.write = set_service_notifier_register,
	.llseek = seq_lseek,
	.release = seq_release,
};

static ssize_t show_service_notifier_id(struct seq_file *f, void *unused)
{
	seq_printf(f, "Service instance ID: %d\n", test_data.instance_id);
	return 0;
}

static ssize_t set_service_notifier_id(struct file *fp,
						const char __user *buf,
						size_t count, loff_t *unused)
{
	int val, rc;
	char kbuf[MAX_STRING_LEN];

	if (MAX_STRING_LEN < count) {
		rc = -EIO;
		goto err;
	}
	rc = copy_from_user(kbuf, buf, count);
	if (rc != 0) {
		rc = -EFAULT;
		goto err;
	}

	kbuf[count - 1] = '\0';
	rc = kstrtoint(kbuf, 0, &val);
	if (rc < 0)
		goto err;

	test_data.instance_id = val;
	return count;
err:
	pr_err("Invalid input parameters: rc = %d\n", rc);
	return rc;
}

static int open_service_notifier_id(struct inode *inode, struct file *f)
{
	return single_open(f, (void *) show_service_notifier_id,
							inode->i_private);
}

static const struct file_operations service_notifier_id_fops = {
	.open = open_service_notifier_id,
	.read = seq_read,
	.write = set_service_notifier_id,
	.llseek = seq_lseek,
	.release = seq_release,
};

static struct dentry *service_notifier_dir;
static struct dentry *service_path_file;
static struct dentry *service_id_file;

static int __init service_notifier_init(void)
{
	service_notifier_dir = debugfs_create_dir("service_notifier", NULL);
	if (service_notifier_dir) {
		service_path_file = debugfs_create_file("service_path",
				S_IRUGO | S_IWUSR, service_notifier_dir, NULL,
				&service_notifier_register_fops);
		if (!service_path_file)
			goto err;
		service_id_file = debugfs_create_file("service_id",
				S_IRUGO | S_IWUSR, service_notifier_dir, NULL,
				&service_notifier_id_fops);
		if (!service_id_file)
			goto err;
	}
	return 0;
err:
	debugfs_remove_recursive(service_notifier_dir);
	return 0;
}

static void __exit service_notifier_exit(void)
{
	debugfs_remove_recursive(service_notifier_dir);
	test_data.nb.notifier_call = nb_callback;
}
module_init(service_notifier_init);
module_exit(service_notifier_exit);