1 // SPDX-License-Identifier: GPL-2.0
3 * Sample in-kernel QMI client driver
5 * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
6 * Copyright (C) 2017 Linaro Ltd.
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>
19 #include <linux/soc/qcom/qmi.h>
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
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
31 #define TEST_MED_DATA_SIZE_V01 8192
32 #define TEST_MAX_NAME_SIZE_V01 255
34 #define TEST_PING_REQ_MSG_ID_V01 0x20
35 #define TEST_DATA_REQ_MSG_ID_V01 0x21
37 #define TEST_PING_REQ_MAX_MSG_LEN_V01 266
38 #define TEST_DATA_REQ_MAX_MSG_LEN_V01 8456
40 struct test_name_type_v01
{
42 char name
[TEST_MAX_NAME_SIZE_V01
];
45 static struct qmi_elem_info test_name_type_v01_ei
[] = {
47 .data_type
= QMI_DATA_LEN
,
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
,
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
,
67 struct test_ping_req_msg_v01
{
71 struct test_name_type_v01 client_name
;
74 static struct qmi_elem_info test_ping_req_msg_v01_ei
[] = {
76 .data_type
= QMI_UNSIGNED_1_BYTE
,
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
,
85 .data_type
= QMI_OPT_FLAG
,
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
,
94 .data_type
= QMI_STRUCT
,
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
,
101 .ei_array
= test_name_type_v01_ei
,
106 struct test_ping_resp_msg_v01
{
107 struct qmi_response_type_v01 resp
;
112 u8 service_name_valid
;
113 struct test_name_type_v01 service_name
;
116 static struct qmi_elem_info test_ping_resp_msg_v01_ei
[] = {
118 .data_type
= QMI_STRUCT
,
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
,
125 .ei_array
= qmi_response_type_v01_ei
,
128 .data_type
= QMI_OPT_FLAG
,
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
,
137 .data_type
= QMI_UNSIGNED_1_BYTE
,
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
,
146 .data_type
= QMI_OPT_FLAG
,
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
,
155 .data_type
= QMI_STRUCT
,
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
,
162 .ei_array
= test_name_type_v01_ei
,
167 struct test_data_req_msg_v01
{
169 u8 data
[TEST_MED_DATA_SIZE_V01
];
171 u8 client_name_valid
;
172 struct test_name_type_v01 client_name
;
175 static struct qmi_elem_info test_data_req_msg_v01_ei
[] = {
177 .data_type
= QMI_DATA_LEN
,
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
,
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
,
195 .data_type
= QMI_OPT_FLAG
,
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
,
204 .data_type
= QMI_STRUCT
,
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
,
211 .ei_array
= test_name_type_v01_ei
,
216 struct test_data_resp_msg_v01
{
217 struct qmi_response_type_v01 resp
;
221 u8 data
[TEST_MED_DATA_SIZE_V01
];
223 u8 service_name_valid
;
224 struct test_name_type_v01 service_name
;
227 static struct qmi_elem_info test_data_resp_msg_v01_ei
[] = {
229 .data_type
= QMI_STRUCT
,
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
,
236 .ei_array
= qmi_response_type_v01_ei
,
239 .data_type
= QMI_OPT_FLAG
,
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
,
248 .data_type
= QMI_DATA_LEN
,
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
,
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
,
266 .data_type
= QMI_OPT_FLAG
,
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
,
275 .data_type
= QMI_STRUCT
,
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
,
282 .ei_array
= test_name_type_v01_ei
,
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
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
299 * Return: @count, or negative errno on failure.
301 static ssize_t
ping_write(struct file
*file
, const char __user
*user_buf
,
302 size_t count
, loff_t
*ppos
)
304 struct qmi_handle
*qmi
= file
->private_data
;
305 struct test_ping_req_msg_v01 req
= {};
309 memcpy(req
.ping
, "ping", sizeof(req
.ping
));
311 ret
= qmi_txn_init(qmi
, &txn
, NULL
, NULL
);
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
);
320 qmi_txn_cancel(&txn
);
324 ret
= qmi_txn_wait(&txn
, 5 * HZ
);
331 static const struct file_operations ping_fops
= {
336 static void ping_pong_cb(struct qmi_handle
*qmi
, struct sockaddr_qrtr
*sq
,
337 struct qmi_txn
*txn
, const void *data
)
339 const struct test_ping_resp_msg_v01
*resp
= data
;
342 pr_err("spurious ping response\n");
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
;
351 complete(&txn
->completion
);
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
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.
366 * Return: @count, or negative errno on failure.
368 static ssize_t
data_write(struct file
*file
, const char __user
*user_buf
,
369 size_t count
, loff_t
*ppos
)
372 struct qmi_handle
*qmi
= file
->private_data
;
373 struct test_data_resp_msg_v01
*resp
;
374 struct test_data_req_msg_v01
*req
;
378 req
= kzalloc(sizeof(*req
), GFP_KERNEL
);
382 resp
= kzalloc(sizeof(*resp
), GFP_KERNEL
);
388 req
->data_len
= min_t(size_t, sizeof(req
->data
), count
);
389 if (copy_from_user(req
->data
, user_buf
, req
->data_len
)) {
394 ret
= qmi_txn_init(qmi
, &txn
, test_data_resp_msg_v01_ei
, resp
);
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
);
403 qmi_txn_cancel(&txn
);
407 ret
= qmi_txn_wait(&txn
, 5 * HZ
);
410 } else if (!resp
->data_valid
||
411 resp
->data_len
!= req
->data_len
||
412 memcmp(resp
->data
, req
->data
, req
->data_len
)) {
413 pr_err("response data doesn't match expectation\n");
427 static const struct file_operations data_fops
= {
432 static struct qmi_msg_handler qmi_sample_handlers
[] = {
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
),
444 struct qmi_handle qmi
;
446 struct dentry
*de_dir
;
447 struct dentry
*de_data
;
448 struct dentry
*de_ping
;
451 static struct dentry
*qmi_debug_dir
;
453 static int qmi_sample_probe(struct platform_device
*pdev
)
455 struct sockaddr_qrtr
*sq
;
456 struct qmi_sample
*sample
;
460 sample
= devm_kzalloc(&pdev
->dev
, sizeof(*sample
), GFP_KERNEL
);
464 ret
= qmi_handle_init(&sample
->qmi
, TEST_DATA_REQ_MAX_MSG_LEN_V01
,
466 qmi_sample_handlers
);
470 sq
= dev_get_platdata(&pdev
->dev
);
471 ret
= kernel_connect(sample
->qmi
.sock
, (struct sockaddr
*)sq
,
474 pr_err("failed to connect to remote service port\n");
475 goto err_release_qmi_handle
;
478 snprintf(path
, sizeof(path
), "%d:%d", sq
->sq_node
, sq
->sq_port
);
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
;
486 sample
->de_data
= debugfs_create_file("data", 0600, sample
->de_dir
,
488 if (IS_ERR(sample
->de_data
)) {
489 ret
= PTR_ERR(sample
->de_data
);
490 goto err_remove_de_dir
;
493 sample
->de_ping
= debugfs_create_file("ping", 0600, sample
->de_dir
,
495 if (IS_ERR(sample
->de_ping
)) {
496 ret
= PTR_ERR(sample
->de_ping
);
497 goto err_remove_de_data
;
500 platform_set_drvdata(pdev
, sample
);
505 debugfs_remove(sample
->de_data
);
507 debugfs_remove(sample
->de_dir
);
508 err_release_qmi_handle
:
509 qmi_handle_release(&sample
->qmi
);
514 static int qmi_sample_remove(struct platform_device
*pdev
)
516 struct qmi_sample
*sample
= platform_get_drvdata(pdev
);
518 debugfs_remove(sample
->de_ping
);
519 debugfs_remove(sample
->de_data
);
520 debugfs_remove(sample
->de_dir
);
522 qmi_handle_release(&sample
->qmi
);
527 static struct platform_driver qmi_sample_driver
= {
528 .probe
= qmi_sample_probe
,
529 .remove
= qmi_sample_remove
,
531 .name
= "qmi_sample_client",
535 static int qmi_sample_new_server(struct qmi_handle
*qmi
,
536 struct qmi_service
*service
)
538 struct platform_device
*pdev
;
539 struct sockaddr_qrtr sq
= { AF_QIPCRTR
, service
->node
, service
->port
};
542 pdev
= platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO
);
546 ret
= platform_device_add_data(pdev
, &sq
, sizeof(sq
));
550 ret
= platform_device_add(pdev
);
554 service
->priv
= pdev
;
559 platform_device_put(pdev
);
564 static void qmi_sample_del_server(struct qmi_handle
*qmi
,
565 struct qmi_service
*service
)
567 struct platform_device
*pdev
= service
->priv
;
569 platform_device_unregister(pdev
);
572 static struct qmi_handle lookup_client
;
574 static struct qmi_ops lookup_ops
= {
575 .new_server
= qmi_sample_new_server
,
576 .del_server
= qmi_sample_del_server
,
579 static int qmi_sample_init(void)
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
);
589 ret
= platform_driver_register(&qmi_sample_driver
);
591 goto err_remove_debug_dir
;
593 ret
= qmi_handle_init(&lookup_client
, 0, &lookup_ops
, NULL
);
595 goto err_unregister_driver
;
597 qmi_add_lookup(&lookup_client
, 15, 0, 0);
601 err_unregister_driver
:
602 platform_driver_unregister(&qmi_sample_driver
);
603 err_remove_debug_dir
:
604 debugfs_remove(qmi_debug_dir
);
609 static void qmi_sample_exit(void)
611 qmi_handle_release(&lookup_client
);
613 platform_driver_unregister(&qmi_sample_driver
);
615 debugfs_remove(qmi_debug_dir
);
618 module_init(qmi_sample_init
);
619 module_exit(qmi_sample_exit
);
621 MODULE_DESCRIPTION("Sample QMI client driver");
622 MODULE_LICENSE("GPL v2");