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

source code of linux/samples/qmi/qmi_sample_client.c