620 lines · 14.2 KB · C
RawDownload
// 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t