qmi_sample_client.c (14521B)
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 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 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 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 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 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(req.ping, "ping", 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(resp->data, req->data, 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");