18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Sample in-kernel QMI client driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
68c2ecf20Sopenharmony_ci * Copyright (C) 2017 Linaro Ltd.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
118c2ecf20Sopenharmony_ci#include <linux/device.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/qrtr.h>
148c2ecf20Sopenharmony_ci#include <linux/net.h>
158c2ecf20Sopenharmony_ci#include <linux/completion.h>
168c2ecf20Sopenharmony_ci#include <linux/idr.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <net/sock.h>
198c2ecf20Sopenharmony_ci#include <linux/soc/qcom/qmi.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define PING_REQ1_TLV_TYPE		0x1
228c2ecf20Sopenharmony_ci#define PING_RESP1_TLV_TYPE		0x2
238c2ecf20Sopenharmony_ci#define PING_OPT1_TLV_TYPE		0x10
248c2ecf20Sopenharmony_ci#define PING_OPT2_TLV_TYPE		0x11
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define DATA_REQ1_TLV_TYPE		0x1
278c2ecf20Sopenharmony_ci#define DATA_RESP1_TLV_TYPE		0x2
288c2ecf20Sopenharmony_ci#define DATA_OPT1_TLV_TYPE		0x10
298c2ecf20Sopenharmony_ci#define DATA_OPT2_TLV_TYPE		0x11
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define TEST_MED_DATA_SIZE_V01		8192
328c2ecf20Sopenharmony_ci#define TEST_MAX_NAME_SIZE_V01		255
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define TEST_PING_REQ_MSG_ID_V01	0x20
358c2ecf20Sopenharmony_ci#define TEST_DATA_REQ_MSG_ID_V01	0x21
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define TEST_PING_REQ_MAX_MSG_LEN_V01	266
388c2ecf20Sopenharmony_ci#define TEST_DATA_REQ_MAX_MSG_LEN_V01	8456
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct test_name_type_v01 {
418c2ecf20Sopenharmony_ci	u32 name_len;
428c2ecf20Sopenharmony_ci	char name[TEST_MAX_NAME_SIZE_V01];
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic struct qmi_elem_info test_name_type_v01_ei[] = {
468c2ecf20Sopenharmony_ci	{
478c2ecf20Sopenharmony_ci		.data_type	= QMI_DATA_LEN,
488c2ecf20Sopenharmony_ci		.elem_len	= 1,
498c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u8),
508c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
518c2ecf20Sopenharmony_ci		.tlv_type	= QMI_COMMON_TLV_TYPE,
528c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_name_type_v01,
538c2ecf20Sopenharmony_ci					   name_len),
548c2ecf20Sopenharmony_ci	},
558c2ecf20Sopenharmony_ci	{
568c2ecf20Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
578c2ecf20Sopenharmony_ci		.elem_len	= TEST_MAX_NAME_SIZE_V01,
588c2ecf20Sopenharmony_ci		.elem_size	= sizeof(char),
598c2ecf20Sopenharmony_ci		.array_type	= VAR_LEN_ARRAY,
608c2ecf20Sopenharmony_ci		.tlv_type	= QMI_COMMON_TLV_TYPE,
618c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_name_type_v01,
628c2ecf20Sopenharmony_ci					   name),
638c2ecf20Sopenharmony_ci	},
648c2ecf20Sopenharmony_ci	{}
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistruct test_ping_req_msg_v01 {
688c2ecf20Sopenharmony_ci	char ping[4];
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	u8 client_name_valid;
718c2ecf20Sopenharmony_ci	struct test_name_type_v01 client_name;
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic struct qmi_elem_info test_ping_req_msg_v01_ei[] = {
758c2ecf20Sopenharmony_ci	{
768c2ecf20Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
778c2ecf20Sopenharmony_ci		.elem_len	= 4,
788c2ecf20Sopenharmony_ci		.elem_size	= sizeof(char),
798c2ecf20Sopenharmony_ci		.array_type	= STATIC_ARRAY,
808c2ecf20Sopenharmony_ci		.tlv_type	= PING_REQ1_TLV_TYPE,
818c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_ping_req_msg_v01,
828c2ecf20Sopenharmony_ci					   ping),
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	{
858c2ecf20Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
868c2ecf20Sopenharmony_ci		.elem_len	= 1,
878c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u8),
888c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
898c2ecf20Sopenharmony_ci		.tlv_type	= PING_OPT1_TLV_TYPE,
908c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_ping_req_msg_v01,
918c2ecf20Sopenharmony_ci					   client_name_valid),
928c2ecf20Sopenharmony_ci	},
938c2ecf20Sopenharmony_ci	{
948c2ecf20Sopenharmony_ci		.data_type	= QMI_STRUCT,
958c2ecf20Sopenharmony_ci		.elem_len	= 1,
968c2ecf20Sopenharmony_ci		.elem_size	= sizeof(struct test_name_type_v01),
978c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
988c2ecf20Sopenharmony_ci		.tlv_type	= PING_OPT1_TLV_TYPE,
998c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_ping_req_msg_v01,
1008c2ecf20Sopenharmony_ci					   client_name),
1018c2ecf20Sopenharmony_ci		.ei_array	= test_name_type_v01_ei,
1028c2ecf20Sopenharmony_ci	},
1038c2ecf20Sopenharmony_ci	{}
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistruct test_ping_resp_msg_v01 {
1078c2ecf20Sopenharmony_ci	struct qmi_response_type_v01 resp;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	u8 pong_valid;
1108c2ecf20Sopenharmony_ci	char pong[4];
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	u8 service_name_valid;
1138c2ecf20Sopenharmony_ci	struct test_name_type_v01 service_name;
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic struct qmi_elem_info test_ping_resp_msg_v01_ei[] = {
1178c2ecf20Sopenharmony_ci	{
1188c2ecf20Sopenharmony_ci		.data_type	= QMI_STRUCT,
1198c2ecf20Sopenharmony_ci		.elem_len	= 1,
1208c2ecf20Sopenharmony_ci		.elem_size	= sizeof(struct qmi_response_type_v01),
1218c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
1228c2ecf20Sopenharmony_ci		.tlv_type	= PING_RESP1_TLV_TYPE,
1238c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
1248c2ecf20Sopenharmony_ci					   resp),
1258c2ecf20Sopenharmony_ci		.ei_array	= qmi_response_type_v01_ei,
1268c2ecf20Sopenharmony_ci	},
1278c2ecf20Sopenharmony_ci	{
1288c2ecf20Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
1298c2ecf20Sopenharmony_ci		.elem_len	= 1,
1308c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u8),
1318c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
1328c2ecf20Sopenharmony_ci		.tlv_type	= PING_OPT1_TLV_TYPE,
1338c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
1348c2ecf20Sopenharmony_ci					   pong_valid),
1358c2ecf20Sopenharmony_ci	},
1368c2ecf20Sopenharmony_ci	{
1378c2ecf20Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
1388c2ecf20Sopenharmony_ci		.elem_len	= 4,
1398c2ecf20Sopenharmony_ci		.elem_size	= sizeof(char),
1408c2ecf20Sopenharmony_ci		.array_type	= STATIC_ARRAY,
1418c2ecf20Sopenharmony_ci		.tlv_type	= PING_OPT1_TLV_TYPE,
1428c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
1438c2ecf20Sopenharmony_ci					   pong),
1448c2ecf20Sopenharmony_ci	},
1458c2ecf20Sopenharmony_ci	{
1468c2ecf20Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
1478c2ecf20Sopenharmony_ci		.elem_len	= 1,
1488c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u8),
1498c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
1508c2ecf20Sopenharmony_ci		.tlv_type	= PING_OPT2_TLV_TYPE,
1518c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
1528c2ecf20Sopenharmony_ci					   service_name_valid),
1538c2ecf20Sopenharmony_ci	},
1548c2ecf20Sopenharmony_ci	{
1558c2ecf20Sopenharmony_ci		.data_type	= QMI_STRUCT,
1568c2ecf20Sopenharmony_ci		.elem_len	= 1,
1578c2ecf20Sopenharmony_ci		.elem_size	= sizeof(struct test_name_type_v01),
1588c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
1598c2ecf20Sopenharmony_ci		.tlv_type	= PING_OPT2_TLV_TYPE,
1608c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
1618c2ecf20Sopenharmony_ci					   service_name),
1628c2ecf20Sopenharmony_ci		.ei_array	= test_name_type_v01_ei,
1638c2ecf20Sopenharmony_ci	},
1648c2ecf20Sopenharmony_ci	{}
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistruct test_data_req_msg_v01 {
1688c2ecf20Sopenharmony_ci	u32 data_len;
1698c2ecf20Sopenharmony_ci	u8 data[TEST_MED_DATA_SIZE_V01];
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	u8 client_name_valid;
1728c2ecf20Sopenharmony_ci	struct test_name_type_v01 client_name;
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic struct qmi_elem_info test_data_req_msg_v01_ei[] = {
1768c2ecf20Sopenharmony_ci	{
1778c2ecf20Sopenharmony_ci		.data_type	= QMI_DATA_LEN,
1788c2ecf20Sopenharmony_ci		.elem_len	= 1,
1798c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u32),
1808c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
1818c2ecf20Sopenharmony_ci		.tlv_type	= DATA_REQ1_TLV_TYPE,
1828c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_req_msg_v01,
1838c2ecf20Sopenharmony_ci					   data_len),
1848c2ecf20Sopenharmony_ci	},
1858c2ecf20Sopenharmony_ci	{
1868c2ecf20Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
1878c2ecf20Sopenharmony_ci		.elem_len	= TEST_MED_DATA_SIZE_V01,
1888c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u8),
1898c2ecf20Sopenharmony_ci		.array_type	= VAR_LEN_ARRAY,
1908c2ecf20Sopenharmony_ci		.tlv_type	= DATA_REQ1_TLV_TYPE,
1918c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_req_msg_v01,
1928c2ecf20Sopenharmony_ci					   data),
1938c2ecf20Sopenharmony_ci	},
1948c2ecf20Sopenharmony_ci	{
1958c2ecf20Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
1968c2ecf20Sopenharmony_ci		.elem_len	= 1,
1978c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u8),
1988c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
1998c2ecf20Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
2008c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_req_msg_v01,
2018c2ecf20Sopenharmony_ci					   client_name_valid),
2028c2ecf20Sopenharmony_ci	},
2038c2ecf20Sopenharmony_ci	{
2048c2ecf20Sopenharmony_ci		.data_type	= QMI_STRUCT,
2058c2ecf20Sopenharmony_ci		.elem_len	= 1,
2068c2ecf20Sopenharmony_ci		.elem_size	= sizeof(struct test_name_type_v01),
2078c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2088c2ecf20Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
2098c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_req_msg_v01,
2108c2ecf20Sopenharmony_ci					   client_name),
2118c2ecf20Sopenharmony_ci		.ei_array	= test_name_type_v01_ei,
2128c2ecf20Sopenharmony_ci	},
2138c2ecf20Sopenharmony_ci	{}
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistruct test_data_resp_msg_v01 {
2178c2ecf20Sopenharmony_ci	struct qmi_response_type_v01 resp;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	u8 data_valid;
2208c2ecf20Sopenharmony_ci	u32 data_len;
2218c2ecf20Sopenharmony_ci	u8 data[TEST_MED_DATA_SIZE_V01];
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	u8 service_name_valid;
2248c2ecf20Sopenharmony_ci	struct test_name_type_v01 service_name;
2258c2ecf20Sopenharmony_ci};
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic struct qmi_elem_info test_data_resp_msg_v01_ei[] = {
2288c2ecf20Sopenharmony_ci	{
2298c2ecf20Sopenharmony_ci		.data_type	= QMI_STRUCT,
2308c2ecf20Sopenharmony_ci		.elem_len	= 1,
2318c2ecf20Sopenharmony_ci		.elem_size	= sizeof(struct qmi_response_type_v01),
2328c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2338c2ecf20Sopenharmony_ci		.tlv_type	= DATA_RESP1_TLV_TYPE,
2348c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
2358c2ecf20Sopenharmony_ci					   resp),
2368c2ecf20Sopenharmony_ci		.ei_array	= qmi_response_type_v01_ei,
2378c2ecf20Sopenharmony_ci	},
2388c2ecf20Sopenharmony_ci	{
2398c2ecf20Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
2408c2ecf20Sopenharmony_ci		.elem_len	= 1,
2418c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u8),
2428c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2438c2ecf20Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
2448c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
2458c2ecf20Sopenharmony_ci					   data_valid),
2468c2ecf20Sopenharmony_ci	},
2478c2ecf20Sopenharmony_ci	{
2488c2ecf20Sopenharmony_ci		.data_type	= QMI_DATA_LEN,
2498c2ecf20Sopenharmony_ci		.elem_len	= 1,
2508c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u32),
2518c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2528c2ecf20Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
2538c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
2548c2ecf20Sopenharmony_ci					   data_len),
2558c2ecf20Sopenharmony_ci	},
2568c2ecf20Sopenharmony_ci	{
2578c2ecf20Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
2588c2ecf20Sopenharmony_ci		.elem_len	= TEST_MED_DATA_SIZE_V01,
2598c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u8),
2608c2ecf20Sopenharmony_ci		.array_type	= VAR_LEN_ARRAY,
2618c2ecf20Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
2628c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
2638c2ecf20Sopenharmony_ci					   data),
2648c2ecf20Sopenharmony_ci	},
2658c2ecf20Sopenharmony_ci	{
2668c2ecf20Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
2678c2ecf20Sopenharmony_ci		.elem_len	= 1,
2688c2ecf20Sopenharmony_ci		.elem_size	= sizeof(u8),
2698c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2708c2ecf20Sopenharmony_ci		.tlv_type	= DATA_OPT2_TLV_TYPE,
2718c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
2728c2ecf20Sopenharmony_ci					   service_name_valid),
2738c2ecf20Sopenharmony_ci	},
2748c2ecf20Sopenharmony_ci	{
2758c2ecf20Sopenharmony_ci		.data_type	= QMI_STRUCT,
2768c2ecf20Sopenharmony_ci		.elem_len	= 1,
2778c2ecf20Sopenharmony_ci		.elem_size	= sizeof(struct test_name_type_v01),
2788c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2798c2ecf20Sopenharmony_ci		.tlv_type	= DATA_OPT2_TLV_TYPE,
2808c2ecf20Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
2818c2ecf20Sopenharmony_ci					   service_name),
2828c2ecf20Sopenharmony_ci		.ei_array	= test_name_type_v01_ei,
2838c2ecf20Sopenharmony_ci	},
2848c2ecf20Sopenharmony_ci	{}
2858c2ecf20Sopenharmony_ci};
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci * ping_write() - ping_pong debugfs file write handler
2898c2ecf20Sopenharmony_ci * @file:	debugfs file context
2908c2ecf20Sopenharmony_ci * @user_buf:	reference to the user data (ignored)
2918c2ecf20Sopenharmony_ci * @count:	number of bytes in @user_buf
2928c2ecf20Sopenharmony_ci * @ppos:	offset in @file to write
2938c2ecf20Sopenharmony_ci *
2948c2ecf20Sopenharmony_ci * This function allows user space to send out a ping_pong QMI encoded message
2958c2ecf20Sopenharmony_ci * to the associated remote test service and will return with the result of the
2968c2ecf20Sopenharmony_ci * transaction. It serves as an example of how to provide a custom response
2978c2ecf20Sopenharmony_ci * handler.
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci * Return: @count, or negative errno on failure.
3008c2ecf20Sopenharmony_ci */
3018c2ecf20Sopenharmony_cistatic ssize_t ping_write(struct file *file, const char __user *user_buf,
3028c2ecf20Sopenharmony_ci			  size_t count, loff_t *ppos)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct qmi_handle *qmi = file->private_data;
3058c2ecf20Sopenharmony_ci	struct test_ping_req_msg_v01 req = {};
3068c2ecf20Sopenharmony_ci	struct qmi_txn txn;
3078c2ecf20Sopenharmony_ci	int ret;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	memcpy(req.ping, "ping", sizeof(req.ping));
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	ret = qmi_txn_init(qmi, &txn, NULL, NULL);
3128c2ecf20Sopenharmony_ci	if (ret < 0)
3138c2ecf20Sopenharmony_ci		return ret;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	ret = qmi_send_request(qmi, NULL, &txn,
3168c2ecf20Sopenharmony_ci			       TEST_PING_REQ_MSG_ID_V01,
3178c2ecf20Sopenharmony_ci			       TEST_PING_REQ_MAX_MSG_LEN_V01,
3188c2ecf20Sopenharmony_ci			       test_ping_req_msg_v01_ei, &req);
3198c2ecf20Sopenharmony_ci	if (ret < 0) {
3208c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
3218c2ecf20Sopenharmony_ci		return ret;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
3258c2ecf20Sopenharmony_ci	if (ret < 0)
3268c2ecf20Sopenharmony_ci		count = ret;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return count;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic const struct file_operations ping_fops = {
3328c2ecf20Sopenharmony_ci	.open = simple_open,
3338c2ecf20Sopenharmony_ci	.write = ping_write,
3348c2ecf20Sopenharmony_ci};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
3378c2ecf20Sopenharmony_ci			 struct qmi_txn *txn, const void *data)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	const struct test_ping_resp_msg_v01 *resp = data;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (!txn) {
3428c2ecf20Sopenharmony_ci		pr_err("spurious ping response\n");
3438c2ecf20Sopenharmony_ci		return;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (resp->resp.result == QMI_RESULT_FAILURE_V01)
3478c2ecf20Sopenharmony_ci		txn->result = -ENXIO;
3488c2ecf20Sopenharmony_ci	else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4))
3498c2ecf20Sopenharmony_ci		txn->result = -EINVAL;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	complete(&txn->completion);
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci/*
3558c2ecf20Sopenharmony_ci * data_write() - data debugfs file write handler
3568c2ecf20Sopenharmony_ci * @file:	debugfs file context
3578c2ecf20Sopenharmony_ci * @user_buf:	reference to the user data
3588c2ecf20Sopenharmony_ci * @count:	number of bytes in @user_buf
3598c2ecf20Sopenharmony_ci * @ppos:	offset in @file to write
3608c2ecf20Sopenharmony_ci *
3618c2ecf20Sopenharmony_ci * This function allows user space to send out a data QMI encoded message to
3628c2ecf20Sopenharmony_ci * the associated remote test service and will return with the result of the
3638c2ecf20Sopenharmony_ci * transaction. It serves as an example of how to have the QMI helpers decode a
3648c2ecf20Sopenharmony_ci * transaction response into a provided object automatically.
3658c2ecf20Sopenharmony_ci *
3668c2ecf20Sopenharmony_ci * Return: @count, or negative errno on failure.
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_cistatic ssize_t data_write(struct file *file, const char __user *user_buf,
3698c2ecf20Sopenharmony_ci			  size_t count, loff_t *ppos)
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct qmi_handle *qmi = file->private_data;
3738c2ecf20Sopenharmony_ci	struct test_data_resp_msg_v01 *resp;
3748c2ecf20Sopenharmony_ci	struct test_data_req_msg_v01 *req;
3758c2ecf20Sopenharmony_ci	struct qmi_txn txn;
3768c2ecf20Sopenharmony_ci	int ret;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
3798c2ecf20Sopenharmony_ci	if (!req)
3808c2ecf20Sopenharmony_ci		return -ENOMEM;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
3838c2ecf20Sopenharmony_ci	if (!resp) {
3848c2ecf20Sopenharmony_ci		kfree(req);
3858c2ecf20Sopenharmony_ci		return -ENOMEM;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	req->data_len = min_t(size_t, sizeof(req->data), count);
3898c2ecf20Sopenharmony_ci	if (copy_from_user(req->data, user_buf, req->data_len)) {
3908c2ecf20Sopenharmony_ci		ret = -EFAULT;
3918c2ecf20Sopenharmony_ci		goto out;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp);
3958c2ecf20Sopenharmony_ci	if (ret < 0)
3968c2ecf20Sopenharmony_ci		goto out;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	ret = qmi_send_request(qmi, NULL, &txn,
3998c2ecf20Sopenharmony_ci			       TEST_DATA_REQ_MSG_ID_V01,
4008c2ecf20Sopenharmony_ci			       TEST_DATA_REQ_MAX_MSG_LEN_V01,
4018c2ecf20Sopenharmony_ci			       test_data_req_msg_v01_ei, req);
4028c2ecf20Sopenharmony_ci	if (ret < 0) {
4038c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
4048c2ecf20Sopenharmony_ci		goto out;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
4088c2ecf20Sopenharmony_ci	if (ret < 0) {
4098c2ecf20Sopenharmony_ci		goto out;
4108c2ecf20Sopenharmony_ci	} else if (!resp->data_valid ||
4118c2ecf20Sopenharmony_ci		   resp->data_len != req->data_len ||
4128c2ecf20Sopenharmony_ci		   memcmp(resp->data, req->data, req->data_len)) {
4138c2ecf20Sopenharmony_ci		pr_err("response data doesn't match expectation\n");
4148c2ecf20Sopenharmony_ci		ret = -EINVAL;
4158c2ecf20Sopenharmony_ci		goto out;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	ret = count;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ciout:
4218c2ecf20Sopenharmony_ci	kfree(resp);
4228c2ecf20Sopenharmony_ci	kfree(req);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return ret;
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic const struct file_operations data_fops = {
4288c2ecf20Sopenharmony_ci	.open = simple_open,
4298c2ecf20Sopenharmony_ci	.write = data_write,
4308c2ecf20Sopenharmony_ci};
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic struct qmi_msg_handler qmi_sample_handlers[] = {
4338c2ecf20Sopenharmony_ci	{
4348c2ecf20Sopenharmony_ci		.type = QMI_RESPONSE,
4358c2ecf20Sopenharmony_ci		.msg_id = TEST_PING_REQ_MSG_ID_V01,
4368c2ecf20Sopenharmony_ci		.ei = test_ping_resp_msg_v01_ei,
4378c2ecf20Sopenharmony_ci		.decoded_size = sizeof(struct test_ping_req_msg_v01),
4388c2ecf20Sopenharmony_ci		.fn = ping_pong_cb
4398c2ecf20Sopenharmony_ci	},
4408c2ecf20Sopenharmony_ci	{}
4418c2ecf20Sopenharmony_ci};
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistruct qmi_sample {
4448c2ecf20Sopenharmony_ci	struct qmi_handle qmi;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	struct dentry *de_dir;
4478c2ecf20Sopenharmony_ci	struct dentry *de_data;
4488c2ecf20Sopenharmony_ci	struct dentry *de_ping;
4498c2ecf20Sopenharmony_ci};
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic struct dentry *qmi_debug_dir;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic int qmi_sample_probe(struct platform_device *pdev)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct sockaddr_qrtr *sq;
4568c2ecf20Sopenharmony_ci	struct qmi_sample *sample;
4578c2ecf20Sopenharmony_ci	char path[20];
4588c2ecf20Sopenharmony_ci	int ret;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL);
4618c2ecf20Sopenharmony_ci	if (!sample)
4628c2ecf20Sopenharmony_ci		return -ENOMEM;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01,
4658c2ecf20Sopenharmony_ci			      NULL,
4668c2ecf20Sopenharmony_ci			      qmi_sample_handlers);
4678c2ecf20Sopenharmony_ci	if (ret < 0)
4688c2ecf20Sopenharmony_ci		return ret;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	sq = dev_get_platdata(&pdev->dev);
4718c2ecf20Sopenharmony_ci	ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq,
4728c2ecf20Sopenharmony_ci			     sizeof(*sq), 0);
4738c2ecf20Sopenharmony_ci	if (ret < 0) {
4748c2ecf20Sopenharmony_ci		pr_err("failed to connect to remote service port\n");
4758c2ecf20Sopenharmony_ci		goto err_release_qmi_handle;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	sample->de_dir = debugfs_create_dir(path, qmi_debug_dir);
4818c2ecf20Sopenharmony_ci	if (IS_ERR(sample->de_dir)) {
4828c2ecf20Sopenharmony_ci		ret = PTR_ERR(sample->de_dir);
4838c2ecf20Sopenharmony_ci		goto err_release_qmi_handle;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	sample->de_data = debugfs_create_file("data", 0600, sample->de_dir,
4878c2ecf20Sopenharmony_ci					      sample, &data_fops);
4888c2ecf20Sopenharmony_ci	if (IS_ERR(sample->de_data)) {
4898c2ecf20Sopenharmony_ci		ret = PTR_ERR(sample->de_data);
4908c2ecf20Sopenharmony_ci		goto err_remove_de_dir;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir,
4948c2ecf20Sopenharmony_ci					      sample, &ping_fops);
4958c2ecf20Sopenharmony_ci	if (IS_ERR(sample->de_ping)) {
4968c2ecf20Sopenharmony_ci		ret = PTR_ERR(sample->de_ping);
4978c2ecf20Sopenharmony_ci		goto err_remove_de_data;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, sample);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	return 0;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cierr_remove_de_data:
5058c2ecf20Sopenharmony_ci	debugfs_remove(sample->de_data);
5068c2ecf20Sopenharmony_cierr_remove_de_dir:
5078c2ecf20Sopenharmony_ci	debugfs_remove(sample->de_dir);
5088c2ecf20Sopenharmony_cierr_release_qmi_handle:
5098c2ecf20Sopenharmony_ci	qmi_handle_release(&sample->qmi);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	return ret;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic int qmi_sample_remove(struct platform_device *pdev)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	struct qmi_sample *sample = platform_get_drvdata(pdev);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	debugfs_remove(sample->de_ping);
5198c2ecf20Sopenharmony_ci	debugfs_remove(sample->de_data);
5208c2ecf20Sopenharmony_ci	debugfs_remove(sample->de_dir);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	qmi_handle_release(&sample->qmi);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return 0;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic struct platform_driver qmi_sample_driver = {
5288c2ecf20Sopenharmony_ci	.probe = qmi_sample_probe,
5298c2ecf20Sopenharmony_ci	.remove = qmi_sample_remove,
5308c2ecf20Sopenharmony_ci	.driver = {
5318c2ecf20Sopenharmony_ci		.name = "qmi_sample_client",
5328c2ecf20Sopenharmony_ci	},
5338c2ecf20Sopenharmony_ci};
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic int qmi_sample_new_server(struct qmi_handle *qmi,
5368c2ecf20Sopenharmony_ci				 struct qmi_service *service)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct platform_device *pdev;
5398c2ecf20Sopenharmony_ci	struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port };
5408c2ecf20Sopenharmony_ci	int ret;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	pdev = platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO);
5438c2ecf20Sopenharmony_ci	if (!pdev)
5448c2ecf20Sopenharmony_ci		return -ENOMEM;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	ret = platform_device_add_data(pdev, &sq, sizeof(sq));
5478c2ecf20Sopenharmony_ci	if (ret)
5488c2ecf20Sopenharmony_ci		goto err_put_device;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	ret = platform_device_add(pdev);
5518c2ecf20Sopenharmony_ci	if (ret)
5528c2ecf20Sopenharmony_ci		goto err_put_device;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	service->priv = pdev;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cierr_put_device:
5598c2ecf20Sopenharmony_ci	platform_device_put(pdev);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	return ret;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic void qmi_sample_del_server(struct qmi_handle *qmi,
5658c2ecf20Sopenharmony_ci				  struct qmi_service *service)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	struct platform_device *pdev = service->priv;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	platform_device_unregister(pdev);
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic struct qmi_handle lookup_client;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic struct qmi_ops lookup_ops = {
5758c2ecf20Sopenharmony_ci	.new_server = qmi_sample_new_server,
5768c2ecf20Sopenharmony_ci	.del_server = qmi_sample_del_server,
5778c2ecf20Sopenharmony_ci};
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int qmi_sample_init(void)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	int ret;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	qmi_debug_dir = debugfs_create_dir("qmi_sample", NULL);
5848c2ecf20Sopenharmony_ci	if (IS_ERR(qmi_debug_dir)) {
5858c2ecf20Sopenharmony_ci		pr_err("failed to create qmi_sample dir\n");
5868c2ecf20Sopenharmony_ci		return PTR_ERR(qmi_debug_dir);
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	ret = platform_driver_register(&qmi_sample_driver);
5908c2ecf20Sopenharmony_ci	if (ret)
5918c2ecf20Sopenharmony_ci		goto err_remove_debug_dir;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	ret = qmi_handle_init(&lookup_client, 0, &lookup_ops, NULL);
5948c2ecf20Sopenharmony_ci	if (ret < 0)
5958c2ecf20Sopenharmony_ci		goto err_unregister_driver;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	qmi_add_lookup(&lookup_client, 15, 0, 0);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	return 0;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cierr_unregister_driver:
6028c2ecf20Sopenharmony_ci	platform_driver_unregister(&qmi_sample_driver);
6038c2ecf20Sopenharmony_cierr_remove_debug_dir:
6048c2ecf20Sopenharmony_ci	debugfs_remove(qmi_debug_dir);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	return ret;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic void qmi_sample_exit(void)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	qmi_handle_release(&lookup_client);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	platform_driver_unregister(&qmi_sample_driver);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	debugfs_remove(qmi_debug_dir);
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cimodule_init(qmi_sample_init);
6198c2ecf20Sopenharmony_cimodule_exit(qmi_sample_exit);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sample QMI client driver");
6228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
623