// SPDX-License-Identifier: GPL-2.0/* * Sample in-kernel QMI client driver * * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/debugfs.h>#include <linux/device.h>#include <linux/platform_device.h>#include <linux/qrtr.h>#include <linux/net.h>#include <linux/completion.h>#include <linux/idr.h>#include <linux/string.h>#include <net/sock.h>#include <linux/soc/qcom/qmi.h> #define PING_REQ1_TLV_TYPE 0x1#define PING_RESP1_TLV_TYPE 0x2#define PING_OPT1_TLV_TYPE 0x10#define PING_OPT2_TLV_TYPE 0x11 #define DATA_REQ1_TLV_TYPE 0x1#define DATA_RESP1_TLV_TYPE 0x2#define DATA_OPT1_TLV_TYPE 0x10#define DATA_OPT2_TLV_TYPE 0x11 #define TEST_MED_DATA_SIZE_V01 8192#define TEST_MAX_NAME_SIZE_V01 255 #define TEST_PING_REQ_MSG_ID_V01 0x20#define TEST_DATA_REQ_MSG_ID_V01 0x21 #define TEST_PING_REQ_MAX_MSG_LEN_V01 266#define TEST_DATA_REQ_MAX_MSG_LEN_V01 8456 struct test_name_type_v01 { u32 name_len; char name[TEST_MAX_NAME_SIZE_V01];}; static const struct qmi_elem_info test_name_type_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = QMI_COMMON_TLV_TYPE, .offset = offsetof(struct test_name_type_v01, name_len), }, { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = TEST_MAX_NAME_SIZE_V01, .elem_size = sizeof(char), .array_type = VAR_LEN_ARRAY, .tlv_type = QMI_COMMON_TLV_TYPE, .offset = offsetof(struct test_name_type_v01, name), }, {}}; struct test_ping_req_msg_v01 { char ping[4]; u8 client_name_valid; struct test_name_type_v01 client_name;}; static const struct qmi_elem_info test_ping_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 4, .elem_size = sizeof(char), .array_type = STATIC_ARRAY, .tlv_type = PING_REQ1_TLV_TYPE, .offset = offsetof(struct test_ping_req_msg_v01, ping), }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = PING_OPT1_TLV_TYPE, .offset = offsetof(struct test_ping_req_msg_v01, client_name_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct test_name_type_v01), .array_type = NO_ARRAY, .tlv_type = PING_OPT1_TLV_TYPE, .offset = offsetof(struct test_ping_req_msg_v01, client_name), .ei_array = test_name_type_v01_ei, }, {}}; struct test_ping_resp_msg_v01 { struct qmi_response_type_v01 resp; u8 pong_valid; char pong[4]; u8 service_name_valid; struct test_name_type_v01 service_name;}; static const struct qmi_elem_info test_ping_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct qmi_response_type_v01), .array_type = NO_ARRAY, .tlv_type = PING_RESP1_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, resp), .ei_array = qmi_response_type_v01_ei, }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = PING_OPT1_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, pong_valid), }, { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 4, .elem_size = sizeof(char), .array_type = STATIC_ARRAY, .tlv_type = PING_OPT1_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, pong), }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = PING_OPT2_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, service_name_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct test_name_type_v01), .array_type = NO_ARRAY, .tlv_type = PING_OPT2_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, service_name), .ei_array = test_name_type_v01_ei, }, {}}; struct test_data_req_msg_v01 { u32 data_len; u8 data[TEST_MED_DATA_SIZE_V01]; u8 client_name_valid; struct test_name_type_v01 client_name;}; static const struct qmi_elem_info test_data_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, .elem_size = sizeof(u32), .array_type = NO_ARRAY, .tlv_type = DATA_REQ1_TLV_TYPE, .offset = offsetof(struct test_data_req_msg_v01, data_len), }, { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = TEST_MED_DATA_SIZE_V01, .elem_size = sizeof(u8), .array_type = VAR_LEN_ARRAY, .tlv_type = DATA_REQ1_TLV_TYPE, .offset = offsetof(struct test_data_req_msg_v01, data), }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_req_msg_v01, client_name_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct test_name_type_v01), .array_type = NO_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_req_msg_v01, client_name), .ei_array = test_name_type_v01_ei, }, {}}; struct test_data_resp_msg_v01 { struct qmi_response_type_v01 resp; u8 data_valid; u32 data_len; u8 data[TEST_MED_DATA_SIZE_V01]; u8 service_name_valid; struct test_name_type_v01 service_name;}; static const struct qmi_elem_info test_data_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct qmi_response_type_v01), .array_type = NO_ARRAY, .tlv_type = DATA_RESP1_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, resp), .ei_array = qmi_response_type_v01_ei, }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, data_valid), }, { .data_type = QMI_DATA_LEN, .elem_len = 1, .elem_size = sizeof(u32), .array_type = NO_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, data_len), }, { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = TEST_MED_DATA_SIZE_V01, .elem_size = sizeof(u8), .array_type = VAR_LEN_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, data), }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = DATA_OPT2_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, service_name_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct test_name_type_v01), .array_type = NO_ARRAY, .tlv_type = DATA_OPT2_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, service_name), .ei_array = test_name_type_v01_ei, }, {}}; /* * ping_write() - ping_pong debugfs file write handler * @file: debugfs file context * @user_buf: reference to the user data (ignored) * @count: number of bytes in @user_buf * @ppos: offset in @file to write * * This function allows user space to send out a ping_pong QMI encoded message * to the associated remote test service and will return with the result of the * transaction. It serves as an example of how to provide a custom response * handler. * * Return: @count, or negative errno on failure. */static ssize_t ping_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos){ struct qmi_handle *qmi = file->private_data; struct test_ping_req_msg_v01 req = {}; struct qmi_txn txn; int ret; memcpy(req.ping, "ping", sizeof(req.ping)); ret = qmi_txn_init(qmi, &txn, NULL, NULL); if (ret < 0) return ret; ret = qmi_send_request(qmi, NULL, &txn, TEST_PING_REQ_MSG_ID_V01, TEST_PING_REQ_MAX_MSG_LEN_V01, test_ping_req_msg_v01_ei, &req); if (ret < 0) { qmi_txn_cancel(&txn); return ret; } ret = qmi_txn_wait(&txn, 5 * HZ); if (ret < 0) count = ret; return count;} static const struct file_operations ping_fops = { .open = simple_open, .write = ping_write,}; static void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, struct qmi_txn *txn, const void *data){ const struct test_ping_resp_msg_v01 *resp = data; if (!txn) { pr_err("spurious ping response\n"); return; } if (resp->resp.result == QMI_RESULT_FAILURE_V01) txn->result = -ENXIO; else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4)) txn->result = -EINVAL; complete(&txn->completion);} /* * data_write() - data debugfs file write handler * @file: debugfs file context * @user_buf: reference to the user data * @count: number of bytes in @user_buf * @ppos: offset in @file to write * * This function allows user space to send out a data QMI encoded message to * the associated remote test service and will return with the result of the * transaction. It serves as an example of how to have the QMI helpers decode a * transaction response into a provided object automatically. * * Return: @count, or negative errno on failure. */static ssize_t data_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct qmi_handle *qmi = file->private_data; struct test_data_resp_msg_v01 *resp; struct test_data_req_msg_v01 *req; struct qmi_txn txn; int ret; req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } req->data_len = min_t(size_t, sizeof(req->data), count); if (copy_from_user(req->data, user_buf, req->data_len)) { ret = -EFAULT; goto out; } ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp); if (ret < 0) goto out; ret = qmi_send_request(qmi, NULL, &txn, TEST_DATA_REQ_MSG_ID_V01, TEST_DATA_REQ_MAX_MSG_LEN_V01, test_data_req_msg_v01_ei, req); if (ret < 0) { qmi_txn_cancel(&txn); goto out; } ret = qmi_txn_wait(&txn, 5 * HZ); if (ret < 0) { goto out; } else if (!resp->data_valid || resp->data_len != req->data_len || memcmp(resp->data, req->data, req->data_len)) { pr_err("response data doesn't match expectation\n"); ret = -EINVAL; goto out; } ret = count; out: kfree(resp); kfree(req); return ret;} static const struct file_operations data_fops = { .open = simple_open, .write = data_write,}; static const struct qmi_msg_handler qmi_sample_handlers[] = { { .type = QMI_RESPONSE, .msg_id = TEST_PING_REQ_MSG_ID_V01, .ei = test_ping_resp_msg_v01_ei, .decoded_size = sizeof(struct test_ping_req_msg_v01), .fn = ping_pong_cb }, {}}; struct qmi_sample { struct qmi_handle qmi; struct dentry *de_dir; struct dentry *de_data; struct dentry *de_ping;}; static struct dentry *qmi_debug_dir; static int qmi_sample_probe(struct platform_device *pdev){ struct sockaddr_qrtr *sq; struct qmi_sample *sample; char path[20]; int ret; sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL); if (!sample) return -ENOMEM; ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01, NULL, qmi_sample_handlers); if (ret < 0) return ret; sq = dev_get_platdata(&pdev->dev); ret = kernel_connect(sample->qmi.sock, (struct sockaddr_unsized *)sq, sizeof(*sq), 0); if (ret < 0) { pr_err("failed to connect to remote service port\n"); goto err_release_qmi_handle; } snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port); sample->de_dir = debugfs_create_dir(path, qmi_debug_dir); if (IS_ERR(sample->de_dir)) { ret = PTR_ERR(sample->de_dir); goto err_release_qmi_handle; } sample->de_data = debugfs_create_file("data", 0600, sample->de_dir, sample, &data_fops); if (IS_ERR(sample->de_data)) { ret = PTR_ERR(sample->de_data); goto err_remove_de_dir; } sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir, sample, &ping_fops); if (IS_ERR(sample->de_ping)) { ret = PTR_ERR(sample->de_ping); goto err_remove_de_data; } platform_set_drvdata(pdev, sample);Lines 1–500 of 620. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Sample in-kernel QMI client driver * * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/qrtr.h> #include <linux/net.h> #include <linux/completion.h> #include <linux/idr.h> #include <linux/string.h> #include <net/sock.h> #include <linux/soc/qcom/qmi.h> #define PING_REQ1_TLV_TYPE 0x1 #define PING_RESP1_TLV_TYPE 0x2 #define PING_OPT1_TLV_TYPE 0x10 #define PING_OPT2_TLV_TYPE 0x11 #define DATA_REQ1_TLV_TYPE 0x1 #define DATA_RESP1_TLV_TYPE 0x2 #define DATA_OPT1_TLV_TYPE 0x10 #define DATA_OPT2_TLV_TYPE 0x11 #define TEST_MED_DATA_SIZE_V01 8192 #define TEST_MAX_NAME_SIZE_V01 255 #define TEST_PING_REQ_MSG_ID_V01 0x20 #define TEST_DATA_REQ_MSG_ID_V01 0x21 #define TEST_PING_REQ_MAX_MSG_LEN_V01 266 #define TEST_DATA_REQ_MAX_MSG_LEN_V01 8456 struct test_name_type_v01 { u32 name_len; char name[TEST_MAX_NAME_SIZE_V01]; }; static const struct qmi_elem_info test_name_type_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = QMI_COMMON_TLV_TYPE, .offset = offsetof(struct test_name_type_v01, name_len), }, { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = TEST_MAX_NAME_SIZE_V01, .elem_size = sizeof(char), .array_type = VAR_LEN_ARRAY, .tlv_type = QMI_COMMON_TLV_TYPE, .offset = offsetof(struct test_name_type_v01, name), }, {} }; struct test_ping_req_msg_v01 { char ping[4]; u8 client_name_valid; struct test_name_type_v01 client_name; }; static const struct qmi_elem_info test_ping_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 4, .elem_size = sizeof(char), .array_type = STATIC_ARRAY, .tlv_type = PING_REQ1_TLV_TYPE, .offset = offsetof(struct test_ping_req_msg_v01, ping), }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = PING_OPT1_TLV_TYPE, .offset = offsetof(struct test_ping_req_msg_v01, client_name_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct test_name_type_v01), .array_type = NO_ARRAY, .tlv_type = PING_OPT1_TLV_TYPE, .offset = offsetof(struct test_ping_req_msg_v01, client_name), .ei_array = test_name_type_v01_ei, }, {} }; struct test_ping_resp_msg_v01 { struct qmi_response_type_v01 resp; u8 pong_valid; char pong[4]; u8 service_name_valid; struct test_name_type_v01 service_name; }; static const struct qmi_elem_info test_ping_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct qmi_response_type_v01), .array_type = NO_ARRAY, .tlv_type = PING_RESP1_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, resp), .ei_array = qmi_response_type_v01_ei, }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = PING_OPT1_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, pong_valid), }, { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 4, .elem_size = sizeof(char), .array_type = STATIC_ARRAY, .tlv_type = PING_OPT1_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, pong), }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = PING_OPT2_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, service_name_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct test_name_type_v01), .array_type = NO_ARRAY, .tlv_type = PING_OPT2_TLV_TYPE, .offset = offsetof(struct test_ping_resp_msg_v01, service_name), .ei_array = test_name_type_v01_ei, }, {} }; struct test_data_req_msg_v01 { u32 data_len; u8 data[TEST_MED_DATA_SIZE_V01]; u8 client_name_valid; struct test_name_type_v01 client_name; }; static const struct qmi_elem_info test_data_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, .elem_size = sizeof(u32), .array_type = NO_ARRAY, .tlv_type = DATA_REQ1_TLV_TYPE, .offset = offsetof(struct test_data_req_msg_v01, data_len), }, { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = TEST_MED_DATA_SIZE_V01, .elem_size = sizeof(u8), .array_type = VAR_LEN_ARRAY, .tlv_type = DATA_REQ1_TLV_TYPE, .offset = offsetof(struct test_data_req_msg_v01, data), }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_req_msg_v01, client_name_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct test_name_type_v01), .array_type = NO_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_req_msg_v01, client_name), .ei_array = test_name_type_v01_ei, }, {} }; struct test_data_resp_msg_v01 { struct qmi_response_type_v01 resp; u8 data_valid; u32 data_len; u8 data[TEST_MED_DATA_SIZE_V01]; u8 service_name_valid; struct test_name_type_v01 service_name; }; static const struct qmi_elem_info test_data_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct qmi_response_type_v01), .array_type = NO_ARRAY, .tlv_type = DATA_RESP1_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, resp), .ei_array = qmi_response_type_v01_ei, }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, data_valid), }, { .data_type = QMI_DATA_LEN, .elem_len = 1, .elem_size = sizeof(u32), .array_type = NO_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, data_len), }, { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = TEST_MED_DATA_SIZE_V01, .elem_size = sizeof(u8), .array_type = VAR_LEN_ARRAY, .tlv_type = DATA_OPT1_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, data), }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = DATA_OPT2_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, service_name_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct test_name_type_v01), .array_type = NO_ARRAY, .tlv_type = DATA_OPT2_TLV_TYPE, .offset = offsetof(struct test_data_resp_msg_v01, service_name), .ei_array = test_name_type_v01_ei, }, {} }; /* * ping_write() - ping_pong debugfs file write handler * @file: debugfs file context * @user_buf: reference to the user data (ignored) * @count: number of bytes in @user_buf * @ppos: offset in @file to write * * This function allows user space to send out a ping_pong QMI encoded message * to the associated remote test service and will return with the result of the * transaction. It serves as an example of how to provide a custom response * handler. * * Return: @count, or negative errno on failure. */ static ssize_t ping_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct qmi_handle *qmi = file->private_data; struct test_ping_req_msg_v01 req = {}; struct qmi_txn txn; int ret; memcpy(req.ping, "ping", sizeof(req.ping)); ret = qmi_txn_init(qmi, &txn, NULL, NULL); if (ret < 0) return ret; ret = qmi_send_request(qmi, NULL, &txn, TEST_PING_REQ_MSG_ID_V01, TEST_PING_REQ_MAX_MSG_LEN_V01, test_ping_req_msg_v01_ei, &req); if (ret < 0) { qmi_txn_cancel(&txn); return ret; } ret = qmi_txn_wait(&txn, 5 * HZ); if (ret < 0) count = ret; return count; } static const struct file_operations ping_fops = { .open = simple_open, .write = ping_write, }; static void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, struct qmi_txn *txn, const void *data) { const struct test_ping_resp_msg_v01 *resp = data; if (!txn) { pr_err("spurious ping response\n"); return; } if (resp->resp.result == QMI_RESULT_FAILURE_V01) txn->result = -ENXIO; else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4)) txn->result = -EINVAL; complete(&txn->completion); } /* * data_write() - data debugfs file write handler * @file: debugfs file context * @user_buf: reference to the user data * @count: number of bytes in @user_buf * @ppos: offset in @file to write * * This function allows user space to send out a data QMI encoded message to * the associated remote test service and will return with the result of the * transaction. It serves as an example of how to have the QMI helpers decode a * transaction response into a provided object automatically. * * Return: @count, or negative errno on failure. */ static ssize_t data_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct qmi_handle *qmi = file->private_data; struct test_data_resp_msg_v01 *resp; struct test_data_req_msg_v01 *req; struct qmi_txn txn; int ret; req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } req->data_len = min_t(size_t, sizeof(req->data), count); if (copy_from_user(req->data, user_buf, req->data_len)) { ret = -EFAULT; goto out; } ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp); if (ret < 0) goto out; ret = qmi_send_request(qmi, NULL, &txn, TEST_DATA_REQ_MSG_ID_V01, TEST_DATA_REQ_MAX_MSG_LEN_V01, test_data_req_msg_v01_ei, req); if (ret < 0) { qmi_txn_cancel(&txn); goto out; } ret = qmi_txn_wait(&txn, 5 * HZ); if (ret < 0) { goto out; } else if (!resp->data_valid || resp->data_len != req->data_len || memcmp(resp->data, req->data, req->data_len)) { pr_err("response data doesn't match expectation\n"); ret = -EINVAL; goto out; } ret = count; out: kfree(resp); kfree(req); return ret; } static const struct file_operations data_fops = { .open = simple_open, .write = data_write, }; static const struct qmi_msg_handler qmi_sample_handlers[] = { { .type = QMI_RESPONSE, .msg_id = TEST_PING_REQ_MSG_ID_V01, .ei = test_ping_resp_msg_v01_ei, .decoded_size = sizeof(struct test_ping_req_msg_v01), .fn = ping_pong_cb }, {} }; struct qmi_sample { struct qmi_handle qmi; struct dentry *de_dir; struct dentry *de_data; struct dentry *de_ping; }; static struct dentry *qmi_debug_dir; static int qmi_sample_probe(struct platform_device *pdev) { struct sockaddr_qrtr *sq; struct qmi_sample *sample; char path[20]; int ret; sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL); if (!sample) return -ENOMEM; ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01, NULL, qmi_sample_handlers); if (ret < 0) return ret; sq = dev_get_platdata(&pdev->dev); ret = kernel_connect(sample->qmi.sock, (struct sockaddr_unsized *)sq, sizeof(*sq), 0); if (ret < 0) { pr_err("failed to connect to remote service port\n"); goto err_release_qmi_handle; } snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port); sample->de_dir = debugfs_create_dir(path, qmi_debug_dir); if (IS_ERR(sample->de_dir)) { ret = PTR_ERR(sample->de_dir); goto err_release_qmi_handle; } sample->de_data = debugfs_create_file("data", 0600, sample->de_dir, sample, &data_fops); if (IS_ERR(sample->de_data)) { ret = PTR_ERR(sample->de_data); goto err_remove_de_dir; } sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir, sample, &ping_fops); if (IS_ERR(sample->de_ping)) { ret = PTR_ERR(sample->de_ping); goto err_remove_de_data; } platform_set_drvdata(pdev, sample);