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