xref: /OK3568_Linux_fs/kernel/samples/qmi/qmi_sample_client.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Sample in-kernel QMI client driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
6*4882a593Smuzhiyun  * Copyright (C) 2017 Linaro Ltd.
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/debugfs.h>
11*4882a593Smuzhiyun #include <linux/device.h>
12*4882a593Smuzhiyun #include <linux/platform_device.h>
13*4882a593Smuzhiyun #include <linux/qrtr.h>
14*4882a593Smuzhiyun #include <linux/net.h>
15*4882a593Smuzhiyun #include <linux/completion.h>
16*4882a593Smuzhiyun #include <linux/idr.h>
17*4882a593Smuzhiyun #include <linux/string.h>
18*4882a593Smuzhiyun #include <net/sock.h>
19*4882a593Smuzhiyun #include <linux/soc/qcom/qmi.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #define PING_REQ1_TLV_TYPE		0x1
22*4882a593Smuzhiyun #define PING_RESP1_TLV_TYPE		0x2
23*4882a593Smuzhiyun #define PING_OPT1_TLV_TYPE		0x10
24*4882a593Smuzhiyun #define PING_OPT2_TLV_TYPE		0x11
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #define DATA_REQ1_TLV_TYPE		0x1
27*4882a593Smuzhiyun #define DATA_RESP1_TLV_TYPE		0x2
28*4882a593Smuzhiyun #define DATA_OPT1_TLV_TYPE		0x10
29*4882a593Smuzhiyun #define DATA_OPT2_TLV_TYPE		0x11
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #define TEST_MED_DATA_SIZE_V01		8192
32*4882a593Smuzhiyun #define TEST_MAX_NAME_SIZE_V01		255
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #define TEST_PING_REQ_MSG_ID_V01	0x20
35*4882a593Smuzhiyun #define TEST_DATA_REQ_MSG_ID_V01	0x21
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #define TEST_PING_REQ_MAX_MSG_LEN_V01	266
38*4882a593Smuzhiyun #define TEST_DATA_REQ_MAX_MSG_LEN_V01	8456
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun struct test_name_type_v01 {
41*4882a593Smuzhiyun 	u32 name_len;
42*4882a593Smuzhiyun 	char name[TEST_MAX_NAME_SIZE_V01];
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun static struct qmi_elem_info test_name_type_v01_ei[] = {
46*4882a593Smuzhiyun 	{
47*4882a593Smuzhiyun 		.data_type	= QMI_DATA_LEN,
48*4882a593Smuzhiyun 		.elem_len	= 1,
49*4882a593Smuzhiyun 		.elem_size	= sizeof(u8),
50*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
51*4882a593Smuzhiyun 		.tlv_type	= QMI_COMMON_TLV_TYPE,
52*4882a593Smuzhiyun 		.offset		= offsetof(struct test_name_type_v01,
53*4882a593Smuzhiyun 					   name_len),
54*4882a593Smuzhiyun 	},
55*4882a593Smuzhiyun 	{
56*4882a593Smuzhiyun 		.data_type	= QMI_UNSIGNED_1_BYTE,
57*4882a593Smuzhiyun 		.elem_len	= TEST_MAX_NAME_SIZE_V01,
58*4882a593Smuzhiyun 		.elem_size	= sizeof(char),
59*4882a593Smuzhiyun 		.array_type	= VAR_LEN_ARRAY,
60*4882a593Smuzhiyun 		.tlv_type	= QMI_COMMON_TLV_TYPE,
61*4882a593Smuzhiyun 		.offset		= offsetof(struct test_name_type_v01,
62*4882a593Smuzhiyun 					   name),
63*4882a593Smuzhiyun 	},
64*4882a593Smuzhiyun 	{}
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun struct test_ping_req_msg_v01 {
68*4882a593Smuzhiyun 	char ping[4];
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	u8 client_name_valid;
71*4882a593Smuzhiyun 	struct test_name_type_v01 client_name;
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun static struct qmi_elem_info test_ping_req_msg_v01_ei[] = {
75*4882a593Smuzhiyun 	{
76*4882a593Smuzhiyun 		.data_type	= QMI_UNSIGNED_1_BYTE,
77*4882a593Smuzhiyun 		.elem_len	= 4,
78*4882a593Smuzhiyun 		.elem_size	= sizeof(char),
79*4882a593Smuzhiyun 		.array_type	= STATIC_ARRAY,
80*4882a593Smuzhiyun 		.tlv_type	= PING_REQ1_TLV_TYPE,
81*4882a593Smuzhiyun 		.offset		= offsetof(struct test_ping_req_msg_v01,
82*4882a593Smuzhiyun 					   ping),
83*4882a593Smuzhiyun 	},
84*4882a593Smuzhiyun 	{
85*4882a593Smuzhiyun 		.data_type	= QMI_OPT_FLAG,
86*4882a593Smuzhiyun 		.elem_len	= 1,
87*4882a593Smuzhiyun 		.elem_size	= sizeof(u8),
88*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
89*4882a593Smuzhiyun 		.tlv_type	= PING_OPT1_TLV_TYPE,
90*4882a593Smuzhiyun 		.offset		= offsetof(struct test_ping_req_msg_v01,
91*4882a593Smuzhiyun 					   client_name_valid),
92*4882a593Smuzhiyun 	},
93*4882a593Smuzhiyun 	{
94*4882a593Smuzhiyun 		.data_type	= QMI_STRUCT,
95*4882a593Smuzhiyun 		.elem_len	= 1,
96*4882a593Smuzhiyun 		.elem_size	= sizeof(struct test_name_type_v01),
97*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
98*4882a593Smuzhiyun 		.tlv_type	= PING_OPT1_TLV_TYPE,
99*4882a593Smuzhiyun 		.offset		= offsetof(struct test_ping_req_msg_v01,
100*4882a593Smuzhiyun 					   client_name),
101*4882a593Smuzhiyun 		.ei_array	= test_name_type_v01_ei,
102*4882a593Smuzhiyun 	},
103*4882a593Smuzhiyun 	{}
104*4882a593Smuzhiyun };
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun struct test_ping_resp_msg_v01 {
107*4882a593Smuzhiyun 	struct qmi_response_type_v01 resp;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	u8 pong_valid;
110*4882a593Smuzhiyun 	char pong[4];
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	u8 service_name_valid;
113*4882a593Smuzhiyun 	struct test_name_type_v01 service_name;
114*4882a593Smuzhiyun };
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun static struct qmi_elem_info test_ping_resp_msg_v01_ei[] = {
117*4882a593Smuzhiyun 	{
118*4882a593Smuzhiyun 		.data_type	= QMI_STRUCT,
119*4882a593Smuzhiyun 		.elem_len	= 1,
120*4882a593Smuzhiyun 		.elem_size	= sizeof(struct qmi_response_type_v01),
121*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
122*4882a593Smuzhiyun 		.tlv_type	= PING_RESP1_TLV_TYPE,
123*4882a593Smuzhiyun 		.offset		= offsetof(struct test_ping_resp_msg_v01,
124*4882a593Smuzhiyun 					   resp),
125*4882a593Smuzhiyun 		.ei_array	= qmi_response_type_v01_ei,
126*4882a593Smuzhiyun 	},
127*4882a593Smuzhiyun 	{
128*4882a593Smuzhiyun 		.data_type	= QMI_OPT_FLAG,
129*4882a593Smuzhiyun 		.elem_len	= 1,
130*4882a593Smuzhiyun 		.elem_size	= sizeof(u8),
131*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
132*4882a593Smuzhiyun 		.tlv_type	= PING_OPT1_TLV_TYPE,
133*4882a593Smuzhiyun 		.offset		= offsetof(struct test_ping_resp_msg_v01,
134*4882a593Smuzhiyun 					   pong_valid),
135*4882a593Smuzhiyun 	},
136*4882a593Smuzhiyun 	{
137*4882a593Smuzhiyun 		.data_type	= QMI_UNSIGNED_1_BYTE,
138*4882a593Smuzhiyun 		.elem_len	= 4,
139*4882a593Smuzhiyun 		.elem_size	= sizeof(char),
140*4882a593Smuzhiyun 		.array_type	= STATIC_ARRAY,
141*4882a593Smuzhiyun 		.tlv_type	= PING_OPT1_TLV_TYPE,
142*4882a593Smuzhiyun 		.offset		= offsetof(struct test_ping_resp_msg_v01,
143*4882a593Smuzhiyun 					   pong),
144*4882a593Smuzhiyun 	},
145*4882a593Smuzhiyun 	{
146*4882a593Smuzhiyun 		.data_type	= QMI_OPT_FLAG,
147*4882a593Smuzhiyun 		.elem_len	= 1,
148*4882a593Smuzhiyun 		.elem_size	= sizeof(u8),
149*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
150*4882a593Smuzhiyun 		.tlv_type	= PING_OPT2_TLV_TYPE,
151*4882a593Smuzhiyun 		.offset		= offsetof(struct test_ping_resp_msg_v01,
152*4882a593Smuzhiyun 					   service_name_valid),
153*4882a593Smuzhiyun 	},
154*4882a593Smuzhiyun 	{
155*4882a593Smuzhiyun 		.data_type	= QMI_STRUCT,
156*4882a593Smuzhiyun 		.elem_len	= 1,
157*4882a593Smuzhiyun 		.elem_size	= sizeof(struct test_name_type_v01),
158*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
159*4882a593Smuzhiyun 		.tlv_type	= PING_OPT2_TLV_TYPE,
160*4882a593Smuzhiyun 		.offset		= offsetof(struct test_ping_resp_msg_v01,
161*4882a593Smuzhiyun 					   service_name),
162*4882a593Smuzhiyun 		.ei_array	= test_name_type_v01_ei,
163*4882a593Smuzhiyun 	},
164*4882a593Smuzhiyun 	{}
165*4882a593Smuzhiyun };
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun struct test_data_req_msg_v01 {
168*4882a593Smuzhiyun 	u32 data_len;
169*4882a593Smuzhiyun 	u8 data[TEST_MED_DATA_SIZE_V01];
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	u8 client_name_valid;
172*4882a593Smuzhiyun 	struct test_name_type_v01 client_name;
173*4882a593Smuzhiyun };
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun static struct qmi_elem_info test_data_req_msg_v01_ei[] = {
176*4882a593Smuzhiyun 	{
177*4882a593Smuzhiyun 		.data_type	= QMI_DATA_LEN,
178*4882a593Smuzhiyun 		.elem_len	= 1,
179*4882a593Smuzhiyun 		.elem_size	= sizeof(u32),
180*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
181*4882a593Smuzhiyun 		.tlv_type	= DATA_REQ1_TLV_TYPE,
182*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_req_msg_v01,
183*4882a593Smuzhiyun 					   data_len),
184*4882a593Smuzhiyun 	},
185*4882a593Smuzhiyun 	{
186*4882a593Smuzhiyun 		.data_type	= QMI_UNSIGNED_1_BYTE,
187*4882a593Smuzhiyun 		.elem_len	= TEST_MED_DATA_SIZE_V01,
188*4882a593Smuzhiyun 		.elem_size	= sizeof(u8),
189*4882a593Smuzhiyun 		.array_type	= VAR_LEN_ARRAY,
190*4882a593Smuzhiyun 		.tlv_type	= DATA_REQ1_TLV_TYPE,
191*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_req_msg_v01,
192*4882a593Smuzhiyun 					   data),
193*4882a593Smuzhiyun 	},
194*4882a593Smuzhiyun 	{
195*4882a593Smuzhiyun 		.data_type	= QMI_OPT_FLAG,
196*4882a593Smuzhiyun 		.elem_len	= 1,
197*4882a593Smuzhiyun 		.elem_size	= sizeof(u8),
198*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
199*4882a593Smuzhiyun 		.tlv_type	= DATA_OPT1_TLV_TYPE,
200*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_req_msg_v01,
201*4882a593Smuzhiyun 					   client_name_valid),
202*4882a593Smuzhiyun 	},
203*4882a593Smuzhiyun 	{
204*4882a593Smuzhiyun 		.data_type	= QMI_STRUCT,
205*4882a593Smuzhiyun 		.elem_len	= 1,
206*4882a593Smuzhiyun 		.elem_size	= sizeof(struct test_name_type_v01),
207*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
208*4882a593Smuzhiyun 		.tlv_type	= DATA_OPT1_TLV_TYPE,
209*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_req_msg_v01,
210*4882a593Smuzhiyun 					   client_name),
211*4882a593Smuzhiyun 		.ei_array	= test_name_type_v01_ei,
212*4882a593Smuzhiyun 	},
213*4882a593Smuzhiyun 	{}
214*4882a593Smuzhiyun };
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun struct test_data_resp_msg_v01 {
217*4882a593Smuzhiyun 	struct qmi_response_type_v01 resp;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	u8 data_valid;
220*4882a593Smuzhiyun 	u32 data_len;
221*4882a593Smuzhiyun 	u8 data[TEST_MED_DATA_SIZE_V01];
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	u8 service_name_valid;
224*4882a593Smuzhiyun 	struct test_name_type_v01 service_name;
225*4882a593Smuzhiyun };
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun static struct qmi_elem_info test_data_resp_msg_v01_ei[] = {
228*4882a593Smuzhiyun 	{
229*4882a593Smuzhiyun 		.data_type	= QMI_STRUCT,
230*4882a593Smuzhiyun 		.elem_len	= 1,
231*4882a593Smuzhiyun 		.elem_size	= sizeof(struct qmi_response_type_v01),
232*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
233*4882a593Smuzhiyun 		.tlv_type	= DATA_RESP1_TLV_TYPE,
234*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_resp_msg_v01,
235*4882a593Smuzhiyun 					   resp),
236*4882a593Smuzhiyun 		.ei_array	= qmi_response_type_v01_ei,
237*4882a593Smuzhiyun 	},
238*4882a593Smuzhiyun 	{
239*4882a593Smuzhiyun 		.data_type	= QMI_OPT_FLAG,
240*4882a593Smuzhiyun 		.elem_len	= 1,
241*4882a593Smuzhiyun 		.elem_size	= sizeof(u8),
242*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
243*4882a593Smuzhiyun 		.tlv_type	= DATA_OPT1_TLV_TYPE,
244*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_resp_msg_v01,
245*4882a593Smuzhiyun 					   data_valid),
246*4882a593Smuzhiyun 	},
247*4882a593Smuzhiyun 	{
248*4882a593Smuzhiyun 		.data_type	= QMI_DATA_LEN,
249*4882a593Smuzhiyun 		.elem_len	= 1,
250*4882a593Smuzhiyun 		.elem_size	= sizeof(u32),
251*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
252*4882a593Smuzhiyun 		.tlv_type	= DATA_OPT1_TLV_TYPE,
253*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_resp_msg_v01,
254*4882a593Smuzhiyun 					   data_len),
255*4882a593Smuzhiyun 	},
256*4882a593Smuzhiyun 	{
257*4882a593Smuzhiyun 		.data_type	= QMI_UNSIGNED_1_BYTE,
258*4882a593Smuzhiyun 		.elem_len	= TEST_MED_DATA_SIZE_V01,
259*4882a593Smuzhiyun 		.elem_size	= sizeof(u8),
260*4882a593Smuzhiyun 		.array_type	= VAR_LEN_ARRAY,
261*4882a593Smuzhiyun 		.tlv_type	= DATA_OPT1_TLV_TYPE,
262*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_resp_msg_v01,
263*4882a593Smuzhiyun 					   data),
264*4882a593Smuzhiyun 	},
265*4882a593Smuzhiyun 	{
266*4882a593Smuzhiyun 		.data_type	= QMI_OPT_FLAG,
267*4882a593Smuzhiyun 		.elem_len	= 1,
268*4882a593Smuzhiyun 		.elem_size	= sizeof(u8),
269*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
270*4882a593Smuzhiyun 		.tlv_type	= DATA_OPT2_TLV_TYPE,
271*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_resp_msg_v01,
272*4882a593Smuzhiyun 					   service_name_valid),
273*4882a593Smuzhiyun 	},
274*4882a593Smuzhiyun 	{
275*4882a593Smuzhiyun 		.data_type	= QMI_STRUCT,
276*4882a593Smuzhiyun 		.elem_len	= 1,
277*4882a593Smuzhiyun 		.elem_size	= sizeof(struct test_name_type_v01),
278*4882a593Smuzhiyun 		.array_type	= NO_ARRAY,
279*4882a593Smuzhiyun 		.tlv_type	= DATA_OPT2_TLV_TYPE,
280*4882a593Smuzhiyun 		.offset		= offsetof(struct test_data_resp_msg_v01,
281*4882a593Smuzhiyun 					   service_name),
282*4882a593Smuzhiyun 		.ei_array	= test_name_type_v01_ei,
283*4882a593Smuzhiyun 	},
284*4882a593Smuzhiyun 	{}
285*4882a593Smuzhiyun };
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun /*
288*4882a593Smuzhiyun  * ping_write() - ping_pong debugfs file write handler
289*4882a593Smuzhiyun  * @file:	debugfs file context
290*4882a593Smuzhiyun  * @user_buf:	reference to the user data (ignored)
291*4882a593Smuzhiyun  * @count:	number of bytes in @user_buf
292*4882a593Smuzhiyun  * @ppos:	offset in @file to write
293*4882a593Smuzhiyun  *
294*4882a593Smuzhiyun  * This function allows user space to send out a ping_pong QMI encoded message
295*4882a593Smuzhiyun  * to the associated remote test service and will return with the result of the
296*4882a593Smuzhiyun  * transaction. It serves as an example of how to provide a custom response
297*4882a593Smuzhiyun  * handler.
298*4882a593Smuzhiyun  *
299*4882a593Smuzhiyun  * Return: @count, or negative errno on failure.
300*4882a593Smuzhiyun  */
ping_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)301*4882a593Smuzhiyun static ssize_t ping_write(struct file *file, const char __user *user_buf,
302*4882a593Smuzhiyun 			  size_t count, loff_t *ppos)
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun 	struct qmi_handle *qmi = file->private_data;
305*4882a593Smuzhiyun 	struct test_ping_req_msg_v01 req = {};
306*4882a593Smuzhiyun 	struct qmi_txn txn;
307*4882a593Smuzhiyun 	int ret;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	memcpy(req.ping, "ping", sizeof(req.ping));
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	ret = qmi_txn_init(qmi, &txn, NULL, NULL);
312*4882a593Smuzhiyun 	if (ret < 0)
313*4882a593Smuzhiyun 		return ret;
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	ret = qmi_send_request(qmi, NULL, &txn,
316*4882a593Smuzhiyun 			       TEST_PING_REQ_MSG_ID_V01,
317*4882a593Smuzhiyun 			       TEST_PING_REQ_MAX_MSG_LEN_V01,
318*4882a593Smuzhiyun 			       test_ping_req_msg_v01_ei, &req);
319*4882a593Smuzhiyun 	if (ret < 0) {
320*4882a593Smuzhiyun 		qmi_txn_cancel(&txn);
321*4882a593Smuzhiyun 		return ret;
322*4882a593Smuzhiyun 	}
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	ret = qmi_txn_wait(&txn, 5 * HZ);
325*4882a593Smuzhiyun 	if (ret < 0)
326*4882a593Smuzhiyun 		count = ret;
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	return count;
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun static const struct file_operations ping_fops = {
332*4882a593Smuzhiyun 	.open = simple_open,
333*4882a593Smuzhiyun 	.write = ping_write,
334*4882a593Smuzhiyun };
335*4882a593Smuzhiyun 
ping_pong_cb(struct qmi_handle * qmi,struct sockaddr_qrtr * sq,struct qmi_txn * txn,const void * data)336*4882a593Smuzhiyun static void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
337*4882a593Smuzhiyun 			 struct qmi_txn *txn, const void *data)
338*4882a593Smuzhiyun {
339*4882a593Smuzhiyun 	const struct test_ping_resp_msg_v01 *resp = data;
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	if (!txn) {
342*4882a593Smuzhiyun 		pr_err("spurious ping response\n");
343*4882a593Smuzhiyun 		return;
344*4882a593Smuzhiyun 	}
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	if (resp->resp.result == QMI_RESULT_FAILURE_V01)
347*4882a593Smuzhiyun 		txn->result = -ENXIO;
348*4882a593Smuzhiyun 	else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4))
349*4882a593Smuzhiyun 		txn->result = -EINVAL;
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	complete(&txn->completion);
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun /*
355*4882a593Smuzhiyun  * data_write() - data debugfs file write handler
356*4882a593Smuzhiyun  * @file:	debugfs file context
357*4882a593Smuzhiyun  * @user_buf:	reference to the user data
358*4882a593Smuzhiyun  * @count:	number of bytes in @user_buf
359*4882a593Smuzhiyun  * @ppos:	offset in @file to write
360*4882a593Smuzhiyun  *
361*4882a593Smuzhiyun  * This function allows user space to send out a data QMI encoded message to
362*4882a593Smuzhiyun  * the associated remote test service and will return with the result of the
363*4882a593Smuzhiyun  * transaction. It serves as an example of how to have the QMI helpers decode a
364*4882a593Smuzhiyun  * transaction response into a provided object automatically.
365*4882a593Smuzhiyun  *
366*4882a593Smuzhiyun  * Return: @count, or negative errno on failure.
367*4882a593Smuzhiyun  */
data_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)368*4882a593Smuzhiyun static ssize_t data_write(struct file *file, const char __user *user_buf,
369*4882a593Smuzhiyun 			  size_t count, loff_t *ppos)
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun 	struct qmi_handle *qmi = file->private_data;
373*4882a593Smuzhiyun 	struct test_data_resp_msg_v01 *resp;
374*4882a593Smuzhiyun 	struct test_data_req_msg_v01 *req;
375*4882a593Smuzhiyun 	struct qmi_txn txn;
376*4882a593Smuzhiyun 	int ret;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	req = kzalloc(sizeof(*req), GFP_KERNEL);
379*4882a593Smuzhiyun 	if (!req)
380*4882a593Smuzhiyun 		return -ENOMEM;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
383*4882a593Smuzhiyun 	if (!resp) {
384*4882a593Smuzhiyun 		kfree(req);
385*4882a593Smuzhiyun 		return -ENOMEM;
386*4882a593Smuzhiyun 	}
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	req->data_len = min_t(size_t, sizeof(req->data), count);
389*4882a593Smuzhiyun 	if (copy_from_user(req->data, user_buf, req->data_len)) {
390*4882a593Smuzhiyun 		ret = -EFAULT;
391*4882a593Smuzhiyun 		goto out;
392*4882a593Smuzhiyun 	}
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp);
395*4882a593Smuzhiyun 	if (ret < 0)
396*4882a593Smuzhiyun 		goto out;
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	ret = qmi_send_request(qmi, NULL, &txn,
399*4882a593Smuzhiyun 			       TEST_DATA_REQ_MSG_ID_V01,
400*4882a593Smuzhiyun 			       TEST_DATA_REQ_MAX_MSG_LEN_V01,
401*4882a593Smuzhiyun 			       test_data_req_msg_v01_ei, req);
402*4882a593Smuzhiyun 	if (ret < 0) {
403*4882a593Smuzhiyun 		qmi_txn_cancel(&txn);
404*4882a593Smuzhiyun 		goto out;
405*4882a593Smuzhiyun 	}
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	ret = qmi_txn_wait(&txn, 5 * HZ);
408*4882a593Smuzhiyun 	if (ret < 0) {
409*4882a593Smuzhiyun 		goto out;
410*4882a593Smuzhiyun 	} else if (!resp->data_valid ||
411*4882a593Smuzhiyun 		   resp->data_len != req->data_len ||
412*4882a593Smuzhiyun 		   memcmp(resp->data, req->data, req->data_len)) {
413*4882a593Smuzhiyun 		pr_err("response data doesn't match expectation\n");
414*4882a593Smuzhiyun 		ret = -EINVAL;
415*4882a593Smuzhiyun 		goto out;
416*4882a593Smuzhiyun 	}
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 	ret = count;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun out:
421*4882a593Smuzhiyun 	kfree(resp);
422*4882a593Smuzhiyun 	kfree(req);
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	return ret;
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun static const struct file_operations data_fops = {
428*4882a593Smuzhiyun 	.open = simple_open,
429*4882a593Smuzhiyun 	.write = data_write,
430*4882a593Smuzhiyun };
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun static struct qmi_msg_handler qmi_sample_handlers[] = {
433*4882a593Smuzhiyun 	{
434*4882a593Smuzhiyun 		.type = QMI_RESPONSE,
435*4882a593Smuzhiyun 		.msg_id = TEST_PING_REQ_MSG_ID_V01,
436*4882a593Smuzhiyun 		.ei = test_ping_resp_msg_v01_ei,
437*4882a593Smuzhiyun 		.decoded_size = sizeof(struct test_ping_req_msg_v01),
438*4882a593Smuzhiyun 		.fn = ping_pong_cb
439*4882a593Smuzhiyun 	},
440*4882a593Smuzhiyun 	{}
441*4882a593Smuzhiyun };
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun struct qmi_sample {
444*4882a593Smuzhiyun 	struct qmi_handle qmi;
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	struct dentry *de_dir;
447*4882a593Smuzhiyun 	struct dentry *de_data;
448*4882a593Smuzhiyun 	struct dentry *de_ping;
449*4882a593Smuzhiyun };
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun static struct dentry *qmi_debug_dir;
452*4882a593Smuzhiyun 
qmi_sample_probe(struct platform_device * pdev)453*4882a593Smuzhiyun static int qmi_sample_probe(struct platform_device *pdev)
454*4882a593Smuzhiyun {
455*4882a593Smuzhiyun 	struct sockaddr_qrtr *sq;
456*4882a593Smuzhiyun 	struct qmi_sample *sample;
457*4882a593Smuzhiyun 	char path[20];
458*4882a593Smuzhiyun 	int ret;
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL);
461*4882a593Smuzhiyun 	if (!sample)
462*4882a593Smuzhiyun 		return -ENOMEM;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01,
465*4882a593Smuzhiyun 			      NULL,
466*4882a593Smuzhiyun 			      qmi_sample_handlers);
467*4882a593Smuzhiyun 	if (ret < 0)
468*4882a593Smuzhiyun 		return ret;
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun 	sq = dev_get_platdata(&pdev->dev);
471*4882a593Smuzhiyun 	ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq,
472*4882a593Smuzhiyun 			     sizeof(*sq), 0);
473*4882a593Smuzhiyun 	if (ret < 0) {
474*4882a593Smuzhiyun 		pr_err("failed to connect to remote service port\n");
475*4882a593Smuzhiyun 		goto err_release_qmi_handle;
476*4882a593Smuzhiyun 	}
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port);
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	sample->de_dir = debugfs_create_dir(path, qmi_debug_dir);
481*4882a593Smuzhiyun 	if (IS_ERR(sample->de_dir)) {
482*4882a593Smuzhiyun 		ret = PTR_ERR(sample->de_dir);
483*4882a593Smuzhiyun 		goto err_release_qmi_handle;
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	sample->de_data = debugfs_create_file("data", 0600, sample->de_dir,
487*4882a593Smuzhiyun 					      sample, &data_fops);
488*4882a593Smuzhiyun 	if (IS_ERR(sample->de_data)) {
489*4882a593Smuzhiyun 		ret = PTR_ERR(sample->de_data);
490*4882a593Smuzhiyun 		goto err_remove_de_dir;
491*4882a593Smuzhiyun 	}
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir,
494*4882a593Smuzhiyun 					      sample, &ping_fops);
495*4882a593Smuzhiyun 	if (IS_ERR(sample->de_ping)) {
496*4882a593Smuzhiyun 		ret = PTR_ERR(sample->de_ping);
497*4882a593Smuzhiyun 		goto err_remove_de_data;
498*4882a593Smuzhiyun 	}
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 	platform_set_drvdata(pdev, sample);
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	return 0;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun err_remove_de_data:
505*4882a593Smuzhiyun 	debugfs_remove(sample->de_data);
506*4882a593Smuzhiyun err_remove_de_dir:
507*4882a593Smuzhiyun 	debugfs_remove(sample->de_dir);
508*4882a593Smuzhiyun err_release_qmi_handle:
509*4882a593Smuzhiyun 	qmi_handle_release(&sample->qmi);
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	return ret;
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun 
qmi_sample_remove(struct platform_device * pdev)514*4882a593Smuzhiyun static int qmi_sample_remove(struct platform_device *pdev)
515*4882a593Smuzhiyun {
516*4882a593Smuzhiyun 	struct qmi_sample *sample = platform_get_drvdata(pdev);
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	debugfs_remove(sample->de_ping);
519*4882a593Smuzhiyun 	debugfs_remove(sample->de_data);
520*4882a593Smuzhiyun 	debugfs_remove(sample->de_dir);
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	qmi_handle_release(&sample->qmi);
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	return 0;
525*4882a593Smuzhiyun }
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun static struct platform_driver qmi_sample_driver = {
528*4882a593Smuzhiyun 	.probe = qmi_sample_probe,
529*4882a593Smuzhiyun 	.remove = qmi_sample_remove,
530*4882a593Smuzhiyun 	.driver = {
531*4882a593Smuzhiyun 		.name = "qmi_sample_client",
532*4882a593Smuzhiyun 	},
533*4882a593Smuzhiyun };
534*4882a593Smuzhiyun 
qmi_sample_new_server(struct qmi_handle * qmi,struct qmi_service * service)535*4882a593Smuzhiyun static int qmi_sample_new_server(struct qmi_handle *qmi,
536*4882a593Smuzhiyun 				 struct qmi_service *service)
537*4882a593Smuzhiyun {
538*4882a593Smuzhiyun 	struct platform_device *pdev;
539*4882a593Smuzhiyun 	struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port };
540*4882a593Smuzhiyun 	int ret;
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	pdev = platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO);
543*4882a593Smuzhiyun 	if (!pdev)
544*4882a593Smuzhiyun 		return -ENOMEM;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	ret = platform_device_add_data(pdev, &sq, sizeof(sq));
547*4882a593Smuzhiyun 	if (ret)
548*4882a593Smuzhiyun 		goto err_put_device;
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	ret = platform_device_add(pdev);
551*4882a593Smuzhiyun 	if (ret)
552*4882a593Smuzhiyun 		goto err_put_device;
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	service->priv = pdev;
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	return 0;
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun err_put_device:
559*4882a593Smuzhiyun 	platform_device_put(pdev);
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	return ret;
562*4882a593Smuzhiyun }
563*4882a593Smuzhiyun 
qmi_sample_del_server(struct qmi_handle * qmi,struct qmi_service * service)564*4882a593Smuzhiyun static void qmi_sample_del_server(struct qmi_handle *qmi,
565*4882a593Smuzhiyun 				  struct qmi_service *service)
566*4882a593Smuzhiyun {
567*4882a593Smuzhiyun 	struct platform_device *pdev = service->priv;
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	platform_device_unregister(pdev);
570*4882a593Smuzhiyun }
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun static struct qmi_handle lookup_client;
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun static struct qmi_ops lookup_ops = {
575*4882a593Smuzhiyun 	.new_server = qmi_sample_new_server,
576*4882a593Smuzhiyun 	.del_server = qmi_sample_del_server,
577*4882a593Smuzhiyun };
578*4882a593Smuzhiyun 
qmi_sample_init(void)579*4882a593Smuzhiyun static int qmi_sample_init(void)
580*4882a593Smuzhiyun {
581*4882a593Smuzhiyun 	int ret;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	qmi_debug_dir = debugfs_create_dir("qmi_sample", NULL);
584*4882a593Smuzhiyun 	if (IS_ERR(qmi_debug_dir)) {
585*4882a593Smuzhiyun 		pr_err("failed to create qmi_sample dir\n");
586*4882a593Smuzhiyun 		return PTR_ERR(qmi_debug_dir);
587*4882a593Smuzhiyun 	}
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	ret = platform_driver_register(&qmi_sample_driver);
590*4882a593Smuzhiyun 	if (ret)
591*4882a593Smuzhiyun 		goto err_remove_debug_dir;
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	ret = qmi_handle_init(&lookup_client, 0, &lookup_ops, NULL);
594*4882a593Smuzhiyun 	if (ret < 0)
595*4882a593Smuzhiyun 		goto err_unregister_driver;
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	qmi_add_lookup(&lookup_client, 15, 0, 0);
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 	return 0;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun err_unregister_driver:
602*4882a593Smuzhiyun 	platform_driver_unregister(&qmi_sample_driver);
603*4882a593Smuzhiyun err_remove_debug_dir:
604*4882a593Smuzhiyun 	debugfs_remove(qmi_debug_dir);
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	return ret;
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun 
qmi_sample_exit(void)609*4882a593Smuzhiyun static void qmi_sample_exit(void)
610*4882a593Smuzhiyun {
611*4882a593Smuzhiyun 	qmi_handle_release(&lookup_client);
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	platform_driver_unregister(&qmi_sample_driver);
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	debugfs_remove(qmi_debug_dir);
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun module_init(qmi_sample_init);
619*4882a593Smuzhiyun module_exit(qmi_sample_exit);
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun MODULE_DESCRIPTION("Sample QMI client driver");
622*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
623