xref: /openbmc/linux/samples/qmi/qmi_sample_client.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1842891beSBjorn Andersson // SPDX-License-Identifier: GPL-2.0
2842891beSBjorn Andersson /*
3842891beSBjorn Andersson  * Sample in-kernel QMI client driver
4842891beSBjorn Andersson  *
5842891beSBjorn Andersson  * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
6842891beSBjorn Andersson  * Copyright (C) 2017 Linaro Ltd.
7842891beSBjorn Andersson  */
8842891beSBjorn Andersson #include <linux/kernel.h>
9842891beSBjorn Andersson #include <linux/module.h>
10842891beSBjorn Andersson #include <linux/debugfs.h>
11842891beSBjorn Andersson #include <linux/device.h>
12842891beSBjorn Andersson #include <linux/platform_device.h>
13842891beSBjorn Andersson #include <linux/qrtr.h>
14842891beSBjorn Andersson #include <linux/net.h>
15842891beSBjorn Andersson #include <linux/completion.h>
16842891beSBjorn Andersson #include <linux/idr.h>
17842891beSBjorn Andersson #include <linux/string.h>
18842891beSBjorn Andersson #include <net/sock.h>
19842891beSBjorn Andersson #include <linux/soc/qcom/qmi.h>
20842891beSBjorn Andersson 
21842891beSBjorn Andersson #define PING_REQ1_TLV_TYPE		0x1
22842891beSBjorn Andersson #define PING_RESP1_TLV_TYPE		0x2
23842891beSBjorn Andersson #define PING_OPT1_TLV_TYPE		0x10
24842891beSBjorn Andersson #define PING_OPT2_TLV_TYPE		0x11
25842891beSBjorn Andersson 
26842891beSBjorn Andersson #define DATA_REQ1_TLV_TYPE		0x1
27842891beSBjorn Andersson #define DATA_RESP1_TLV_TYPE		0x2
28842891beSBjorn Andersson #define DATA_OPT1_TLV_TYPE		0x10
29842891beSBjorn Andersson #define DATA_OPT2_TLV_TYPE		0x11
30842891beSBjorn Andersson 
31842891beSBjorn Andersson #define TEST_MED_DATA_SIZE_V01		8192
32842891beSBjorn Andersson #define TEST_MAX_NAME_SIZE_V01		255
33842891beSBjorn Andersson 
34842891beSBjorn Andersson #define TEST_PING_REQ_MSG_ID_V01	0x20
35842891beSBjorn Andersson #define TEST_DATA_REQ_MSG_ID_V01	0x21
36842891beSBjorn Andersson 
37842891beSBjorn Andersson #define TEST_PING_REQ_MAX_MSG_LEN_V01	266
38842891beSBjorn Andersson #define TEST_DATA_REQ_MAX_MSG_LEN_V01	8456
39842891beSBjorn Andersson 
40842891beSBjorn Andersson struct test_name_type_v01 {
41842891beSBjorn Andersson 	u32 name_len;
42842891beSBjorn Andersson 	char name[TEST_MAX_NAME_SIZE_V01];
43842891beSBjorn Andersson };
44842891beSBjorn Andersson 
45*ff6d3658SJeff Johnson static const struct qmi_elem_info test_name_type_v01_ei[] = {
46842891beSBjorn Andersson 	{
47842891beSBjorn Andersson 		.data_type	= QMI_DATA_LEN,
48842891beSBjorn Andersson 		.elem_len	= 1,
49842891beSBjorn Andersson 		.elem_size	= sizeof(u8),
50842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
51842891beSBjorn Andersson 		.tlv_type	= QMI_COMMON_TLV_TYPE,
52842891beSBjorn Andersson 		.offset		= offsetof(struct test_name_type_v01,
53842891beSBjorn Andersson 					   name_len),
54842891beSBjorn Andersson 	},
55842891beSBjorn Andersson 	{
56842891beSBjorn Andersson 		.data_type	= QMI_UNSIGNED_1_BYTE,
57842891beSBjorn Andersson 		.elem_len	= TEST_MAX_NAME_SIZE_V01,
58842891beSBjorn Andersson 		.elem_size	= sizeof(char),
59842891beSBjorn Andersson 		.array_type	= VAR_LEN_ARRAY,
60842891beSBjorn Andersson 		.tlv_type	= QMI_COMMON_TLV_TYPE,
61842891beSBjorn Andersson 		.offset		= offsetof(struct test_name_type_v01,
62842891beSBjorn Andersson 					   name),
63842891beSBjorn Andersson 	},
64842891beSBjorn Andersson 	{}
65842891beSBjorn Andersson };
66842891beSBjorn Andersson 
67842891beSBjorn Andersson struct test_ping_req_msg_v01 {
68842891beSBjorn Andersson 	char ping[4];
69842891beSBjorn Andersson 
70842891beSBjorn Andersson 	u8 client_name_valid;
71842891beSBjorn Andersson 	struct test_name_type_v01 client_name;
72842891beSBjorn Andersson };
73842891beSBjorn Andersson 
74*ff6d3658SJeff Johnson static const struct qmi_elem_info test_ping_req_msg_v01_ei[] = {
75842891beSBjorn Andersson 	{
76842891beSBjorn Andersson 		.data_type	= QMI_UNSIGNED_1_BYTE,
77842891beSBjorn Andersson 		.elem_len	= 4,
78842891beSBjorn Andersson 		.elem_size	= sizeof(char),
79842891beSBjorn Andersson 		.array_type	= STATIC_ARRAY,
80842891beSBjorn Andersson 		.tlv_type	= PING_REQ1_TLV_TYPE,
81842891beSBjorn Andersson 		.offset		= offsetof(struct test_ping_req_msg_v01,
82842891beSBjorn Andersson 					   ping),
83842891beSBjorn Andersson 	},
84842891beSBjorn Andersson 	{
85842891beSBjorn Andersson 		.data_type	= QMI_OPT_FLAG,
86842891beSBjorn Andersson 		.elem_len	= 1,
87842891beSBjorn Andersson 		.elem_size	= sizeof(u8),
88842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
89842891beSBjorn Andersson 		.tlv_type	= PING_OPT1_TLV_TYPE,
90842891beSBjorn Andersson 		.offset		= offsetof(struct test_ping_req_msg_v01,
91842891beSBjorn Andersson 					   client_name_valid),
92842891beSBjorn Andersson 	},
93842891beSBjorn Andersson 	{
94842891beSBjorn Andersson 		.data_type	= QMI_STRUCT,
95842891beSBjorn Andersson 		.elem_len	= 1,
96842891beSBjorn Andersson 		.elem_size	= sizeof(struct test_name_type_v01),
97842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
98842891beSBjorn Andersson 		.tlv_type	= PING_OPT1_TLV_TYPE,
99842891beSBjorn Andersson 		.offset		= offsetof(struct test_ping_req_msg_v01,
100842891beSBjorn Andersson 					   client_name),
101842891beSBjorn Andersson 		.ei_array	= test_name_type_v01_ei,
102842891beSBjorn Andersson 	},
103842891beSBjorn Andersson 	{}
104842891beSBjorn Andersson };
105842891beSBjorn Andersson 
106842891beSBjorn Andersson struct test_ping_resp_msg_v01 {
107842891beSBjorn Andersson 	struct qmi_response_type_v01 resp;
108842891beSBjorn Andersson 
109842891beSBjorn Andersson 	u8 pong_valid;
110842891beSBjorn Andersson 	char pong[4];
111842891beSBjorn Andersson 
112842891beSBjorn Andersson 	u8 service_name_valid;
113842891beSBjorn Andersson 	struct test_name_type_v01 service_name;
114842891beSBjorn Andersson };
115842891beSBjorn Andersson 
116*ff6d3658SJeff Johnson static const struct qmi_elem_info test_ping_resp_msg_v01_ei[] = {
117842891beSBjorn Andersson 	{
118842891beSBjorn Andersson 		.data_type	= QMI_STRUCT,
119842891beSBjorn Andersson 		.elem_len	= 1,
120842891beSBjorn Andersson 		.elem_size	= sizeof(struct qmi_response_type_v01),
121842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
122842891beSBjorn Andersson 		.tlv_type	= PING_RESP1_TLV_TYPE,
123842891beSBjorn Andersson 		.offset		= offsetof(struct test_ping_resp_msg_v01,
124842891beSBjorn Andersson 					   resp),
125842891beSBjorn Andersson 		.ei_array	= qmi_response_type_v01_ei,
126842891beSBjorn Andersson 	},
127842891beSBjorn Andersson 	{
128842891beSBjorn Andersson 		.data_type	= QMI_OPT_FLAG,
129842891beSBjorn Andersson 		.elem_len	= 1,
130842891beSBjorn Andersson 		.elem_size	= sizeof(u8),
131842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
132842891beSBjorn Andersson 		.tlv_type	= PING_OPT1_TLV_TYPE,
133842891beSBjorn Andersson 		.offset		= offsetof(struct test_ping_resp_msg_v01,
134842891beSBjorn Andersson 					   pong_valid),
135842891beSBjorn Andersson 	},
136842891beSBjorn Andersson 	{
137842891beSBjorn Andersson 		.data_type	= QMI_UNSIGNED_1_BYTE,
138842891beSBjorn Andersson 		.elem_len	= 4,
139842891beSBjorn Andersson 		.elem_size	= sizeof(char),
140842891beSBjorn Andersson 		.array_type	= STATIC_ARRAY,
141842891beSBjorn Andersson 		.tlv_type	= PING_OPT1_TLV_TYPE,
142842891beSBjorn Andersson 		.offset		= offsetof(struct test_ping_resp_msg_v01,
143842891beSBjorn Andersson 					   pong),
144842891beSBjorn Andersson 	},
145842891beSBjorn Andersson 	{
146842891beSBjorn Andersson 		.data_type	= QMI_OPT_FLAG,
147842891beSBjorn Andersson 		.elem_len	= 1,
148842891beSBjorn Andersson 		.elem_size	= sizeof(u8),
149842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
150842891beSBjorn Andersson 		.tlv_type	= PING_OPT2_TLV_TYPE,
151842891beSBjorn Andersson 		.offset		= offsetof(struct test_ping_resp_msg_v01,
152842891beSBjorn Andersson 					   service_name_valid),
153842891beSBjorn Andersson 	},
154842891beSBjorn Andersson 	{
155842891beSBjorn Andersson 		.data_type	= QMI_STRUCT,
156842891beSBjorn Andersson 		.elem_len	= 1,
157842891beSBjorn Andersson 		.elem_size	= sizeof(struct test_name_type_v01),
158842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
159842891beSBjorn Andersson 		.tlv_type	= PING_OPT2_TLV_TYPE,
160842891beSBjorn Andersson 		.offset		= offsetof(struct test_ping_resp_msg_v01,
161842891beSBjorn Andersson 					   service_name),
162842891beSBjorn Andersson 		.ei_array	= test_name_type_v01_ei,
163842891beSBjorn Andersson 	},
164842891beSBjorn Andersson 	{}
165842891beSBjorn Andersson };
166842891beSBjorn Andersson 
167842891beSBjorn Andersson struct test_data_req_msg_v01 {
168842891beSBjorn Andersson 	u32 data_len;
169842891beSBjorn Andersson 	u8 data[TEST_MED_DATA_SIZE_V01];
170842891beSBjorn Andersson 
171842891beSBjorn Andersson 	u8 client_name_valid;
172842891beSBjorn Andersson 	struct test_name_type_v01 client_name;
173842891beSBjorn Andersson };
174842891beSBjorn Andersson 
175*ff6d3658SJeff Johnson static const struct qmi_elem_info test_data_req_msg_v01_ei[] = {
176842891beSBjorn Andersson 	{
177842891beSBjorn Andersson 		.data_type	= QMI_DATA_LEN,
178842891beSBjorn Andersson 		.elem_len	= 1,
179842891beSBjorn Andersson 		.elem_size	= sizeof(u32),
180842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
181842891beSBjorn Andersson 		.tlv_type	= DATA_REQ1_TLV_TYPE,
182842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_req_msg_v01,
183842891beSBjorn Andersson 					   data_len),
184842891beSBjorn Andersson 	},
185842891beSBjorn Andersson 	{
186842891beSBjorn Andersson 		.data_type	= QMI_UNSIGNED_1_BYTE,
187842891beSBjorn Andersson 		.elem_len	= TEST_MED_DATA_SIZE_V01,
188842891beSBjorn Andersson 		.elem_size	= sizeof(u8),
189842891beSBjorn Andersson 		.array_type	= VAR_LEN_ARRAY,
190842891beSBjorn Andersson 		.tlv_type	= DATA_REQ1_TLV_TYPE,
191842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_req_msg_v01,
192842891beSBjorn Andersson 					   data),
193842891beSBjorn Andersson 	},
194842891beSBjorn Andersson 	{
195842891beSBjorn Andersson 		.data_type	= QMI_OPT_FLAG,
196842891beSBjorn Andersson 		.elem_len	= 1,
197842891beSBjorn Andersson 		.elem_size	= sizeof(u8),
198842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
199842891beSBjorn Andersson 		.tlv_type	= DATA_OPT1_TLV_TYPE,
200842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_req_msg_v01,
201842891beSBjorn Andersson 					   client_name_valid),
202842891beSBjorn Andersson 	},
203842891beSBjorn Andersson 	{
204842891beSBjorn Andersson 		.data_type	= QMI_STRUCT,
205842891beSBjorn Andersson 		.elem_len	= 1,
206842891beSBjorn Andersson 		.elem_size	= sizeof(struct test_name_type_v01),
207842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
208842891beSBjorn Andersson 		.tlv_type	= DATA_OPT1_TLV_TYPE,
209842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_req_msg_v01,
210842891beSBjorn Andersson 					   client_name),
211842891beSBjorn Andersson 		.ei_array	= test_name_type_v01_ei,
212842891beSBjorn Andersson 	},
213842891beSBjorn Andersson 	{}
214842891beSBjorn Andersson };
215842891beSBjorn Andersson 
216842891beSBjorn Andersson struct test_data_resp_msg_v01 {
217842891beSBjorn Andersson 	struct qmi_response_type_v01 resp;
218842891beSBjorn Andersson 
219842891beSBjorn Andersson 	u8 data_valid;
220842891beSBjorn Andersson 	u32 data_len;
221842891beSBjorn Andersson 	u8 data[TEST_MED_DATA_SIZE_V01];
222842891beSBjorn Andersson 
223842891beSBjorn Andersson 	u8 service_name_valid;
224842891beSBjorn Andersson 	struct test_name_type_v01 service_name;
225842891beSBjorn Andersson };
226842891beSBjorn Andersson 
227*ff6d3658SJeff Johnson static const struct qmi_elem_info test_data_resp_msg_v01_ei[] = {
228842891beSBjorn Andersson 	{
229842891beSBjorn Andersson 		.data_type	= QMI_STRUCT,
230842891beSBjorn Andersson 		.elem_len	= 1,
231842891beSBjorn Andersson 		.elem_size	= sizeof(struct qmi_response_type_v01),
232842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
233842891beSBjorn Andersson 		.tlv_type	= DATA_RESP1_TLV_TYPE,
234842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_resp_msg_v01,
235842891beSBjorn Andersson 					   resp),
236842891beSBjorn Andersson 		.ei_array	= qmi_response_type_v01_ei,
237842891beSBjorn Andersson 	},
238842891beSBjorn Andersson 	{
239842891beSBjorn Andersson 		.data_type	= QMI_OPT_FLAG,
240842891beSBjorn Andersson 		.elem_len	= 1,
241842891beSBjorn Andersson 		.elem_size	= sizeof(u8),
242842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
243842891beSBjorn Andersson 		.tlv_type	= DATA_OPT1_TLV_TYPE,
244842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_resp_msg_v01,
245842891beSBjorn Andersson 					   data_valid),
246842891beSBjorn Andersson 	},
247842891beSBjorn Andersson 	{
248842891beSBjorn Andersson 		.data_type	= QMI_DATA_LEN,
249842891beSBjorn Andersson 		.elem_len	= 1,
250842891beSBjorn Andersson 		.elem_size	= sizeof(u32),
251842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
252842891beSBjorn Andersson 		.tlv_type	= DATA_OPT1_TLV_TYPE,
253842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_resp_msg_v01,
254842891beSBjorn Andersson 					   data_len),
255842891beSBjorn Andersson 	},
256842891beSBjorn Andersson 	{
257842891beSBjorn Andersson 		.data_type	= QMI_UNSIGNED_1_BYTE,
258842891beSBjorn Andersson 		.elem_len	= TEST_MED_DATA_SIZE_V01,
259842891beSBjorn Andersson 		.elem_size	= sizeof(u8),
260842891beSBjorn Andersson 		.array_type	= VAR_LEN_ARRAY,
261842891beSBjorn Andersson 		.tlv_type	= DATA_OPT1_TLV_TYPE,
262842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_resp_msg_v01,
263842891beSBjorn Andersson 					   data),
264842891beSBjorn Andersson 	},
265842891beSBjorn Andersson 	{
266842891beSBjorn Andersson 		.data_type	= QMI_OPT_FLAG,
267842891beSBjorn Andersson 		.elem_len	= 1,
268842891beSBjorn Andersson 		.elem_size	= sizeof(u8),
269842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
270842891beSBjorn Andersson 		.tlv_type	= DATA_OPT2_TLV_TYPE,
271842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_resp_msg_v01,
272842891beSBjorn Andersson 					   service_name_valid),
273842891beSBjorn Andersson 	},
274842891beSBjorn Andersson 	{
275842891beSBjorn Andersson 		.data_type	= QMI_STRUCT,
276842891beSBjorn Andersson 		.elem_len	= 1,
277842891beSBjorn Andersson 		.elem_size	= sizeof(struct test_name_type_v01),
278842891beSBjorn Andersson 		.array_type	= NO_ARRAY,
279842891beSBjorn Andersson 		.tlv_type	= DATA_OPT2_TLV_TYPE,
280842891beSBjorn Andersson 		.offset		= offsetof(struct test_data_resp_msg_v01,
281842891beSBjorn Andersson 					   service_name),
282842891beSBjorn Andersson 		.ei_array	= test_name_type_v01_ei,
283842891beSBjorn Andersson 	},
284842891beSBjorn Andersson 	{}
285842891beSBjorn Andersson };
286842891beSBjorn Andersson 
287842891beSBjorn Andersson /*
288842891beSBjorn Andersson  * ping_write() - ping_pong debugfs file write handler
289842891beSBjorn Andersson  * @file:	debugfs file context
290842891beSBjorn Andersson  * @user_buf:	reference to the user data (ignored)
291842891beSBjorn Andersson  * @count:	number of bytes in @user_buf
292842891beSBjorn Andersson  * @ppos:	offset in @file to write
293842891beSBjorn Andersson  *
294842891beSBjorn Andersson  * This function allows user space to send out a ping_pong QMI encoded message
295842891beSBjorn Andersson  * to the associated remote test service and will return with the result of the
296842891beSBjorn Andersson  * transaction. It serves as an example of how to provide a custom response
297842891beSBjorn Andersson  * handler.
298842891beSBjorn Andersson  *
299842891beSBjorn Andersson  * Return: @count, or negative errno on failure.
300842891beSBjorn Andersson  */
ping_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)301842891beSBjorn Andersson static ssize_t ping_write(struct file *file, const char __user *user_buf,
302842891beSBjorn Andersson 			  size_t count, loff_t *ppos)
303842891beSBjorn Andersson {
304842891beSBjorn Andersson 	struct qmi_handle *qmi = file->private_data;
305842891beSBjorn Andersson 	struct test_ping_req_msg_v01 req = {};
306842891beSBjorn Andersson 	struct qmi_txn txn;
307842891beSBjorn Andersson 	int ret;
308842891beSBjorn Andersson 
309842891beSBjorn Andersson 	memcpy(req.ping, "ping", sizeof(req.ping));
310842891beSBjorn Andersson 
311842891beSBjorn Andersson 	ret = qmi_txn_init(qmi, &txn, NULL, NULL);
312842891beSBjorn Andersson 	if (ret < 0)
313842891beSBjorn Andersson 		return ret;
314842891beSBjorn Andersson 
315842891beSBjorn Andersson 	ret = qmi_send_request(qmi, NULL, &txn,
316842891beSBjorn Andersson 			       TEST_PING_REQ_MSG_ID_V01,
317842891beSBjorn Andersson 			       TEST_PING_REQ_MAX_MSG_LEN_V01,
318842891beSBjorn Andersson 			       test_ping_req_msg_v01_ei, &req);
319842891beSBjorn Andersson 	if (ret < 0) {
320842891beSBjorn Andersson 		qmi_txn_cancel(&txn);
321842891beSBjorn Andersson 		return ret;
322842891beSBjorn Andersson 	}
323842891beSBjorn Andersson 
324842891beSBjorn Andersson 	ret = qmi_txn_wait(&txn, 5 * HZ);
325842891beSBjorn Andersson 	if (ret < 0)
326842891beSBjorn Andersson 		count = ret;
327842891beSBjorn Andersson 
328842891beSBjorn Andersson 	return count;
329842891beSBjorn Andersson }
330842891beSBjorn Andersson 
331842891beSBjorn Andersson static const struct file_operations ping_fops = {
332842891beSBjorn Andersson 	.open = simple_open,
333842891beSBjorn Andersson 	.write = ping_write,
334842891beSBjorn Andersson };
335842891beSBjorn Andersson 
ping_pong_cb(struct qmi_handle * qmi,struct sockaddr_qrtr * sq,struct qmi_txn * txn,const void * data)336842891beSBjorn Andersson static void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
337842891beSBjorn Andersson 			 struct qmi_txn *txn, const void *data)
338842891beSBjorn Andersson {
339842891beSBjorn Andersson 	const struct test_ping_resp_msg_v01 *resp = data;
340842891beSBjorn Andersson 
341842891beSBjorn Andersson 	if (!txn) {
342842891beSBjorn Andersson 		pr_err("spurious ping response\n");
343842891beSBjorn Andersson 		return;
344842891beSBjorn Andersson 	}
345842891beSBjorn Andersson 
346842891beSBjorn Andersson 	if (resp->resp.result == QMI_RESULT_FAILURE_V01)
347842891beSBjorn Andersson 		txn->result = -ENXIO;
348842891beSBjorn Andersson 	else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4))
349842891beSBjorn Andersson 		txn->result = -EINVAL;
350842891beSBjorn Andersson 
351842891beSBjorn Andersson 	complete(&txn->completion);
352842891beSBjorn Andersson }
353842891beSBjorn Andersson 
354842891beSBjorn Andersson /*
355842891beSBjorn Andersson  * data_write() - data debugfs file write handler
356842891beSBjorn Andersson  * @file:	debugfs file context
357842891beSBjorn Andersson  * @user_buf:	reference to the user data
358842891beSBjorn Andersson  * @count:	number of bytes in @user_buf
359842891beSBjorn Andersson  * @ppos:	offset in @file to write
360842891beSBjorn Andersson  *
361842891beSBjorn Andersson  * This function allows user space to send out a data QMI encoded message to
362842891beSBjorn Andersson  * the associated remote test service and will return with the result of the
363842891beSBjorn Andersson  * transaction. It serves as an example of how to have the QMI helpers decode a
364842891beSBjorn Andersson  * transaction response into a provided object automatically.
365842891beSBjorn Andersson  *
366842891beSBjorn Andersson  * Return: @count, or negative errno on failure.
367842891beSBjorn Andersson  */
data_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)368842891beSBjorn Andersson static ssize_t data_write(struct file *file, const char __user *user_buf,
369842891beSBjorn Andersson 			  size_t count, loff_t *ppos)
370842891beSBjorn Andersson 
371842891beSBjorn Andersson {
372842891beSBjorn Andersson 	struct qmi_handle *qmi = file->private_data;
373842891beSBjorn Andersson 	struct test_data_resp_msg_v01 *resp;
374842891beSBjorn Andersson 	struct test_data_req_msg_v01 *req;
375842891beSBjorn Andersson 	struct qmi_txn txn;
376842891beSBjorn Andersson 	int ret;
377842891beSBjorn Andersson 
378842891beSBjorn Andersson 	req = kzalloc(sizeof(*req), GFP_KERNEL);
379842891beSBjorn Andersson 	if (!req)
380842891beSBjorn Andersson 		return -ENOMEM;
381842891beSBjorn Andersson 
382842891beSBjorn Andersson 	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
383842891beSBjorn Andersson 	if (!resp) {
384842891beSBjorn Andersson 		kfree(req);
385842891beSBjorn Andersson 		return -ENOMEM;
386842891beSBjorn Andersson 	}
387842891beSBjorn Andersson 
388842891beSBjorn Andersson 	req->data_len = min_t(size_t, sizeof(req->data), count);
389842891beSBjorn Andersson 	if (copy_from_user(req->data, user_buf, req->data_len)) {
390842891beSBjorn Andersson 		ret = -EFAULT;
391842891beSBjorn Andersson 		goto out;
392842891beSBjorn Andersson 	}
393842891beSBjorn Andersson 
394842891beSBjorn Andersson 	ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp);
395842891beSBjorn Andersson 	if (ret < 0)
396842891beSBjorn Andersson 		goto out;
397842891beSBjorn Andersson 
398842891beSBjorn Andersson 	ret = qmi_send_request(qmi, NULL, &txn,
399842891beSBjorn Andersson 			       TEST_DATA_REQ_MSG_ID_V01,
400842891beSBjorn Andersson 			       TEST_DATA_REQ_MAX_MSG_LEN_V01,
401842891beSBjorn Andersson 			       test_data_req_msg_v01_ei, req);
402842891beSBjorn Andersson 	if (ret < 0) {
403842891beSBjorn Andersson 		qmi_txn_cancel(&txn);
404842891beSBjorn Andersson 		goto out;
405842891beSBjorn Andersson 	}
406842891beSBjorn Andersson 
407842891beSBjorn Andersson 	ret = qmi_txn_wait(&txn, 5 * HZ);
408842891beSBjorn Andersson 	if (ret < 0) {
409842891beSBjorn Andersson 		goto out;
410842891beSBjorn Andersson 	} else if (!resp->data_valid ||
411842891beSBjorn Andersson 		   resp->data_len != req->data_len ||
412842891beSBjorn Andersson 		   memcmp(resp->data, req->data, req->data_len)) {
413842891beSBjorn Andersson 		pr_err("response data doesn't match expectation\n");
414842891beSBjorn Andersson 		ret = -EINVAL;
415842891beSBjorn Andersson 		goto out;
416842891beSBjorn Andersson 	}
417842891beSBjorn Andersson 
418842891beSBjorn Andersson 	ret = count;
419842891beSBjorn Andersson 
420842891beSBjorn Andersson out:
421842891beSBjorn Andersson 	kfree(resp);
422842891beSBjorn Andersson 	kfree(req);
423842891beSBjorn Andersson 
424842891beSBjorn Andersson 	return ret;
425842891beSBjorn Andersson }
426842891beSBjorn Andersson 
427842891beSBjorn Andersson static const struct file_operations data_fops = {
428842891beSBjorn Andersson 	.open = simple_open,
429842891beSBjorn Andersson 	.write = data_write,
430842891beSBjorn Andersson };
431842891beSBjorn Andersson 
432bfc1b659SRikard Falkeborn static const struct qmi_msg_handler qmi_sample_handlers[] = {
433842891beSBjorn Andersson 	{
434842891beSBjorn Andersson 		.type = QMI_RESPONSE,
435842891beSBjorn Andersson 		.msg_id = TEST_PING_REQ_MSG_ID_V01,
436842891beSBjorn Andersson 		.ei = test_ping_resp_msg_v01_ei,
437842891beSBjorn Andersson 		.decoded_size = sizeof(struct test_ping_req_msg_v01),
438842891beSBjorn Andersson 		.fn = ping_pong_cb
439842891beSBjorn Andersson 	},
440842891beSBjorn Andersson 	{}
441842891beSBjorn Andersson };
442842891beSBjorn Andersson 
443842891beSBjorn Andersson struct qmi_sample {
444842891beSBjorn Andersson 	struct qmi_handle qmi;
445842891beSBjorn Andersson 
446842891beSBjorn Andersson 	struct dentry *de_dir;
447842891beSBjorn Andersson 	struct dentry *de_data;
448842891beSBjorn Andersson 	struct dentry *de_ping;
449842891beSBjorn Andersson };
450842891beSBjorn Andersson 
451842891beSBjorn Andersson static struct dentry *qmi_debug_dir;
452842891beSBjorn Andersson 
qmi_sample_probe(struct platform_device * pdev)453842891beSBjorn Andersson static int qmi_sample_probe(struct platform_device *pdev)
454842891beSBjorn Andersson {
455842891beSBjorn Andersson 	struct sockaddr_qrtr *sq;
456842891beSBjorn Andersson 	struct qmi_sample *sample;
457842891beSBjorn Andersson 	char path[20];
458842891beSBjorn Andersson 	int ret;
459842891beSBjorn Andersson 
460842891beSBjorn Andersson 	sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL);
461842891beSBjorn Andersson 	if (!sample)
462842891beSBjorn Andersson 		return -ENOMEM;
463842891beSBjorn Andersson 
464842891beSBjorn Andersson 	ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01,
465842891beSBjorn Andersson 			      NULL,
466842891beSBjorn Andersson 			      qmi_sample_handlers);
467842891beSBjorn Andersson 	if (ret < 0)
468842891beSBjorn Andersson 		return ret;
469842891beSBjorn Andersson 
470842891beSBjorn Andersson 	sq = dev_get_platdata(&pdev->dev);
471842891beSBjorn Andersson 	ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq,
472842891beSBjorn Andersson 			     sizeof(*sq), 0);
473842891beSBjorn Andersson 	if (ret < 0) {
474842891beSBjorn Andersson 		pr_err("failed to connect to remote service port\n");
475842891beSBjorn Andersson 		goto err_release_qmi_handle;
476842891beSBjorn Andersson 	}
477842891beSBjorn Andersson 
478842891beSBjorn Andersson 	snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port);
479842891beSBjorn Andersson 
480842891beSBjorn Andersson 	sample->de_dir = debugfs_create_dir(path, qmi_debug_dir);
481842891beSBjorn Andersson 	if (IS_ERR(sample->de_dir)) {
482842891beSBjorn Andersson 		ret = PTR_ERR(sample->de_dir);
483842891beSBjorn Andersson 		goto err_release_qmi_handle;
484842891beSBjorn Andersson 	}
485842891beSBjorn Andersson 
486842891beSBjorn Andersson 	sample->de_data = debugfs_create_file("data", 0600, sample->de_dir,
487842891beSBjorn Andersson 					      sample, &data_fops);
488842891beSBjorn Andersson 	if (IS_ERR(sample->de_data)) {
489842891beSBjorn Andersson 		ret = PTR_ERR(sample->de_data);
490842891beSBjorn Andersson 		goto err_remove_de_dir;
491842891beSBjorn Andersson 	}
492842891beSBjorn Andersson 
493842891beSBjorn Andersson 	sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir,
494842891beSBjorn Andersson 					      sample, &ping_fops);
495842891beSBjorn Andersson 	if (IS_ERR(sample->de_ping)) {
496842891beSBjorn Andersson 		ret = PTR_ERR(sample->de_ping);
497842891beSBjorn Andersson 		goto err_remove_de_data;
498842891beSBjorn Andersson 	}
499842891beSBjorn Andersson 
500842891beSBjorn Andersson 	platform_set_drvdata(pdev, sample);
501842891beSBjorn Andersson 
502842891beSBjorn Andersson 	return 0;
503842891beSBjorn Andersson 
504842891beSBjorn Andersson err_remove_de_data:
505842891beSBjorn Andersson 	debugfs_remove(sample->de_data);
506842891beSBjorn Andersson err_remove_de_dir:
507842891beSBjorn Andersson 	debugfs_remove(sample->de_dir);
508842891beSBjorn Andersson err_release_qmi_handle:
509842891beSBjorn Andersson 	qmi_handle_release(&sample->qmi);
510842891beSBjorn Andersson 
511842891beSBjorn Andersson 	return ret;
512842891beSBjorn Andersson }
513842891beSBjorn Andersson 
qmi_sample_remove(struct platform_device * pdev)514842891beSBjorn Andersson static int qmi_sample_remove(struct platform_device *pdev)
515842891beSBjorn Andersson {
516842891beSBjorn Andersson 	struct qmi_sample *sample = platform_get_drvdata(pdev);
517842891beSBjorn Andersson 
518842891beSBjorn Andersson 	debugfs_remove(sample->de_ping);
519842891beSBjorn Andersson 	debugfs_remove(sample->de_data);
520842891beSBjorn Andersson 	debugfs_remove(sample->de_dir);
521842891beSBjorn Andersson 
522842891beSBjorn Andersson 	qmi_handle_release(&sample->qmi);
523842891beSBjorn Andersson 
524842891beSBjorn Andersson 	return 0;
525842891beSBjorn Andersson }
526842891beSBjorn Andersson 
527842891beSBjorn Andersson static struct platform_driver qmi_sample_driver = {
528842891beSBjorn Andersson 	.probe = qmi_sample_probe,
529842891beSBjorn Andersson 	.remove = qmi_sample_remove,
530842891beSBjorn Andersson 	.driver = {
531842891beSBjorn Andersson 		.name = "qmi_sample_client",
532842891beSBjorn Andersson 	},
533842891beSBjorn Andersson };
534842891beSBjorn Andersson 
qmi_sample_new_server(struct qmi_handle * qmi,struct qmi_service * service)535842891beSBjorn Andersson static int qmi_sample_new_server(struct qmi_handle *qmi,
536842891beSBjorn Andersson 				 struct qmi_service *service)
537842891beSBjorn Andersson {
538842891beSBjorn Andersson 	struct platform_device *pdev;
539842891beSBjorn Andersson 	struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port };
540842891beSBjorn Andersson 	int ret;
541842891beSBjorn Andersson 
542842891beSBjorn Andersson 	pdev = platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO);
543842891beSBjorn Andersson 	if (!pdev)
544842891beSBjorn Andersson 		return -ENOMEM;
545842891beSBjorn Andersson 
546842891beSBjorn Andersson 	ret = platform_device_add_data(pdev, &sq, sizeof(sq));
547842891beSBjorn Andersson 	if (ret)
548842891beSBjorn Andersson 		goto err_put_device;
549842891beSBjorn Andersson 
550842891beSBjorn Andersson 	ret = platform_device_add(pdev);
551842891beSBjorn Andersson 	if (ret)
552842891beSBjorn Andersson 		goto err_put_device;
553842891beSBjorn Andersson 
554842891beSBjorn Andersson 	service->priv = pdev;
555842891beSBjorn Andersson 
556842891beSBjorn Andersson 	return 0;
557842891beSBjorn Andersson 
558842891beSBjorn Andersson err_put_device:
559842891beSBjorn Andersson 	platform_device_put(pdev);
560842891beSBjorn Andersson 
561842891beSBjorn Andersson 	return ret;
562842891beSBjorn Andersson }
563842891beSBjorn Andersson 
qmi_sample_del_server(struct qmi_handle * qmi,struct qmi_service * service)564842891beSBjorn Andersson static void qmi_sample_del_server(struct qmi_handle *qmi,
565842891beSBjorn Andersson 				  struct qmi_service *service)
566842891beSBjorn Andersson {
567842891beSBjorn Andersson 	struct platform_device *pdev = service->priv;
568842891beSBjorn Andersson 
569842891beSBjorn Andersson 	platform_device_unregister(pdev);
570842891beSBjorn Andersson }
571842891beSBjorn Andersson 
572842891beSBjorn Andersson static struct qmi_handle lookup_client;
573842891beSBjorn Andersson 
574bfc1b659SRikard Falkeborn static const struct qmi_ops lookup_ops = {
575842891beSBjorn Andersson 	.new_server = qmi_sample_new_server,
576842891beSBjorn Andersson 	.del_server = qmi_sample_del_server,
577842891beSBjorn Andersson };
578842891beSBjorn Andersson 
qmi_sample_init(void)579842891beSBjorn Andersson static int qmi_sample_init(void)
580842891beSBjorn Andersson {
581842891beSBjorn Andersson 	int ret;
582842891beSBjorn Andersson 
583842891beSBjorn Andersson 	qmi_debug_dir = debugfs_create_dir("qmi_sample", NULL);
584842891beSBjorn Andersson 	if (IS_ERR(qmi_debug_dir)) {
585842891beSBjorn Andersson 		pr_err("failed to create qmi_sample dir\n");
586842891beSBjorn Andersson 		return PTR_ERR(qmi_debug_dir);
587842891beSBjorn Andersson 	}
588842891beSBjorn Andersson 
589842891beSBjorn Andersson 	ret = platform_driver_register(&qmi_sample_driver);
590842891beSBjorn Andersson 	if (ret)
591842891beSBjorn Andersson 		goto err_remove_debug_dir;
592842891beSBjorn Andersson 
593842891beSBjorn Andersson 	ret = qmi_handle_init(&lookup_client, 0, &lookup_ops, NULL);
594842891beSBjorn Andersson 	if (ret < 0)
595842891beSBjorn Andersson 		goto err_unregister_driver;
596842891beSBjorn Andersson 
597842891beSBjorn Andersson 	qmi_add_lookup(&lookup_client, 15, 0, 0);
598842891beSBjorn Andersson 
599842891beSBjorn Andersson 	return 0;
600842891beSBjorn Andersson 
601842891beSBjorn Andersson err_unregister_driver:
602842891beSBjorn Andersson 	platform_driver_unregister(&qmi_sample_driver);
603842891beSBjorn Andersson err_remove_debug_dir:
604842891beSBjorn Andersson 	debugfs_remove(qmi_debug_dir);
605842891beSBjorn Andersson 
606842891beSBjorn Andersson 	return ret;
607842891beSBjorn Andersson }
608842891beSBjorn Andersson 
qmi_sample_exit(void)609842891beSBjorn Andersson static void qmi_sample_exit(void)
610842891beSBjorn Andersson {
611842891beSBjorn Andersson 	qmi_handle_release(&lookup_client);
612842891beSBjorn Andersson 
613842891beSBjorn Andersson 	platform_driver_unregister(&qmi_sample_driver);
614842891beSBjorn Andersson 
615842891beSBjorn Andersson 	debugfs_remove(qmi_debug_dir);
616842891beSBjorn Andersson }
617842891beSBjorn Andersson 
618842891beSBjorn Andersson module_init(qmi_sample_init);
619842891beSBjorn Andersson module_exit(qmi_sample_exit);
620842891beSBjorn Andersson 
621842891beSBjorn Andersson MODULE_DESCRIPTION("Sample QMI client driver");
622842891beSBjorn Andersson MODULE_LICENSE("GPL v2");
623