From patchwork Fri Aug 4 14:59:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bjorn Andersson X-Patchwork-Id: 109424 Delivered-To: patch@linaro.org Received: by 10.140.101.6 with SMTP id t6csp2265977qge; Fri, 4 Aug 2017 08:00:03 -0700 (PDT) X-Received: by 10.99.98.134 with SMTP id w128mr2726283pgb.362.1501858803299; Fri, 04 Aug 2017 08:00:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1501858803; cv=none; d=google.com; s=arc-20160816; b=kAomk76DHEZNfjHvMXR2WGKjeVOsbWv7FPMeEBiBDy8vUGP50t+sdZ2yK7+dv27u/E orCggqgBuqJFJo5oGUzwbQjFoZ3OocRPYMnW+BX6xBWvy29t8+rdX+0KSw9OWF1nnWV2 ZJ1geKQi4NJLLfvCMLJASjFUEGtWBGlI/gaj8/xv1v+gX90WWEFxSe8Pv6k6s1SsXYyk 9fNvq933VK+rPvO2x+kRw+ZbroS55PI2WJ3OAKVBnk0SSu2T3AVbwrnDEKIvtFEd3Rc8 CGZ1hiSwqdySCB6aWWjM+LanZSbXA12zjCVsELFq+diO7j7mrxGvrJ5/qKNgLuAlk6wt ALfQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=fexBw+S3Pevi/ef/6AzJBa4FKtJumb11JuDItp3Y0jo=; b=kgmUAcPOkh4Q9j8RjuE8jC+txgJmKNxaawW2A/+z6LA6GzbBrHdMIW4sc/gT+K5Ooq 2bKPRNwLw/nK5D8SNeR0PoHi0+eVE8398c8jA6QP4CmGeef4QwVX/UqG8izt/IcVzRKX +kEV1PW4hkLbXRQ+rwCMbzCdpTjA0DWC1ud1Rj4oQ9WIJuO5TuQPGuoCSjsjbFeEhWP+ ft5jJIJ+XC/YQF8OU4JS/6/PZWkFY8wPcVUxd/+INnqzasIqX8m34IO+82Ec+//YGOQs Ls+3KjU9QQ0hIK2cPI1aQvJyBBiEvDwr3hulsN+f/fe044Z4Y7AyJi2IIMG7DelZpH85 11dw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=aNSveqaY; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id a3si1208004pld.1014.2017.08.04.08.00.02; Fri, 04 Aug 2017 08:00:03 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=aNSveqaY; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752956AbdHDPAA (ORCPT + 25 others); Fri, 4 Aug 2017 11:00:00 -0400 Received: from mail-pf0-f169.google.com ([209.85.192.169]:33745 "EHLO mail-pf0-f169.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752857AbdHDO7v (ORCPT ); Fri, 4 Aug 2017 10:59:51 -0400 Received: by mail-pf0-f169.google.com with SMTP id d67so8967346pfc.0 for ; Fri, 04 Aug 2017 07:59:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=fexBw+S3Pevi/ef/6AzJBa4FKtJumb11JuDItp3Y0jo=; b=aNSveqaYSOCTGOfnpQjuoPyBOY0q7ar4eCVBeUI+kJjmGUde48fiIrNJabjamr7uHK VR+y073b98Ri6Nrz6ub4kPKGC4/JtKFlPDxwBCba3fX5gbYmrvyHDGc4n0g+GsElzV4G wnbT3QvPcobkcxpzaD/bUPUXksPwTHUF47RbY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=fexBw+S3Pevi/ef/6AzJBa4FKtJumb11JuDItp3Y0jo=; b=sdI3l2qNWOG98uzydB7MXwwqauArdx2sefscj00uYHgFu9ZoJWYzIPr91LB4FFORY7 8M9KsghTYVJur4fHyMYA7gLmgj2wYoPtC6eXxhiFopxMEmWSApDCO9DM6gW9lj2ZKDig Wf2OfqiwpzuYWYtE+Xt93C1SanOWQbJQYrQ23v11Qw1rVRulr5XBi6K8njO/TFazlyQK VoFfyLZlbCGiXxvRDyUJpqZ0zXbn8iocH16rdojvgcwxoyIlOLjEgybORURVfxuXJ8qb 9WYpD0GsxqaDebwViHO4IaggEnN147AQR1gDnz+tAKfw5OgCuZTxNz6/fthijVsAq7IJ zr8w== X-Gm-Message-State: AIVw11377YGYfhTjUfK0xF4XmopwUvDDPi53nufIbfOyA6QNENBf4oqH IGUuDbhZtBjIRQGK X-Received: by 10.84.132.78 with SMTP id 72mr3079619ple.66.1501858791096; Fri, 04 Aug 2017 07:59:51 -0700 (PDT) Received: from localhost.localdomain (ip68-111-217-79.sd.sd.cox.net. [68.111.217.79]) by smtp.gmail.com with ESMTPSA id q24sm3584283pgc.61.2017.08.04.07.59.49 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 04 Aug 2017 07:59:50 -0700 (PDT) From: Bjorn Andersson To: "David S. Miller" , Andy Gross , David Brown Cc: linux-arm-msm@vger.kernel.org, linux-soc@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 6/6] samples: Introduce Qualcomm QRTR sample client Date: Fri, 4 Aug 2017 07:59:38 -0700 Message-Id: <20170804145938.25427-7-bjorn.andersson@linaro.org> X-Mailer: git-send-email 2.12.0 In-Reply-To: <20170804145938.25427-1-bjorn.andersson@linaro.org> References: <20170804145938.25427-1-bjorn.andersson@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Introduce a sample driver that register for server notifications and spawn clients for each available test service (service 15). The spawned clients implements the interface for encoding "ping" and "data" requests and decode the responses from the remote. Signed-off-by: Bjorn Andersson --- samples/Kconfig | 8 + samples/Makefile | 2 +- samples/qrtr/Makefile | 1 + samples/qrtr/qrtr_sample_client.c | 603 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 613 insertions(+), 1 deletion(-) create mode 100644 samples/qrtr/Makefile create mode 100644 samples/qrtr/qrtr_sample_client.c -- 2.12.0 diff --git a/samples/Kconfig b/samples/Kconfig index 9cb63188d3ef..18796928ab58 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -62,6 +62,14 @@ config SAMPLE_KDB Build an example of how to dynamically add the hello command to the kdb shell. +config SAMPLE_QRTR_CLIENT + tristate "Build qrtr client sample -- loadable modules only" + depends on m + select QCOM_QMI_HELPERS + help + Build an qrtr client sample driver, which demonstrates how to + communicate with a remote QRTR service, using QMI encoded messages. + config SAMPLE_RPMSG_CLIENT tristate "Build rpmsg client sample -- loadable modules only" depends on RPMSG && m diff --git a/samples/Makefile b/samples/Makefile index db54e766ddb1..4bf64276860c 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \ configfs/ connector/ v4l/ trace_printk/ blackfin/ \ - vfio-mdev/ statx/ + vfio-mdev/ statx/ qrtr/ diff --git a/samples/qrtr/Makefile b/samples/qrtr/Makefile new file mode 100644 index 000000000000..3f2c2cfdf2e7 --- /dev/null +++ b/samples/qrtr/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SAMPLE_QRTR_CLIENT) += qrtr_sample_client.o diff --git a/samples/qrtr/qrtr_sample_client.c b/samples/qrtr/qrtr_sample_client.c new file mode 100644 index 000000000000..ccb359de4340 --- /dev/null +++ b/samples/qrtr/qrtr_sample_client.c @@ -0,0 +1,603 @@ +/* + * Sample QRTR client driver + * + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Copyright (C) 2017 Linaro Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 { + uint32_t name_len; + char name[TEST_MAX_NAME_SIZE_V01]; +}; + +static struct qmi_elem_info test_name_type_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = 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), + .is_array = 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]; + + uint8_t client_name_valid; + struct test_name_type_v01 client_name; +}; + +struct qmi_elem_info test_ping_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 4, + .elem_size = sizeof(char), + .is_array = 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(uint8_t), + .is_array = 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), + .is_array = 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; + + uint8_t pong_valid; + char pong[4]; + + uint8_t service_name_valid; + struct test_name_type_v01 service_name; +}; + +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), + .is_array = 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(uint8_t), + .is_array = 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), + .is_array = 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(uint8_t), + .is_array = 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), + .is_array = 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 { + uint32_t data_len; + uint8_t data[TEST_MED_DATA_SIZE_V01]; + + uint8_t client_name_valid; + struct test_name_type_v01 client_name; +}; + +struct qmi_elem_info test_data_req_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = 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(uint8_t), + .is_array = 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(uint8_t), + .is_array = 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), + .is_array = 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; + + uint8_t data_valid; + uint32_t data_len; + uint8_t data[TEST_MED_DATA_SIZE_V01]; + + uint8_t service_name_valid; + struct test_name_type_v01 service_name; +}; + +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), + .is_array = 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(uint8_t), + .is_array = 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(uint32_t), + .is_array = 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(uint8_t), + .is_array = 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(uint8_t), + .is_array = 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), + .is_array = 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_pong_store() - ping_pong attribute store handler + * @dev: sample device context + * @attr: the ping_pong attribute + * @buf: write buffer + * @count: length of @buf + * + * Returns @count, or negative errno on failure. + * + * 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. + */ +static ssize_t ping_pong_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qmi_handle *qmi = dev_get_drvdata(dev); + struct test_ping_req_msg_v01 req = {0}; + 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_message(qmi, NULL, &txn, + QMI_REQUEST, + 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 DEVICE_ATTR_WO(ping_pong); + +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_store() - data attribute store handler + * @dev: sample device context + * @attr: the data attribute + * @buf: buffer with message to encode + * @count: length of @buf + * + * Returns @count, or negative errno on failure. + * + * 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. + */ +static ssize_t data_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qmi_handle *qmi = dev_get_drvdata(dev); + 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); + memcpy(req->data, buf, req->data_len); + + ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp); + if (ret < 0) { + count = ret; + goto out; + } + + ret = qmi_send_message(qmi, NULL, &txn, + QMI_REQUEST, + 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); + count = ret; + goto out; + } + + ret = qmi_txn_wait(&txn, 5 * HZ); + if (ret < 0) { + count = ret; + } 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"); + count = -EINVAL; + } + +out: + kfree(resp); + kfree(req); + + return count; +} +static DEVICE_ATTR_WO(data); + +static struct attribute *qrtr_dev_attrs[] = { + &dev_attr_ping_pong.attr, + &dev_attr_data.attr, + NULL +}; +ATTRIBUTE_GROUPS(qrtr_dev); + +static struct qmi_msg_handler qrtr_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 + }, + {} +}; + +static int qrtr_sample_probe(struct platform_device *pdev) +{ + struct qrtr_handle *qrtr; + struct qmi_handle *qmi; + struct sockaddr_qrtr *sq; + int ret; + + qmi = devm_kzalloc(&pdev->dev, sizeof(*qmi), GFP_KERNEL); + if (!qmi) + return -ENOMEM; + + qrtr = &qmi->qrtr; + + ret = qmi_client_init(qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01, + qrtr_sample_handlers); + if (ret < 0) + return ret; + + sq = dev_get_platdata(&pdev->dev); + ret = kernel_connect(qrtr->sock, (struct sockaddr *)sq, + sizeof(*sq), 0); + if (ret < 0) { + pr_err("failed to connect to remote service port\n"); + qmi_client_release(qmi); + return ret; + } + + platform_set_drvdata(pdev, qmi); + + return 0; +} + +static int qrtr_sample_remove(struct platform_device *pdev) +{ + struct qmi_handle *qmi = platform_get_drvdata(pdev); + + qmi_client_release(qmi); + + return 0; +} + +static struct platform_driver qrtr_sample_driver = { + .probe = qrtr_sample_probe, + .remove = qrtr_sample_remove, + .driver = { + .name = "qrtr_sample_client", + }, +}; + +static int qrtr_sample_new_server(struct qrtr_handle *qrtr, + struct qrtr_service *service) +{ + struct platform_device *pdev; + struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port }; + char name[32]; + int ret; + + snprintf(name, sizeof(name), "qrtr_sample_client@%d:%d", + service->node, service->port); + + pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE); + if (!pdev) + return -ENOMEM; + + ret = platform_device_add_data(pdev, &sq, sizeof(sq)); + if (ret) + goto err_put_device; + + pdev->dev.groups = qrtr_dev_groups; + pdev->driver_override = (char *)qrtr_sample_driver.driver.name; + ret = platform_device_add(pdev); + if (ret) + goto err_put_device; + + service->cookie = pdev; + + return 0; + +err_put_device: + platform_device_put(pdev); + + return ret; +} + +static void qrtr_sample_del_server(struct qrtr_handle *qrtr, + struct qrtr_service *service) +{ + struct platform_device *pdev = service->cookie; + + platform_device_unregister(pdev); +} + +static struct qrtr_handle lookup_client; + +static struct qrtr_handle_ops lookup_ops; + +static void qrtr_sample_net_reset_work(struct work_struct *work) +{ + int ret; + + qrtr_client_release(&lookup_client); + + ret = qrtr_client_init(&lookup_client, 0, &lookup_ops); + if (ret < 0) + return; + + qrtr_client_new_lookup(&lookup_client, 15, 0); +} +static DECLARE_WORK(net_reset_work, qrtr_sample_net_reset_work); + +static void qrtr_sample_net_reset(struct qrtr_handle *qrtr) +{ + schedule_work(&net_reset_work); +} + +static struct qrtr_handle_ops lookup_ops = { + .new_server = qrtr_sample_new_server, + .del_server = qrtr_sample_del_server, + .net_reset = qrtr_sample_net_reset, +}; + +static int qrtr_sample_init(void) +{ + int ret; + + ret = platform_driver_register(&qrtr_sample_driver); + if (ret) + return ret; + + ret = qrtr_client_init(&lookup_client, 0, &lookup_ops); + if (ret < 0) + goto err_unregister_driver; + + qrtr_client_new_lookup(&lookup_client, 15, 0); + + return 0; + +err_unregister_driver: + platform_driver_unregister(&qrtr_sample_driver); + + return ret; +} + +static void qrtr_sample_exit(void) +{ + qrtr_client_release(&lookup_client); + + platform_driver_unregister(&qrtr_sample_driver); +} + +module_init(qrtr_sample_init); +module_exit(qrtr_sample_exit); + +MODULE_DESCRIPTION("Sample QRTR client driver"); +MODULE_LICENSE("GPL v2");