msm: qmi: Introduce sample QMI client for test service
The sample QMI client provides a test code for QMI usage. The test_service
client driver uses QMI interface library to send and receive QMI messages
over IPC Router. The test code sends a synchronous QMI request to the
test_service and handles the QMI responses.
Change-Id: I6f43ee56adf8c404131a7dc6b3ca92c6fc91e020
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 5b0ac52..d1545d5 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1568,6 +1568,16 @@
to perform QMI message marshaling and transport them over IPC
Router.
+config MSM_TEST_QMI_CLIENT
+ depends on MSM_QMI_INTERFACE
+ bool "MSM TEST QMI CLIENT"
+ help
+ The sample QMI client provides a test code for QMI usage. The
+ test_service client driver uses QMI interface library to send
+ and receive QMI messages over IPC Router. The test code sends
+ a synchronous QMI request to the test_service and handles the
+ QMI responses.
+
config MSM_ONCRPCROUTER_DEBUG
depends on MSM_ONCRPCROUTER
default y
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 594bb5f..2d617a9 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -138,6 +138,7 @@
obj-$(CONFIG_MSM_IPC_ROUTER)+= ipc_socket.o
obj-$(CONFIG_MSM_IPC_ROUTER_SECURITY)+= msm_ipc_router_security.o
obj-$(CONFIG_MSM_QMI_INTERFACE) += msm_qmi_interface.o
+obj-$(CONFIG_MSM_TEST_QMI_CLIENT) += kernel_test_service_v01.o test_qmi_client.o
obj-$(CONFIG_DEBUG_FS) += smd_rpc_sym.o
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_clients.o
diff --git a/arch/arm/mach-msm/kernel_test_service_v01.c b/arch/arm/mach-msm/kernel_test_service_v01.c
new file mode 100644
index 0000000..498e046
--- /dev/null
+++ b/arch/arm/mach-msm/kernel_test_service_v01.c
@@ -0,0 +1,254 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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/qmi_encdec.h>
+
+#include <mach/msm_qmi_interface.h>
+
+#include "kernel_test_service_v01.h"
+
+#define PING_REQ1_TLV_TYPE 0x1
+#define PING_RESP1_TLV_TYPE 0x2
+#define PING_OPT1_TLV_TYPE 0x10
+#define PING_OPT2_TLV_TYPE 0x11
+
+#define DATA_REQ1_TLV_TYPE 0x1
+#define DATA_RESP1_TLV_TYPE 0x2
+#define DATA_OPT1_TLV_TYPE 0x10
+#define DATA_OPT2_TLV_TYPE 0x11
+
+static struct elem_info test_name_type_v01_ei[] = {
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(struct test_name_type_v01,
+ name_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = TEST_MAX_NAME_SIZE_V01,
+ .elem_size = sizeof(char),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ .offset = offsetof(struct test_name_type_v01,
+ name),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info test_ping_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 4,
+ .elem_size = sizeof(char),
+ .is_array = STATIC_ARRAY,
+ .tlv_type = PING_REQ1_TLV_TYPE,
+ .offset = offsetof(struct test_ping_req_msg_v01,
+ ping),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = PING_OPT1_TLV_TYPE,
+ .offset = offsetof(struct test_ping_req_msg_v01,
+ client_name_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct test_name_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = PING_OPT1_TLV_TYPE,
+ .offset = offsetof(struct test_ping_req_msg_v01,
+ client_name),
+ .ei_array = test_name_type_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info test_ping_resp_msg_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = PING_RESP1_TLV_TYPE,
+ .offset = offsetof(struct test_ping_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = PING_OPT1_TLV_TYPE,
+ .offset = offsetof(struct test_ping_resp_msg_v01,
+ pong_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 4,
+ .elem_size = sizeof(char),
+ .is_array = STATIC_ARRAY,
+ .tlv_type = PING_OPT1_TLV_TYPE,
+ .offset = offsetof(struct test_ping_resp_msg_v01,
+ pong),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = PING_OPT2_TLV_TYPE,
+ .offset = offsetof(struct test_ping_resp_msg_v01,
+ service_name_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct test_name_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = PING_OPT2_TLV_TYPE,
+ .offset = offsetof(struct test_ping_resp_msg_v01,
+ service_name),
+ .ei_array = test_name_type_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info test_data_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = DATA_REQ1_TLV_TYPE,
+ .offset = offsetof(struct test_data_req_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = TEST_MED_DATA_SIZE_V01,
+ .elem_size = sizeof(uint8_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = DATA_REQ1_TLV_TYPE,
+ .offset = offsetof(struct test_data_req_msg_v01,
+ data),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = DATA_OPT1_TLV_TYPE,
+ .offset = offsetof(struct test_data_req_msg_v01,
+ client_name_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct test_name_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = DATA_OPT1_TLV_TYPE,
+ .offset = offsetof(struct test_data_req_msg_v01,
+ client_name),
+ .ei_array = test_name_type_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info test_data_resp_msg_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = DATA_RESP1_TLV_TYPE,
+ .offset = offsetof(struct test_data_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = DATA_OPT1_TLV_TYPE,
+ .offset = offsetof(struct test_data_resp_msg_v01,
+ data_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = DATA_OPT1_TLV_TYPE,
+ .offset = offsetof(struct test_data_resp_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = TEST_MED_DATA_SIZE_V01,
+ .elem_size = sizeof(uint8_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = DATA_OPT1_TLV_TYPE,
+ .offset = offsetof(struct test_data_resp_msg_v01,
+ data),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = DATA_OPT2_TLV_TYPE,
+ .offset = offsetof(struct test_data_resp_msg_v01,
+ service_name_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct test_name_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = DATA_OPT2_TLV_TYPE,
+ .offset = offsetof(struct test_data_resp_msg_v01,
+ service_name),
+ .ei_array = test_name_type_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
diff --git a/arch/arm/mach-msm/kernel_test_service_v01.h b/arch/arm/mach-msm/kernel_test_service_v01.h
new file mode 100644
index 0000000..79c1845
--- /dev/null
+++ b/arch/arm/mach-msm/kernel_test_service_v01.h
@@ -0,0 +1,73 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#ifndef MSM_QMI_TEST_SERVICE_V01_H
+#define MSM_QMI_TEST_SERVICE_V01_H
+
+#include <mach/msm_qmi_interface.h>
+
+#define TEST_MED_DATA_SIZE_V01 8192
+#define TEST_MAX_NAME_SIZE_V01 255
+
+#define TEST_PING_REQ_MSG_ID_V01 0x20
+#define TEST_DATA_REQ_MSG_ID_V01 0x21
+
+#define TEST_PING_REQ_MAX_MSG_LEN_V01 266
+#define TEST_DATA_REQ_MAX_MSG_LEN_V01 8456
+
+struct test_name_type_v01 {
+ uint32_t name_len;
+ char name[TEST_MAX_NAME_SIZE_V01];
+};
+
+struct test_ping_req_msg_v01 {
+ char ping[4];
+
+ uint8_t client_name_valid;
+ struct test_name_type_v01 client_name;
+};
+
+struct test_ping_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+
+ uint8_t pong_valid;
+ char pong[4];
+
+ uint8_t service_name_valid;
+ struct test_name_type_v01 service_name;
+};
+
+struct test_data_req_msg_v01 {
+ uint32_t data_len;
+ uint8_t data[TEST_MED_DATA_SIZE_V01];
+
+ uint8_t client_name_valid;
+ struct test_name_type_v01 client_name;
+};
+
+struct test_data_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+
+ uint8_t data_valid;
+ uint32_t data_len;
+ uint8_t data[TEST_MED_DATA_SIZE_V01];
+
+ uint8_t service_name_valid;
+ struct test_name_type_v01 service_name;
+};
+
+extern struct elem_info test_ping_req_msg_v01_ei[];
+extern struct elem_info test_data_req_msg_v01_ei[];
+extern struct elem_info test_ping_resp_msg_v01_ei[];
+extern struct elem_info test_data_resp_msg_v01_ei[];
+
+#endif
diff --git a/arch/arm/mach-msm/test_qmi_client.c b/arch/arm/mach-msm/test_qmi_client.c
new file mode 100644
index 0000000..d070e37
--- /dev/null
+++ b/arch/arm/mach-msm/test_qmi_client.c
@@ -0,0 +1,344 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/qmi_encdec.h>
+
+#include <asm/uaccess.h>
+
+#include <mach/msm_qmi_interface.h>
+
+#include "kernel_test_service_v01.h"
+
+#define TEST_SERVICE_SVC_ID 0x0000000f
+#define TEST_SERVICE_INS_ID 1
+
+static int test_rep_cnt = 10;
+module_param_named(rep_cnt, test_rep_cnt, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static int test_data_sz = 50;
+module_param_named(data_sz, test_data_sz, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static int test_clnt_debug_mask;
+module_param_named(debug_mask, test_clnt_debug_mask,
+ int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define D(x...) do { \
+ if (test_clnt_debug_mask) \
+ pr_debug(x); \
+} while (0)
+
+/* Variable to initiate the test through debugfs interface */
+static struct dentry *test_dent;
+
+/* Test client port for IPC Router */
+static struct qmi_handle *test_clnt;
+static int test_clnt_reset;
+
+/* Reader thread to receive responses & indications */
+static void test_clnt_recv_msg(struct work_struct *work);
+static DECLARE_DELAYED_WORK(work_recv_msg, test_clnt_recv_msg);
+static void test_clnt_svc_arrive(struct work_struct *work);
+static DECLARE_DELAYED_WORK(work_svc_arrive, test_clnt_svc_arrive);
+static void test_clnt_svc_exit(struct work_struct *work);
+static DECLARE_DELAYED_WORK(work_svc_exit, test_clnt_svc_exit);
+static struct workqueue_struct *test_clnt_workqueue;
+
+/* Variable to hold the test result */
+static int test_res;
+
+static int test_qmi_ping_pong_send_sync_msg(void)
+{
+ struct test_ping_req_msg_v01 req;
+ struct test_ping_resp_msg_v01 resp;
+ struct msg_desc req_desc, resp_desc;
+ int rc;
+
+ memcpy(req.ping, "ping", sizeof(req.ping));
+ req.client_name_valid = 0;
+
+ req_desc.max_msg_len = TEST_PING_REQ_MAX_MSG_LEN_V01;
+ req_desc.msg_id = TEST_PING_REQ_MSG_ID_V01;
+ req_desc.ei_array = test_ping_req_msg_v01_ei;
+
+ resp_desc.max_msg_len = TEST_PING_REQ_MAX_MSG_LEN_V01;
+ resp_desc.msg_id = TEST_PING_REQ_MSG_ID_V01;
+ resp_desc.ei_array = test_ping_resp_msg_v01_ei;
+
+ rc = qmi_send_req_wait(test_clnt, &req_desc, &req, sizeof(req),
+ &resp_desc, &resp, sizeof(resp), 0);
+ if (rc < 0) {
+ pr_err("%s: send req failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ D("%s: Received %s response\n", __func__, resp.pong);
+ return rc;
+}
+
+static int test_qmi_data_send_sync_msg(unsigned int data_len)
+{
+ struct test_data_req_msg_v01 *req;
+ struct test_data_resp_msg_v01 *resp;
+ struct msg_desc req_desc, resp_desc;
+ int rc, i;
+
+ req = kzalloc(sizeof(struct test_data_req_msg_v01), GFP_KERNEL);
+ if (!req) {
+ pr_err("%s: Data req msg alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ resp = kzalloc(sizeof(struct test_data_resp_msg_v01), GFP_KERNEL);
+ if (!resp) {
+ pr_err("%s: Data resp msg alloc failed\n", __func__);
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->data_len = data_len;
+ for (i = 0; i < data_len; i = i + sizeof(int))
+ memcpy(req->data + i, (uint8_t *)&i, sizeof(int));
+ req->client_name_valid = 0;
+
+ req_desc.max_msg_len = TEST_DATA_REQ_MAX_MSG_LEN_V01;
+ req_desc.msg_id = TEST_DATA_REQ_MSG_ID_V01;
+ req_desc.ei_array = test_data_req_msg_v01_ei;
+
+ resp_desc.max_msg_len = TEST_DATA_REQ_MAX_MSG_LEN_V01;
+ resp_desc.msg_id = TEST_DATA_REQ_MSG_ID_V01;
+ resp_desc.ei_array = test_data_resp_msg_v01_ei;
+
+ rc = qmi_send_req_wait(test_clnt, &req_desc, req, sizeof(*req),
+ &resp_desc, resp, sizeof(*resp), 0);
+ if (rc < 0) {
+ pr_err("%s: send req failed\n", __func__);
+ goto data_send_err;
+ }
+
+ D("%s: data_valid %d\n", __func__, resp->data_valid);
+ D("%s: data_len %d\n", __func__, resp->data_len);
+data_send_err:
+ kfree(resp);
+ kfree(req);
+ return rc;
+}
+
+static void test_clnt_recv_msg(struct work_struct *work)
+{
+ int rc;
+
+ rc = qmi_recv_msg(test_clnt);
+ if (rc < 0)
+ pr_err("%s: Error receiving message\n", __func__);
+}
+
+static void test_clnt_notify(struct qmi_handle *handle,
+ enum qmi_event_type event, void *notify_priv)
+{
+ switch (event) {
+ case QMI_RECV_MSG:
+ queue_delayed_work(test_clnt_workqueue,
+ &work_recv_msg, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static void test_clnt_svc_arrive(struct work_struct *work)
+{
+ int rc;
+
+ D("%s begins\n", __func__);
+
+ /* Create a Local client port for QMI communication */
+ test_clnt = qmi_handle_create(test_clnt_notify, NULL);
+ if (!test_clnt) {
+ pr_err("%s: QMI client handle alloc failed\n", __func__);
+ return;
+ }
+
+ D("%s: Lookup server name\n", __func__);
+ rc = qmi_connect_to_service(test_clnt, TEST_SERVICE_SVC_ID,
+ TEST_SERVICE_INS_ID);
+ if (rc < 0) {
+ pr_err("%s: Server not found\n", __func__);
+ qmi_handle_destroy(test_clnt);
+ test_clnt = NULL;
+ return;
+ }
+ test_clnt_reset = 0;
+ D("%s complete\n", __func__);
+}
+
+static void test_clnt_svc_exit(struct work_struct *work)
+{
+ D("%s begins\n", __func__);
+
+ qmi_handle_destroy(test_clnt);
+ test_clnt_reset = 1;
+ test_clnt = NULL;
+
+ D("%s complete\n", __func__);
+}
+
+static int test_clnt_svc_event_notify(struct notifier_block *this,
+ unsigned long code,
+ void *_cmd)
+{
+ D("%s: event %ld\n", __func__, code);
+ switch (code) {
+ case QMI_SERVER_ARRIVE:
+ queue_delayed_work(test_clnt_workqueue,
+ &work_svc_arrive, 0);
+ break;
+ case QMI_SERVER_EXIT:
+ queue_delayed_work(test_clnt_workqueue,
+ &work_svc_exit, 0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int test_qmi_open(struct inode *ip, struct file *fp)
+{
+ if (!test_clnt) {
+ pr_err("%s Test client is not initialized\n", __func__);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static ssize_t test_qmi_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char _buf[16];
+ snprintf(_buf, sizeof(_buf), "%d\n", test_res);
+ test_res = 0;
+ return simple_read_from_buffer(buf, count, pos,
+ _buf, strnlen(_buf, 16));
+}
+
+static int test_qmi_release(struct inode *ip, struct file *fp)
+{
+ return 0;
+}
+
+static ssize_t test_qmi_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ unsigned char cmd[64];
+ int len;
+ int i;
+
+ if (count < 1)
+ return 0;
+
+ len = min(count, (sizeof(cmd) - 1));
+
+ if (copy_from_user(cmd, buf, len))
+ return -EFAULT;
+
+ cmd[len] = 0;
+ if (cmd[len-1] == '\n') {
+ cmd[len-1] = 0;
+ len--;
+ }
+
+ if (!strncmp(cmd, "ping_pong", sizeof(cmd))) {
+ for (i = 0; i < test_rep_cnt; i++) {
+ test_res = test_qmi_ping_pong_send_sync_msg();
+ if (test_res == -ENETRESET || test_clnt_reset) {
+ do {
+ msleep(50);
+ } while (test_clnt_reset);
+ }
+ }
+ } else if (!strncmp(cmd, "data", sizeof(cmd))) {
+ for (i = 0; i < test_rep_cnt; i++) {
+ test_res = test_qmi_data_send_sync_msg(test_data_sz);
+ if (test_res == -ENETRESET || test_clnt_reset) {
+ do {
+ msleep(50);
+ } while (test_clnt_reset);
+ }
+ }
+ } else {
+ test_res = -EINVAL;
+ }
+ return count;
+}
+
+static struct notifier_block test_clnt_nb = {
+ .notifier_call = test_clnt_svc_event_notify,
+};
+
+static const struct file_operations debug_ops = {
+ .owner = THIS_MODULE,
+ .open = test_qmi_open,
+ .read = test_qmi_read,
+ .write = test_qmi_write,
+ .release = test_qmi_release,
+};
+
+static int __init test_qmi_init(void)
+{
+ int rc;
+
+ test_clnt_workqueue = create_singlethread_workqueue("test_clnt");
+ if (!test_clnt_workqueue)
+ return -EFAULT;
+
+ rc = qmi_svc_event_notifier_register(TEST_SERVICE_SVC_ID,
+ TEST_SERVICE_INS_ID, &test_clnt_nb);
+ if (rc < 0) {
+ pr_err("%s: notifier register failed\n", __func__);
+ destroy_workqueue(test_clnt_workqueue);
+ return rc;
+ }
+
+ test_dent = debugfs_create_file("test_qmi_client", 0444, 0,
+ NULL, &debug_ops);
+ if (IS_ERR(test_dent)) {
+ pr_err("%s: unable to create debugfs %ld\n",
+ __func__, IS_ERR(test_dent));
+ test_dent = NULL;
+ qmi_svc_event_notifier_unregister(TEST_SERVICE_SVC_ID,
+ TEST_SERVICE_INS_ID, &test_clnt_nb);
+ destroy_workqueue(test_clnt_workqueue);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static void __exit test_qmi_exit(void)
+{
+ qmi_svc_event_notifier_unregister(TEST_SERVICE_SVC_ID,
+ TEST_SERVICE_INS_ID, &test_clnt_nb);
+ destroy_workqueue(test_clnt_workqueue);
+ debugfs_remove(test_dent);
+}
+
+module_init(test_qmi_init);
+module_exit(test_qmi_exit);
+
+MODULE_DESCRIPTION("TEST QMI Client Driver");
+MODULE_LICENSE("GPL v2");