linux/samples/qmi/qmi_sample_client.c
<<
>>
Prefs
   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 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 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