162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Sample in-kernel QMI client driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
662306a36Sopenharmony_ci * Copyright (C) 2017 Linaro Ltd.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/debugfs.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/qrtr.h>
1462306a36Sopenharmony_ci#include <linux/net.h>
1562306a36Sopenharmony_ci#include <linux/completion.h>
1662306a36Sopenharmony_ci#include <linux/idr.h>
1762306a36Sopenharmony_ci#include <linux/string.h>
1862306a36Sopenharmony_ci#include <net/sock.h>
1962306a36Sopenharmony_ci#include <linux/soc/qcom/qmi.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define PING_REQ1_TLV_TYPE		0x1
2262306a36Sopenharmony_ci#define PING_RESP1_TLV_TYPE		0x2
2362306a36Sopenharmony_ci#define PING_OPT1_TLV_TYPE		0x10
2462306a36Sopenharmony_ci#define PING_OPT2_TLV_TYPE		0x11
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define DATA_REQ1_TLV_TYPE		0x1
2762306a36Sopenharmony_ci#define DATA_RESP1_TLV_TYPE		0x2
2862306a36Sopenharmony_ci#define DATA_OPT1_TLV_TYPE		0x10
2962306a36Sopenharmony_ci#define DATA_OPT2_TLV_TYPE		0x11
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define TEST_MED_DATA_SIZE_V01		8192
3262306a36Sopenharmony_ci#define TEST_MAX_NAME_SIZE_V01		255
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define TEST_PING_REQ_MSG_ID_V01	0x20
3562306a36Sopenharmony_ci#define TEST_DATA_REQ_MSG_ID_V01	0x21
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define TEST_PING_REQ_MAX_MSG_LEN_V01	266
3862306a36Sopenharmony_ci#define TEST_DATA_REQ_MAX_MSG_LEN_V01	8456
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct test_name_type_v01 {
4162306a36Sopenharmony_ci	u32 name_len;
4262306a36Sopenharmony_ci	char name[TEST_MAX_NAME_SIZE_V01];
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic const struct qmi_elem_info test_name_type_v01_ei[] = {
4662306a36Sopenharmony_ci	{
4762306a36Sopenharmony_ci		.data_type	= QMI_DATA_LEN,
4862306a36Sopenharmony_ci		.elem_len	= 1,
4962306a36Sopenharmony_ci		.elem_size	= sizeof(u8),
5062306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
5162306a36Sopenharmony_ci		.tlv_type	= QMI_COMMON_TLV_TYPE,
5262306a36Sopenharmony_ci		.offset		= offsetof(struct test_name_type_v01,
5362306a36Sopenharmony_ci					   name_len),
5462306a36Sopenharmony_ci	},
5562306a36Sopenharmony_ci	{
5662306a36Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
5762306a36Sopenharmony_ci		.elem_len	= TEST_MAX_NAME_SIZE_V01,
5862306a36Sopenharmony_ci		.elem_size	= sizeof(char),
5962306a36Sopenharmony_ci		.array_type	= VAR_LEN_ARRAY,
6062306a36Sopenharmony_ci		.tlv_type	= QMI_COMMON_TLV_TYPE,
6162306a36Sopenharmony_ci		.offset		= offsetof(struct test_name_type_v01,
6262306a36Sopenharmony_ci					   name),
6362306a36Sopenharmony_ci	},
6462306a36Sopenharmony_ci	{}
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistruct test_ping_req_msg_v01 {
6862306a36Sopenharmony_ci	char ping[4];
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	u8 client_name_valid;
7162306a36Sopenharmony_ci	struct test_name_type_v01 client_name;
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic const struct qmi_elem_info test_ping_req_msg_v01_ei[] = {
7562306a36Sopenharmony_ci	{
7662306a36Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
7762306a36Sopenharmony_ci		.elem_len	= 4,
7862306a36Sopenharmony_ci		.elem_size	= sizeof(char),
7962306a36Sopenharmony_ci		.array_type	= STATIC_ARRAY,
8062306a36Sopenharmony_ci		.tlv_type	= PING_REQ1_TLV_TYPE,
8162306a36Sopenharmony_ci		.offset		= offsetof(struct test_ping_req_msg_v01,
8262306a36Sopenharmony_ci					   ping),
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	{
8562306a36Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
8662306a36Sopenharmony_ci		.elem_len	= 1,
8762306a36Sopenharmony_ci		.elem_size	= sizeof(u8),
8862306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
8962306a36Sopenharmony_ci		.tlv_type	= PING_OPT1_TLV_TYPE,
9062306a36Sopenharmony_ci		.offset		= offsetof(struct test_ping_req_msg_v01,
9162306a36Sopenharmony_ci					   client_name_valid),
9262306a36Sopenharmony_ci	},
9362306a36Sopenharmony_ci	{
9462306a36Sopenharmony_ci		.data_type	= QMI_STRUCT,
9562306a36Sopenharmony_ci		.elem_len	= 1,
9662306a36Sopenharmony_ci		.elem_size	= sizeof(struct test_name_type_v01),
9762306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
9862306a36Sopenharmony_ci		.tlv_type	= PING_OPT1_TLV_TYPE,
9962306a36Sopenharmony_ci		.offset		= offsetof(struct test_ping_req_msg_v01,
10062306a36Sopenharmony_ci					   client_name),
10162306a36Sopenharmony_ci		.ei_array	= test_name_type_v01_ei,
10262306a36Sopenharmony_ci	},
10362306a36Sopenharmony_ci	{}
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistruct test_ping_resp_msg_v01 {
10762306a36Sopenharmony_ci	struct qmi_response_type_v01 resp;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	u8 pong_valid;
11062306a36Sopenharmony_ci	char pong[4];
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	u8 service_name_valid;
11362306a36Sopenharmony_ci	struct test_name_type_v01 service_name;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic const struct qmi_elem_info test_ping_resp_msg_v01_ei[] = {
11762306a36Sopenharmony_ci	{
11862306a36Sopenharmony_ci		.data_type	= QMI_STRUCT,
11962306a36Sopenharmony_ci		.elem_len	= 1,
12062306a36Sopenharmony_ci		.elem_size	= sizeof(struct qmi_response_type_v01),
12162306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
12262306a36Sopenharmony_ci		.tlv_type	= PING_RESP1_TLV_TYPE,
12362306a36Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
12462306a36Sopenharmony_ci					   resp),
12562306a36Sopenharmony_ci		.ei_array	= qmi_response_type_v01_ei,
12662306a36Sopenharmony_ci	},
12762306a36Sopenharmony_ci	{
12862306a36Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
12962306a36Sopenharmony_ci		.elem_len	= 1,
13062306a36Sopenharmony_ci		.elem_size	= sizeof(u8),
13162306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
13262306a36Sopenharmony_ci		.tlv_type	= PING_OPT1_TLV_TYPE,
13362306a36Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
13462306a36Sopenharmony_ci					   pong_valid),
13562306a36Sopenharmony_ci	},
13662306a36Sopenharmony_ci	{
13762306a36Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
13862306a36Sopenharmony_ci		.elem_len	= 4,
13962306a36Sopenharmony_ci		.elem_size	= sizeof(char),
14062306a36Sopenharmony_ci		.array_type	= STATIC_ARRAY,
14162306a36Sopenharmony_ci		.tlv_type	= PING_OPT1_TLV_TYPE,
14262306a36Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
14362306a36Sopenharmony_ci					   pong),
14462306a36Sopenharmony_ci	},
14562306a36Sopenharmony_ci	{
14662306a36Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
14762306a36Sopenharmony_ci		.elem_len	= 1,
14862306a36Sopenharmony_ci		.elem_size	= sizeof(u8),
14962306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
15062306a36Sopenharmony_ci		.tlv_type	= PING_OPT2_TLV_TYPE,
15162306a36Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
15262306a36Sopenharmony_ci					   service_name_valid),
15362306a36Sopenharmony_ci	},
15462306a36Sopenharmony_ci	{
15562306a36Sopenharmony_ci		.data_type	= QMI_STRUCT,
15662306a36Sopenharmony_ci		.elem_len	= 1,
15762306a36Sopenharmony_ci		.elem_size	= sizeof(struct test_name_type_v01),
15862306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
15962306a36Sopenharmony_ci		.tlv_type	= PING_OPT2_TLV_TYPE,
16062306a36Sopenharmony_ci		.offset		= offsetof(struct test_ping_resp_msg_v01,
16162306a36Sopenharmony_ci					   service_name),
16262306a36Sopenharmony_ci		.ei_array	= test_name_type_v01_ei,
16362306a36Sopenharmony_ci	},
16462306a36Sopenharmony_ci	{}
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistruct test_data_req_msg_v01 {
16862306a36Sopenharmony_ci	u32 data_len;
16962306a36Sopenharmony_ci	u8 data[TEST_MED_DATA_SIZE_V01];
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	u8 client_name_valid;
17262306a36Sopenharmony_ci	struct test_name_type_v01 client_name;
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic const struct qmi_elem_info test_data_req_msg_v01_ei[] = {
17662306a36Sopenharmony_ci	{
17762306a36Sopenharmony_ci		.data_type	= QMI_DATA_LEN,
17862306a36Sopenharmony_ci		.elem_len	= 1,
17962306a36Sopenharmony_ci		.elem_size	= sizeof(u32),
18062306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
18162306a36Sopenharmony_ci		.tlv_type	= DATA_REQ1_TLV_TYPE,
18262306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_req_msg_v01,
18362306a36Sopenharmony_ci					   data_len),
18462306a36Sopenharmony_ci	},
18562306a36Sopenharmony_ci	{
18662306a36Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
18762306a36Sopenharmony_ci		.elem_len	= TEST_MED_DATA_SIZE_V01,
18862306a36Sopenharmony_ci		.elem_size	= sizeof(u8),
18962306a36Sopenharmony_ci		.array_type	= VAR_LEN_ARRAY,
19062306a36Sopenharmony_ci		.tlv_type	= DATA_REQ1_TLV_TYPE,
19162306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_req_msg_v01,
19262306a36Sopenharmony_ci					   data),
19362306a36Sopenharmony_ci	},
19462306a36Sopenharmony_ci	{
19562306a36Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
19662306a36Sopenharmony_ci		.elem_len	= 1,
19762306a36Sopenharmony_ci		.elem_size	= sizeof(u8),
19862306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
19962306a36Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
20062306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_req_msg_v01,
20162306a36Sopenharmony_ci					   client_name_valid),
20262306a36Sopenharmony_ci	},
20362306a36Sopenharmony_ci	{
20462306a36Sopenharmony_ci		.data_type	= QMI_STRUCT,
20562306a36Sopenharmony_ci		.elem_len	= 1,
20662306a36Sopenharmony_ci		.elem_size	= sizeof(struct test_name_type_v01),
20762306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
20862306a36Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
20962306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_req_msg_v01,
21062306a36Sopenharmony_ci					   client_name),
21162306a36Sopenharmony_ci		.ei_array	= test_name_type_v01_ei,
21262306a36Sopenharmony_ci	},
21362306a36Sopenharmony_ci	{}
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistruct test_data_resp_msg_v01 {
21762306a36Sopenharmony_ci	struct qmi_response_type_v01 resp;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	u8 data_valid;
22062306a36Sopenharmony_ci	u32 data_len;
22162306a36Sopenharmony_ci	u8 data[TEST_MED_DATA_SIZE_V01];
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	u8 service_name_valid;
22462306a36Sopenharmony_ci	struct test_name_type_v01 service_name;
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic const struct qmi_elem_info test_data_resp_msg_v01_ei[] = {
22862306a36Sopenharmony_ci	{
22962306a36Sopenharmony_ci		.data_type	= QMI_STRUCT,
23062306a36Sopenharmony_ci		.elem_len	= 1,
23162306a36Sopenharmony_ci		.elem_size	= sizeof(struct qmi_response_type_v01),
23262306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
23362306a36Sopenharmony_ci		.tlv_type	= DATA_RESP1_TLV_TYPE,
23462306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
23562306a36Sopenharmony_ci					   resp),
23662306a36Sopenharmony_ci		.ei_array	= qmi_response_type_v01_ei,
23762306a36Sopenharmony_ci	},
23862306a36Sopenharmony_ci	{
23962306a36Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
24062306a36Sopenharmony_ci		.elem_len	= 1,
24162306a36Sopenharmony_ci		.elem_size	= sizeof(u8),
24262306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
24362306a36Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
24462306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
24562306a36Sopenharmony_ci					   data_valid),
24662306a36Sopenharmony_ci	},
24762306a36Sopenharmony_ci	{
24862306a36Sopenharmony_ci		.data_type	= QMI_DATA_LEN,
24962306a36Sopenharmony_ci		.elem_len	= 1,
25062306a36Sopenharmony_ci		.elem_size	= sizeof(u32),
25162306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
25262306a36Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
25362306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
25462306a36Sopenharmony_ci					   data_len),
25562306a36Sopenharmony_ci	},
25662306a36Sopenharmony_ci	{
25762306a36Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
25862306a36Sopenharmony_ci		.elem_len	= TEST_MED_DATA_SIZE_V01,
25962306a36Sopenharmony_ci		.elem_size	= sizeof(u8),
26062306a36Sopenharmony_ci		.array_type	= VAR_LEN_ARRAY,
26162306a36Sopenharmony_ci		.tlv_type	= DATA_OPT1_TLV_TYPE,
26262306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
26362306a36Sopenharmony_ci					   data),
26462306a36Sopenharmony_ci	},
26562306a36Sopenharmony_ci	{
26662306a36Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
26762306a36Sopenharmony_ci		.elem_len	= 1,
26862306a36Sopenharmony_ci		.elem_size	= sizeof(u8),
26962306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
27062306a36Sopenharmony_ci		.tlv_type	= DATA_OPT2_TLV_TYPE,
27162306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
27262306a36Sopenharmony_ci					   service_name_valid),
27362306a36Sopenharmony_ci	},
27462306a36Sopenharmony_ci	{
27562306a36Sopenharmony_ci		.data_type	= QMI_STRUCT,
27662306a36Sopenharmony_ci		.elem_len	= 1,
27762306a36Sopenharmony_ci		.elem_size	= sizeof(struct test_name_type_v01),
27862306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
27962306a36Sopenharmony_ci		.tlv_type	= DATA_OPT2_TLV_TYPE,
28062306a36Sopenharmony_ci		.offset		= offsetof(struct test_data_resp_msg_v01,
28162306a36Sopenharmony_ci					   service_name),
28262306a36Sopenharmony_ci		.ei_array	= test_name_type_v01_ei,
28362306a36Sopenharmony_ci	},
28462306a36Sopenharmony_ci	{}
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/*
28862306a36Sopenharmony_ci * ping_write() - ping_pong debugfs file write handler
28962306a36Sopenharmony_ci * @file:	debugfs file context
29062306a36Sopenharmony_ci * @user_buf:	reference to the user data (ignored)
29162306a36Sopenharmony_ci * @count:	number of bytes in @user_buf
29262306a36Sopenharmony_ci * @ppos:	offset in @file to write
29362306a36Sopenharmony_ci *
29462306a36Sopenharmony_ci * This function allows user space to send out a ping_pong QMI encoded message
29562306a36Sopenharmony_ci * to the associated remote test service and will return with the result of the
29662306a36Sopenharmony_ci * transaction. It serves as an example of how to provide a custom response
29762306a36Sopenharmony_ci * handler.
29862306a36Sopenharmony_ci *
29962306a36Sopenharmony_ci * Return: @count, or negative errno on failure.
30062306a36Sopenharmony_ci */
30162306a36Sopenharmony_cistatic ssize_t ping_write(struct file *file, const char __user *user_buf,
30262306a36Sopenharmony_ci			  size_t count, loff_t *ppos)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct qmi_handle *qmi = file->private_data;
30562306a36Sopenharmony_ci	struct test_ping_req_msg_v01 req = {};
30662306a36Sopenharmony_ci	struct qmi_txn txn;
30762306a36Sopenharmony_ci	int ret;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	memcpy(req.ping, "ping", sizeof(req.ping));
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ret = qmi_txn_init(qmi, &txn, NULL, NULL);
31262306a36Sopenharmony_ci	if (ret < 0)
31362306a36Sopenharmony_ci		return ret;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	ret = qmi_send_request(qmi, NULL, &txn,
31662306a36Sopenharmony_ci			       TEST_PING_REQ_MSG_ID_V01,
31762306a36Sopenharmony_ci			       TEST_PING_REQ_MAX_MSG_LEN_V01,
31862306a36Sopenharmony_ci			       test_ping_req_msg_v01_ei, &req);
31962306a36Sopenharmony_ci	if (ret < 0) {
32062306a36Sopenharmony_ci		qmi_txn_cancel(&txn);
32162306a36Sopenharmony_ci		return ret;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
32562306a36Sopenharmony_ci	if (ret < 0)
32662306a36Sopenharmony_ci		count = ret;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return count;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic const struct file_operations ping_fops = {
33262306a36Sopenharmony_ci	.open = simple_open,
33362306a36Sopenharmony_ci	.write = ping_write,
33462306a36Sopenharmony_ci};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
33762306a36Sopenharmony_ci			 struct qmi_txn *txn, const void *data)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	const struct test_ping_resp_msg_v01 *resp = data;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (!txn) {
34262306a36Sopenharmony_ci		pr_err("spurious ping response\n");
34362306a36Sopenharmony_ci		return;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (resp->resp.result == QMI_RESULT_FAILURE_V01)
34762306a36Sopenharmony_ci		txn->result = -ENXIO;
34862306a36Sopenharmony_ci	else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4))
34962306a36Sopenharmony_ci		txn->result = -EINVAL;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	complete(&txn->completion);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/*
35562306a36Sopenharmony_ci * data_write() - data debugfs file write handler
35662306a36Sopenharmony_ci * @file:	debugfs file context
35762306a36Sopenharmony_ci * @user_buf:	reference to the user data
35862306a36Sopenharmony_ci * @count:	number of bytes in @user_buf
35962306a36Sopenharmony_ci * @ppos:	offset in @file to write
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * This function allows user space to send out a data QMI encoded message to
36262306a36Sopenharmony_ci * the associated remote test service and will return with the result of the
36362306a36Sopenharmony_ci * transaction. It serves as an example of how to have the QMI helpers decode a
36462306a36Sopenharmony_ci * transaction response into a provided object automatically.
36562306a36Sopenharmony_ci *
36662306a36Sopenharmony_ci * Return: @count, or negative errno on failure.
36762306a36Sopenharmony_ci */
36862306a36Sopenharmony_cistatic ssize_t data_write(struct file *file, const char __user *user_buf,
36962306a36Sopenharmony_ci			  size_t count, loff_t *ppos)
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct qmi_handle *qmi = file->private_data;
37362306a36Sopenharmony_ci	struct test_data_resp_msg_v01 *resp;
37462306a36Sopenharmony_ci	struct test_data_req_msg_v01 *req;
37562306a36Sopenharmony_ci	struct qmi_txn txn;
37662306a36Sopenharmony_ci	int ret;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
37962306a36Sopenharmony_ci	if (!req)
38062306a36Sopenharmony_ci		return -ENOMEM;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
38362306a36Sopenharmony_ci	if (!resp) {
38462306a36Sopenharmony_ci		kfree(req);
38562306a36Sopenharmony_ci		return -ENOMEM;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	req->data_len = min_t(size_t, sizeof(req->data), count);
38962306a36Sopenharmony_ci	if (copy_from_user(req->data, user_buf, req->data_len)) {
39062306a36Sopenharmony_ci		ret = -EFAULT;
39162306a36Sopenharmony_ci		goto out;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp);
39562306a36Sopenharmony_ci	if (ret < 0)
39662306a36Sopenharmony_ci		goto out;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	ret = qmi_send_request(qmi, NULL, &txn,
39962306a36Sopenharmony_ci			       TEST_DATA_REQ_MSG_ID_V01,
40062306a36Sopenharmony_ci			       TEST_DATA_REQ_MAX_MSG_LEN_V01,
40162306a36Sopenharmony_ci			       test_data_req_msg_v01_ei, req);
40262306a36Sopenharmony_ci	if (ret < 0) {
40362306a36Sopenharmony_ci		qmi_txn_cancel(&txn);
40462306a36Sopenharmony_ci		goto out;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
40862306a36Sopenharmony_ci	if (ret < 0) {
40962306a36Sopenharmony_ci		goto out;
41062306a36Sopenharmony_ci	} else if (!resp->data_valid ||
41162306a36Sopenharmony_ci		   resp->data_len != req->data_len ||
41262306a36Sopenharmony_ci		   memcmp(resp->data, req->data, req->data_len)) {
41362306a36Sopenharmony_ci		pr_err("response data doesn't match expectation\n");
41462306a36Sopenharmony_ci		ret = -EINVAL;
41562306a36Sopenharmony_ci		goto out;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	ret = count;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ciout:
42162306a36Sopenharmony_ci	kfree(resp);
42262306a36Sopenharmony_ci	kfree(req);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return ret;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic const struct file_operations data_fops = {
42862306a36Sopenharmony_ci	.open = simple_open,
42962306a36Sopenharmony_ci	.write = data_write,
43062306a36Sopenharmony_ci};
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic const struct qmi_msg_handler qmi_sample_handlers[] = {
43362306a36Sopenharmony_ci	{
43462306a36Sopenharmony_ci		.type = QMI_RESPONSE,
43562306a36Sopenharmony_ci		.msg_id = TEST_PING_REQ_MSG_ID_V01,
43662306a36Sopenharmony_ci		.ei = test_ping_resp_msg_v01_ei,
43762306a36Sopenharmony_ci		.decoded_size = sizeof(struct test_ping_req_msg_v01),
43862306a36Sopenharmony_ci		.fn = ping_pong_cb
43962306a36Sopenharmony_ci	},
44062306a36Sopenharmony_ci	{}
44162306a36Sopenharmony_ci};
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistruct qmi_sample {
44462306a36Sopenharmony_ci	struct qmi_handle qmi;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	struct dentry *de_dir;
44762306a36Sopenharmony_ci	struct dentry *de_data;
44862306a36Sopenharmony_ci	struct dentry *de_ping;
44962306a36Sopenharmony_ci};
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic struct dentry *qmi_debug_dir;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic int qmi_sample_probe(struct platform_device *pdev)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct sockaddr_qrtr *sq;
45662306a36Sopenharmony_ci	struct qmi_sample *sample;
45762306a36Sopenharmony_ci	char path[20];
45862306a36Sopenharmony_ci	int ret;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL);
46162306a36Sopenharmony_ci	if (!sample)
46262306a36Sopenharmony_ci		return -ENOMEM;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01,
46562306a36Sopenharmony_ci			      NULL,
46662306a36Sopenharmony_ci			      qmi_sample_handlers);
46762306a36Sopenharmony_ci	if (ret < 0)
46862306a36Sopenharmony_ci		return ret;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	sq = dev_get_platdata(&pdev->dev);
47162306a36Sopenharmony_ci	ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq,
47262306a36Sopenharmony_ci			     sizeof(*sq), 0);
47362306a36Sopenharmony_ci	if (ret < 0) {
47462306a36Sopenharmony_ci		pr_err("failed to connect to remote service port\n");
47562306a36Sopenharmony_ci		goto err_release_qmi_handle;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	sample->de_dir = debugfs_create_dir(path, qmi_debug_dir);
48162306a36Sopenharmony_ci	if (IS_ERR(sample->de_dir)) {
48262306a36Sopenharmony_ci		ret = PTR_ERR(sample->de_dir);
48362306a36Sopenharmony_ci		goto err_release_qmi_handle;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	sample->de_data = debugfs_create_file("data", 0600, sample->de_dir,
48762306a36Sopenharmony_ci					      sample, &data_fops);
48862306a36Sopenharmony_ci	if (IS_ERR(sample->de_data)) {
48962306a36Sopenharmony_ci		ret = PTR_ERR(sample->de_data);
49062306a36Sopenharmony_ci		goto err_remove_de_dir;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir,
49462306a36Sopenharmony_ci					      sample, &ping_fops);
49562306a36Sopenharmony_ci	if (IS_ERR(sample->de_ping)) {
49662306a36Sopenharmony_ci		ret = PTR_ERR(sample->de_ping);
49762306a36Sopenharmony_ci		goto err_remove_de_data;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	platform_set_drvdata(pdev, sample);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cierr_remove_de_data:
50562306a36Sopenharmony_ci	debugfs_remove(sample->de_data);
50662306a36Sopenharmony_cierr_remove_de_dir:
50762306a36Sopenharmony_ci	debugfs_remove(sample->de_dir);
50862306a36Sopenharmony_cierr_release_qmi_handle:
50962306a36Sopenharmony_ci	qmi_handle_release(&sample->qmi);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return ret;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int qmi_sample_remove(struct platform_device *pdev)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct qmi_sample *sample = platform_get_drvdata(pdev);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	debugfs_remove(sample->de_ping);
51962306a36Sopenharmony_ci	debugfs_remove(sample->de_data);
52062306a36Sopenharmony_ci	debugfs_remove(sample->de_dir);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	qmi_handle_release(&sample->qmi);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return 0;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic struct platform_driver qmi_sample_driver = {
52862306a36Sopenharmony_ci	.probe = qmi_sample_probe,
52962306a36Sopenharmony_ci	.remove = qmi_sample_remove,
53062306a36Sopenharmony_ci	.driver = {
53162306a36Sopenharmony_ci		.name = "qmi_sample_client",
53262306a36Sopenharmony_ci	},
53362306a36Sopenharmony_ci};
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int qmi_sample_new_server(struct qmi_handle *qmi,
53662306a36Sopenharmony_ci				 struct qmi_service *service)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct platform_device *pdev;
53962306a36Sopenharmony_ci	struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port };
54062306a36Sopenharmony_ci	int ret;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	pdev = platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO);
54362306a36Sopenharmony_ci	if (!pdev)
54462306a36Sopenharmony_ci		return -ENOMEM;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	ret = platform_device_add_data(pdev, &sq, sizeof(sq));
54762306a36Sopenharmony_ci	if (ret)
54862306a36Sopenharmony_ci		goto err_put_device;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	ret = platform_device_add(pdev);
55162306a36Sopenharmony_ci	if (ret)
55262306a36Sopenharmony_ci		goto err_put_device;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	service->priv = pdev;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cierr_put_device:
55962306a36Sopenharmony_ci	platform_device_put(pdev);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	return ret;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic void qmi_sample_del_server(struct qmi_handle *qmi,
56562306a36Sopenharmony_ci				  struct qmi_service *service)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct platform_device *pdev = service->priv;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	platform_device_unregister(pdev);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic struct qmi_handle lookup_client;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic const struct qmi_ops lookup_ops = {
57562306a36Sopenharmony_ci	.new_server = qmi_sample_new_server,
57662306a36Sopenharmony_ci	.del_server = qmi_sample_del_server,
57762306a36Sopenharmony_ci};
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic int qmi_sample_init(void)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	int ret;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	qmi_debug_dir = debugfs_create_dir("qmi_sample", NULL);
58462306a36Sopenharmony_ci	if (IS_ERR(qmi_debug_dir)) {
58562306a36Sopenharmony_ci		pr_err("failed to create qmi_sample dir\n");
58662306a36Sopenharmony_ci		return PTR_ERR(qmi_debug_dir);
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	ret = platform_driver_register(&qmi_sample_driver);
59062306a36Sopenharmony_ci	if (ret)
59162306a36Sopenharmony_ci		goto err_remove_debug_dir;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	ret = qmi_handle_init(&lookup_client, 0, &lookup_ops, NULL);
59462306a36Sopenharmony_ci	if (ret < 0)
59562306a36Sopenharmony_ci		goto err_unregister_driver;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	qmi_add_lookup(&lookup_client, 15, 0, 0);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	return 0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cierr_unregister_driver:
60262306a36Sopenharmony_ci	platform_driver_unregister(&qmi_sample_driver);
60362306a36Sopenharmony_cierr_remove_debug_dir:
60462306a36Sopenharmony_ci	debugfs_remove(qmi_debug_dir);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return ret;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic void qmi_sample_exit(void)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	qmi_handle_release(&lookup_client);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	platform_driver_unregister(&qmi_sample_driver);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	debugfs_remove(qmi_debug_dir);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cimodule_init(qmi_sample_init);
61962306a36Sopenharmony_cimodule_exit(qmi_sample_exit);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ciMODULE_DESCRIPTION("Sample QMI client driver");
62262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
623