Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Sample in-kernel QMI client driver
0004  *
0005  * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
0006  * Copyright (C) 2017 Linaro Ltd.
0007  */
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/debugfs.h>
0011 #include <linux/device.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/qrtr.h>
0014 #include <linux/net.h>
0015 #include <linux/completion.h>
0016 #include <linux/idr.h>
0017 #include <linux/string.h>
0018 #include <net/sock.h>
0019 #include <linux/soc/qcom/qmi.h>
0020 
0021 #define PING_REQ1_TLV_TYPE      0x1
0022 #define PING_RESP1_TLV_TYPE     0x2
0023 #define PING_OPT1_TLV_TYPE      0x10
0024 #define PING_OPT2_TLV_TYPE      0x11
0025 
0026 #define DATA_REQ1_TLV_TYPE      0x1
0027 #define DATA_RESP1_TLV_TYPE     0x2
0028 #define DATA_OPT1_TLV_TYPE      0x10
0029 #define DATA_OPT2_TLV_TYPE      0x11
0030 
0031 #define TEST_MED_DATA_SIZE_V01      8192
0032 #define TEST_MAX_NAME_SIZE_V01      255
0033 
0034 #define TEST_PING_REQ_MSG_ID_V01    0x20
0035 #define TEST_DATA_REQ_MSG_ID_V01    0x21
0036 
0037 #define TEST_PING_REQ_MAX_MSG_LEN_V01   266
0038 #define TEST_DATA_REQ_MAX_MSG_LEN_V01   8456
0039 
0040 struct test_name_type_v01 {
0041     u32 name_len;
0042     char name[TEST_MAX_NAME_SIZE_V01];
0043 };
0044 
0045 static struct qmi_elem_info test_name_type_v01_ei[] = {
0046     {
0047         .data_type  = QMI_DATA_LEN,
0048         .elem_len   = 1,
0049         .elem_size  = sizeof(u8),
0050         .array_type = NO_ARRAY,
0051         .tlv_type   = QMI_COMMON_TLV_TYPE,
0052         .offset     = offsetof(struct test_name_type_v01,
0053                        name_len),
0054     },
0055     {
0056         .data_type  = QMI_UNSIGNED_1_BYTE,
0057         .elem_len   = TEST_MAX_NAME_SIZE_V01,
0058         .elem_size  = sizeof(char),
0059         .array_type = VAR_LEN_ARRAY,
0060         .tlv_type   = QMI_COMMON_TLV_TYPE,
0061         .offset     = offsetof(struct test_name_type_v01,
0062                        name),
0063     },
0064     {}
0065 };
0066 
0067 struct test_ping_req_msg_v01 {
0068     char ping[4];
0069 
0070     u8 client_name_valid;
0071     struct test_name_type_v01 client_name;
0072 };
0073 
0074 static struct qmi_elem_info test_ping_req_msg_v01_ei[] = {
0075     {
0076         .data_type  = QMI_UNSIGNED_1_BYTE,
0077         .elem_len   = 4,
0078         .elem_size  = sizeof(char),
0079         .array_type = STATIC_ARRAY,
0080         .tlv_type   = PING_REQ1_TLV_TYPE,
0081         .offset     = offsetof(struct test_ping_req_msg_v01,
0082                        ping),
0083     },
0084     {
0085         .data_type  = QMI_OPT_FLAG,
0086         .elem_len   = 1,
0087         .elem_size  = sizeof(u8),
0088         .array_type = NO_ARRAY,
0089         .tlv_type   = PING_OPT1_TLV_TYPE,
0090         .offset     = offsetof(struct test_ping_req_msg_v01,
0091                        client_name_valid),
0092     },
0093     {
0094         .data_type  = QMI_STRUCT,
0095         .elem_len   = 1,
0096         .elem_size  = sizeof(struct test_name_type_v01),
0097         .array_type = NO_ARRAY,
0098         .tlv_type   = PING_OPT1_TLV_TYPE,
0099         .offset     = offsetof(struct test_ping_req_msg_v01,
0100                        client_name),
0101         .ei_array   = test_name_type_v01_ei,
0102     },
0103     {}
0104 };
0105 
0106 struct test_ping_resp_msg_v01 {
0107     struct qmi_response_type_v01 resp;
0108 
0109     u8 pong_valid;
0110     char pong[4];
0111 
0112     u8 service_name_valid;
0113     struct test_name_type_v01 service_name;
0114 };
0115 
0116 static struct qmi_elem_info test_ping_resp_msg_v01_ei[] = {
0117     {
0118         .data_type  = QMI_STRUCT,
0119         .elem_len   = 1,
0120         .elem_size  = sizeof(struct qmi_response_type_v01),
0121         .array_type = NO_ARRAY,
0122         .tlv_type   = PING_RESP1_TLV_TYPE,
0123         .offset     = offsetof(struct test_ping_resp_msg_v01,
0124                        resp),
0125         .ei_array   = qmi_response_type_v01_ei,
0126     },
0127     {
0128         .data_type  = QMI_OPT_FLAG,
0129         .elem_len   = 1,
0130         .elem_size  = sizeof(u8),
0131         .array_type = NO_ARRAY,
0132         .tlv_type   = PING_OPT1_TLV_TYPE,
0133         .offset     = offsetof(struct test_ping_resp_msg_v01,
0134                        pong_valid),
0135     },
0136     {
0137         .data_type  = QMI_UNSIGNED_1_BYTE,
0138         .elem_len   = 4,
0139         .elem_size  = sizeof(char),
0140         .array_type = STATIC_ARRAY,
0141         .tlv_type   = PING_OPT1_TLV_TYPE,
0142         .offset     = offsetof(struct test_ping_resp_msg_v01,
0143                        pong),
0144     },
0145     {
0146         .data_type  = QMI_OPT_FLAG,
0147         .elem_len   = 1,
0148         .elem_size  = sizeof(u8),
0149         .array_type = NO_ARRAY,
0150         .tlv_type   = PING_OPT2_TLV_TYPE,
0151         .offset     = offsetof(struct test_ping_resp_msg_v01,
0152                        service_name_valid),
0153     },
0154     {
0155         .data_type  = QMI_STRUCT,
0156         .elem_len   = 1,
0157         .elem_size  = sizeof(struct test_name_type_v01),
0158         .array_type = NO_ARRAY,
0159         .tlv_type   = PING_OPT2_TLV_TYPE,
0160         .offset     = offsetof(struct test_ping_resp_msg_v01,
0161                        service_name),
0162         .ei_array   = test_name_type_v01_ei,
0163     },
0164     {}
0165 };
0166 
0167 struct test_data_req_msg_v01 {
0168     u32 data_len;
0169     u8 data[TEST_MED_DATA_SIZE_V01];
0170 
0171     u8 client_name_valid;
0172     struct test_name_type_v01 client_name;
0173 };
0174 
0175 static struct qmi_elem_info test_data_req_msg_v01_ei[] = {
0176     {
0177         .data_type  = QMI_DATA_LEN,
0178         .elem_len   = 1,
0179         .elem_size  = sizeof(u32),
0180         .array_type = NO_ARRAY,
0181         .tlv_type   = DATA_REQ1_TLV_TYPE,
0182         .offset     = offsetof(struct test_data_req_msg_v01,
0183                        data_len),
0184     },
0185     {
0186         .data_type  = QMI_UNSIGNED_1_BYTE,
0187         .elem_len   = TEST_MED_DATA_SIZE_V01,
0188         .elem_size  = sizeof(u8),
0189         .array_type = VAR_LEN_ARRAY,
0190         .tlv_type   = DATA_REQ1_TLV_TYPE,
0191         .offset     = offsetof(struct test_data_req_msg_v01,
0192                        data),
0193     },
0194     {
0195         .data_type  = QMI_OPT_FLAG,
0196         .elem_len   = 1,
0197         .elem_size  = sizeof(u8),
0198         .array_type = NO_ARRAY,
0199         .tlv_type   = DATA_OPT1_TLV_TYPE,
0200         .offset     = offsetof(struct test_data_req_msg_v01,
0201                        client_name_valid),
0202     },
0203     {
0204         .data_type  = QMI_STRUCT,
0205         .elem_len   = 1,
0206         .elem_size  = sizeof(struct test_name_type_v01),
0207         .array_type = NO_ARRAY,
0208         .tlv_type   = DATA_OPT1_TLV_TYPE,
0209         .offset     = offsetof(struct test_data_req_msg_v01,
0210                        client_name),
0211         .ei_array   = test_name_type_v01_ei,
0212     },
0213     {}
0214 };
0215 
0216 struct test_data_resp_msg_v01 {
0217     struct qmi_response_type_v01 resp;
0218 
0219     u8 data_valid;
0220     u32 data_len;
0221     u8 data[TEST_MED_DATA_SIZE_V01];
0222 
0223     u8 service_name_valid;
0224     struct test_name_type_v01 service_name;
0225 };
0226 
0227 static struct qmi_elem_info test_data_resp_msg_v01_ei[] = {
0228     {
0229         .data_type  = QMI_STRUCT,
0230         .elem_len   = 1,
0231         .elem_size  = sizeof(struct qmi_response_type_v01),
0232         .array_type = NO_ARRAY,
0233         .tlv_type   = DATA_RESP1_TLV_TYPE,
0234         .offset     = offsetof(struct test_data_resp_msg_v01,
0235                        resp),
0236         .ei_array   = qmi_response_type_v01_ei,
0237     },
0238     {
0239         .data_type  = QMI_OPT_FLAG,
0240         .elem_len   = 1,
0241         .elem_size  = sizeof(u8),
0242         .array_type = NO_ARRAY,
0243         .tlv_type   = DATA_OPT1_TLV_TYPE,
0244         .offset     = offsetof(struct test_data_resp_msg_v01,
0245                        data_valid),
0246     },
0247     {
0248         .data_type  = QMI_DATA_LEN,
0249         .elem_len   = 1,
0250         .elem_size  = sizeof(u32),
0251         .array_type = NO_ARRAY,
0252         .tlv_type   = DATA_OPT1_TLV_TYPE,
0253         .offset     = offsetof(struct test_data_resp_msg_v01,
0254                        data_len),
0255     },
0256     {
0257         .data_type  = QMI_UNSIGNED_1_BYTE,
0258         .elem_len   = TEST_MED_DATA_SIZE_V01,
0259         .elem_size  = sizeof(u8),
0260         .array_type = VAR_LEN_ARRAY,
0261         .tlv_type   = DATA_OPT1_TLV_TYPE,
0262         .offset     = offsetof(struct test_data_resp_msg_v01,
0263                        data),
0264     },
0265     {
0266         .data_type  = QMI_OPT_FLAG,
0267         .elem_len   = 1,
0268         .elem_size  = sizeof(u8),
0269         .array_type = NO_ARRAY,
0270         .tlv_type   = DATA_OPT2_TLV_TYPE,
0271         .offset     = offsetof(struct test_data_resp_msg_v01,
0272                        service_name_valid),
0273     },
0274     {
0275         .data_type  = QMI_STRUCT,
0276         .elem_len   = 1,
0277         .elem_size  = sizeof(struct test_name_type_v01),
0278         .array_type = NO_ARRAY,
0279         .tlv_type   = DATA_OPT2_TLV_TYPE,
0280         .offset     = offsetof(struct test_data_resp_msg_v01,
0281                        service_name),
0282         .ei_array   = test_name_type_v01_ei,
0283     },
0284     {}
0285 };
0286 
0287 /*
0288  * ping_write() - ping_pong debugfs file write handler
0289  * @file:   debugfs file context
0290  * @user_buf:   reference to the user data (ignored)
0291  * @count:  number of bytes in @user_buf
0292  * @ppos:   offset in @file to write
0293  *
0294  * This function allows user space to send out a ping_pong QMI encoded message
0295  * to the associated remote test service and will return with the result of the
0296  * transaction. It serves as an example of how to provide a custom response
0297  * handler.
0298  *
0299  * Return: @count, or negative errno on failure.
0300  */
0301 static ssize_t ping_write(struct file *file, const char __user *user_buf,
0302               size_t count, loff_t *ppos)
0303 {
0304     struct qmi_handle *qmi = file->private_data;
0305     struct test_ping_req_msg_v01 req = {};
0306     struct qmi_txn txn;
0307     int ret;
0308 
0309     memcpy(req.ping, "ping", sizeof(req.ping));
0310 
0311     ret = qmi_txn_init(qmi, &txn, NULL, NULL);
0312     if (ret < 0)
0313         return ret;
0314 
0315     ret = qmi_send_request(qmi, NULL, &txn,
0316                    TEST_PING_REQ_MSG_ID_V01,
0317                    TEST_PING_REQ_MAX_MSG_LEN_V01,
0318                    test_ping_req_msg_v01_ei, &req);
0319     if (ret < 0) {
0320         qmi_txn_cancel(&txn);
0321         return ret;
0322     }
0323 
0324     ret = qmi_txn_wait(&txn, 5 * HZ);
0325     if (ret < 0)
0326         count = ret;
0327 
0328     return count;
0329 }
0330 
0331 static const struct file_operations ping_fops = {
0332     .open = simple_open,
0333     .write = ping_write,
0334 };
0335 
0336 static void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
0337              struct qmi_txn *txn, const void *data)
0338 {
0339     const struct test_ping_resp_msg_v01 *resp = data;
0340 
0341     if (!txn) {
0342         pr_err("spurious ping response\n");
0343         return;
0344     }
0345 
0346     if (resp->resp.result == QMI_RESULT_FAILURE_V01)
0347         txn->result = -ENXIO;
0348     else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4))
0349         txn->result = -EINVAL;
0350 
0351     complete(&txn->completion);
0352 }
0353 
0354 /*
0355  * data_write() - data debugfs file write handler
0356  * @file:   debugfs file context
0357  * @user_buf:   reference to the user data
0358  * @count:  number of bytes in @user_buf
0359  * @ppos:   offset in @file to write
0360  *
0361  * This function allows user space to send out a data QMI encoded message to
0362  * the associated remote test service and will return with the result of the
0363  * transaction. It serves as an example of how to have the QMI helpers decode a
0364  * transaction response into a provided object automatically.
0365  *
0366  * Return: @count, or negative errno on failure.
0367  */
0368 static ssize_t data_write(struct file *file, const char __user *user_buf,
0369               size_t count, loff_t *ppos)
0370 
0371 {
0372     struct qmi_handle *qmi = file->private_data;
0373     struct test_data_resp_msg_v01 *resp;
0374     struct test_data_req_msg_v01 *req;
0375     struct qmi_txn txn;
0376     int ret;
0377 
0378     req = kzalloc(sizeof(*req), GFP_KERNEL);
0379     if (!req)
0380         return -ENOMEM;
0381 
0382     resp = kzalloc(sizeof(*resp), GFP_KERNEL);
0383     if (!resp) {
0384         kfree(req);
0385         return -ENOMEM;
0386     }
0387 
0388     req->data_len = min_t(size_t, sizeof(req->data), count);
0389     if (copy_from_user(req->data, user_buf, req->data_len)) {
0390         ret = -EFAULT;
0391         goto out;
0392     }
0393 
0394     ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp);
0395     if (ret < 0)
0396         goto out;
0397 
0398     ret = qmi_send_request(qmi, NULL, &txn,
0399                    TEST_DATA_REQ_MSG_ID_V01,
0400                    TEST_DATA_REQ_MAX_MSG_LEN_V01,
0401                    test_data_req_msg_v01_ei, req);
0402     if (ret < 0) {
0403         qmi_txn_cancel(&txn);
0404         goto out;
0405     }
0406 
0407     ret = qmi_txn_wait(&txn, 5 * HZ);
0408     if (ret < 0) {
0409         goto out;
0410     } else if (!resp->data_valid ||
0411            resp->data_len != req->data_len ||
0412            memcmp(resp->data, req->data, req->data_len)) {
0413         pr_err("response data doesn't match expectation\n");
0414         ret = -EINVAL;
0415         goto out;
0416     }
0417 
0418     ret = count;
0419 
0420 out:
0421     kfree(resp);
0422     kfree(req);
0423 
0424     return ret;
0425 }
0426 
0427 static const struct file_operations data_fops = {
0428     .open = simple_open,
0429     .write = data_write,
0430 };
0431 
0432 static const struct qmi_msg_handler qmi_sample_handlers[] = {
0433     {
0434         .type = QMI_RESPONSE,
0435         .msg_id = TEST_PING_REQ_MSG_ID_V01,
0436         .ei = test_ping_resp_msg_v01_ei,
0437         .decoded_size = sizeof(struct test_ping_req_msg_v01),
0438         .fn = ping_pong_cb
0439     },
0440     {}
0441 };
0442 
0443 struct qmi_sample {
0444     struct qmi_handle qmi;
0445 
0446     struct dentry *de_dir;
0447     struct dentry *de_data;
0448     struct dentry *de_ping;
0449 };
0450 
0451 static struct dentry *qmi_debug_dir;
0452 
0453 static int qmi_sample_probe(struct platform_device *pdev)
0454 {
0455     struct sockaddr_qrtr *sq;
0456     struct qmi_sample *sample;
0457     char path[20];
0458     int ret;
0459 
0460     sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL);
0461     if (!sample)
0462         return -ENOMEM;
0463 
0464     ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01,
0465                   NULL,
0466                   qmi_sample_handlers);
0467     if (ret < 0)
0468         return ret;
0469 
0470     sq = dev_get_platdata(&pdev->dev);
0471     ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq,
0472                  sizeof(*sq), 0);
0473     if (ret < 0) {
0474         pr_err("failed to connect to remote service port\n");
0475         goto err_release_qmi_handle;
0476     }
0477 
0478     snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port);
0479 
0480     sample->de_dir = debugfs_create_dir(path, qmi_debug_dir);
0481     if (IS_ERR(sample->de_dir)) {
0482         ret = PTR_ERR(sample->de_dir);
0483         goto err_release_qmi_handle;
0484     }
0485 
0486     sample->de_data = debugfs_create_file("data", 0600, sample->de_dir,
0487                           sample, &data_fops);
0488     if (IS_ERR(sample->de_data)) {
0489         ret = PTR_ERR(sample->de_data);
0490         goto err_remove_de_dir;
0491     }
0492 
0493     sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir,
0494                           sample, &ping_fops);
0495     if (IS_ERR(sample->de_ping)) {
0496         ret = PTR_ERR(sample->de_ping);
0497         goto err_remove_de_data;
0498     }
0499 
0500     platform_set_drvdata(pdev, sample);
0501 
0502     return 0;
0503 
0504 err_remove_de_data:
0505     debugfs_remove(sample->de_data);
0506 err_remove_de_dir:
0507     debugfs_remove(sample->de_dir);
0508 err_release_qmi_handle:
0509     qmi_handle_release(&sample->qmi);
0510 
0511     return ret;
0512 }
0513 
0514 static int qmi_sample_remove(struct platform_device *pdev)
0515 {
0516     struct qmi_sample *sample = platform_get_drvdata(pdev);
0517 
0518     debugfs_remove(sample->de_ping);
0519     debugfs_remove(sample->de_data);
0520     debugfs_remove(sample->de_dir);
0521 
0522     qmi_handle_release(&sample->qmi);
0523 
0524     return 0;
0525 }
0526 
0527 static struct platform_driver qmi_sample_driver = {
0528     .probe = qmi_sample_probe,
0529     .remove = qmi_sample_remove,
0530     .driver = {
0531         .name = "qmi_sample_client",
0532     },
0533 };
0534 
0535 static int qmi_sample_new_server(struct qmi_handle *qmi,
0536                  struct qmi_service *service)
0537 {
0538     struct platform_device *pdev;
0539     struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port };
0540     int ret;
0541 
0542     pdev = platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO);
0543     if (!pdev)
0544         return -ENOMEM;
0545 
0546     ret = platform_device_add_data(pdev, &sq, sizeof(sq));
0547     if (ret)
0548         goto err_put_device;
0549 
0550     ret = platform_device_add(pdev);
0551     if (ret)
0552         goto err_put_device;
0553 
0554     service->priv = pdev;
0555 
0556     return 0;
0557 
0558 err_put_device:
0559     platform_device_put(pdev);
0560 
0561     return ret;
0562 }
0563 
0564 static void qmi_sample_del_server(struct qmi_handle *qmi,
0565                   struct qmi_service *service)
0566 {
0567     struct platform_device *pdev = service->priv;
0568 
0569     platform_device_unregister(pdev);
0570 }
0571 
0572 static struct qmi_handle lookup_client;
0573 
0574 static const struct qmi_ops lookup_ops = {
0575     .new_server = qmi_sample_new_server,
0576     .del_server = qmi_sample_del_server,
0577 };
0578 
0579 static int qmi_sample_init(void)
0580 {
0581     int ret;
0582 
0583     qmi_debug_dir = debugfs_create_dir("qmi_sample", NULL);
0584     if (IS_ERR(qmi_debug_dir)) {
0585         pr_err("failed to create qmi_sample dir\n");
0586         return PTR_ERR(qmi_debug_dir);
0587     }
0588 
0589     ret = platform_driver_register(&qmi_sample_driver);
0590     if (ret)
0591         goto err_remove_debug_dir;
0592 
0593     ret = qmi_handle_init(&lookup_client, 0, &lookup_ops, NULL);
0594     if (ret < 0)
0595         goto err_unregister_driver;
0596 
0597     qmi_add_lookup(&lookup_client, 15, 0, 0);
0598 
0599     return 0;
0600 
0601 err_unregister_driver:
0602     platform_driver_unregister(&qmi_sample_driver);
0603 err_remove_debug_dir:
0604     debugfs_remove(qmi_debug_dir);
0605 
0606     return ret;
0607 }
0608 
0609 static void qmi_sample_exit(void)
0610 {
0611     qmi_handle_release(&lookup_client);
0612 
0613     platform_driver_unregister(&qmi_sample_driver);
0614 
0615     debugfs_remove(qmi_debug_dir);
0616 }
0617 
0618 module_init(qmi_sample_init);
0619 module_exit(qmi_sample_exit);
0620 
0621 MODULE_DESCRIPTION("Sample QMI client driver");
0622 MODULE_LICENSE("GPL v2");