mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 04:12:04 -04:00
Merge "net: qrtr: mhi_dev: Handle mhi register error case"
This commit is contained in:
commit
37585ba4dd
3 changed files with 277 additions and 0 deletions
|
|
@ -68,4 +68,13 @@ config QRTR_HAVEN
|
|||
Router communication between two virtual machines. The transport
|
||||
uses dynamically shared memory and haven doorbells.
|
||||
|
||||
config QRTR_MHI_DEV
|
||||
tristate "MHI Device IPC Router channels"
|
||||
depends on MSM_MHI_DEV || (COMPILE_TEST && MSM_MHI_DEV=n)
|
||||
help
|
||||
Say Y here to support MHI base ipcrouter channels for device
|
||||
endpoint mode. MHI is the transport used for external modem
|
||||
connections. This driver enables QRTR to run on the modem device
|
||||
side.
|
||||
|
||||
endif # QRTR
|
||||
|
|
|
|||
|
|
@ -7,5 +7,7 @@ obj-$(CONFIG_QRTR_TUN) += qrtr-tun.o
|
|||
qrtr-tun-y := tun.o
|
||||
obj-$(CONFIG_QRTR_MHI) += qrtr-mhi.o
|
||||
qrtr-mhi-y := mhi.o
|
||||
obj-$(CONFIG_QRTR_MHI_DEV) += qrtr-mhi-dev.o
|
||||
qrtr-mhi-dev-y := mhi_dev.o
|
||||
obj-$(CONFIG_QRTR_HAVEN) += qrtr-haven.o
|
||||
qrtr-haven-y := haven.o
|
||||
|
|
|
|||
266
net/qrtr/mhi_dev.c
Normal file
266
net/qrtr/mhi_dev.c
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/msm_mhi_dev.h>
|
||||
#include "qrtr.h"
|
||||
|
||||
#define QRTR_MAX_PKT_SIZE SZ_32K
|
||||
|
||||
/* MHI DEV Enums are defined from Host perspective */
|
||||
#define QRTR_MHI_DEV_OUT MHI_CLIENT_IPCR_IN
|
||||
#define QRTR_MHI_DEV_IN MHI_CLIENT_IPCR_OUT
|
||||
|
||||
/**
|
||||
* struct qrtr_mhi_dev_ep - qrtr mhi device endpoint
|
||||
* @ep: endpoint
|
||||
* @dev: device from platform bus
|
||||
* @out: channel handle from mhi dev
|
||||
* @out_tre: complete when channel is ready to send
|
||||
* @out_lock: hold when resetting completion variable
|
||||
* @in: channel handle from mhi dev
|
||||
* @buf_in: buffer to hold incoming data
|
||||
* @net_id: subnet id used by qrtr core
|
||||
* @rt: realtime option used by qrtr core
|
||||
*/
|
||||
struct qrtr_mhi_dev_ep {
|
||||
struct qrtr_endpoint ep;
|
||||
struct device *dev;
|
||||
struct mhi_dev_client *out;
|
||||
struct completion out_tre;
|
||||
struct mutex out_lock; /* for out critical sections */
|
||||
struct mhi_dev_client *in;
|
||||
void *buf_in;
|
||||
|
||||
u32 net_id;
|
||||
bool rt;
|
||||
};
|
||||
|
||||
static struct qrtr_mhi_dev_ep *qrtr_mhi_device_endpoint;
|
||||
|
||||
static int qrtr_mhi_dev_send(struct qrtr_endpoint *ep, struct sk_buff *skb)
|
||||
{
|
||||
struct qrtr_mhi_dev_ep *qep;
|
||||
struct mhi_req req = { 0 };
|
||||
int rc;
|
||||
|
||||
qep = container_of(ep, struct qrtr_mhi_dev_ep, ep);
|
||||
rc = skb_linearize(skb);
|
||||
if (rc) {
|
||||
kfree_skb(skb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
req.chan = QRTR_MHI_DEV_OUT;
|
||||
req.client = qep->out;
|
||||
req.mode = DMA_SYNC;
|
||||
req.buf = skb->data;
|
||||
req.len = skb->len;
|
||||
|
||||
do {
|
||||
wait_for_completion(&qep->out_tre);
|
||||
|
||||
mutex_lock(&qep->out_lock);
|
||||
rc = mhi_dev_write_channel(&req);
|
||||
if (rc == 0)
|
||||
reinit_completion(&qep->out_tre);
|
||||
mutex_unlock(&qep->out_lock);
|
||||
} while (!rc);
|
||||
|
||||
if (rc != skb->len) {
|
||||
dev_err(qep->dev, "send failed rc:%d len:%d\n", rc, skb->len);
|
||||
kfree_skb(skb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
consume_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qrtr_mhi_dev_read(struct qrtr_mhi_dev_ep *qep)
|
||||
{
|
||||
struct mhi_req req = { 0 };
|
||||
int rc;
|
||||
int bytes_read;
|
||||
|
||||
req.chan = QRTR_MHI_DEV_IN;
|
||||
req.client = qep->in;
|
||||
req.mode = DMA_SYNC;
|
||||
req.buf = qep->buf_in;
|
||||
req.len = QRTR_MAX_PKT_SIZE;
|
||||
|
||||
do {
|
||||
bytes_read = mhi_dev_read_channel(&req);
|
||||
if (bytes_read < 0) {
|
||||
dev_err(qep->dev, "failed to read channel %d\n",
|
||||
bytes_read);
|
||||
return;
|
||||
}
|
||||
if (bytes_read == 0)
|
||||
return;
|
||||
|
||||
rc = qrtr_endpoint_post(&qep->ep, req.buf, req.transfer_len);
|
||||
if (rc == -EINVAL)
|
||||
dev_err(qep->dev, "invalid ipcrouter packet\n");
|
||||
} while (bytes_read > 0);
|
||||
}
|
||||
|
||||
static void qrtr_mhi_dev_event_cb(struct mhi_dev_client_cb_reason *reason)
|
||||
{
|
||||
struct qrtr_mhi_dev_ep *qep;
|
||||
|
||||
qep = qrtr_mhi_device_endpoint;
|
||||
if (!qep)
|
||||
return;
|
||||
|
||||
if (reason->reason == MHI_DEV_TRE_AVAILABLE) {
|
||||
pr_debug("TRE available event for chan %d\n", reason->ch_id);
|
||||
if (reason->ch_id == QRTR_MHI_DEV_IN) {
|
||||
qrtr_mhi_dev_read(qep);
|
||||
} else {
|
||||
mutex_lock(&qep->out_lock);
|
||||
complete_all(&qep->out_tre);
|
||||
mutex_unlock(&qep->out_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int qrtr_mhi_dev_open_channels(struct qrtr_mhi_dev_ep *qep)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* write channel */
|
||||
rc = mhi_dev_open_channel(QRTR_MHI_DEV_IN, &qep->in,
|
||||
qrtr_mhi_dev_event_cb);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* read channel */
|
||||
rc = mhi_dev_open_channel(QRTR_MHI_DEV_OUT, &qep->out,
|
||||
qrtr_mhi_dev_event_cb);
|
||||
if (rc < 0) {
|
||||
mhi_dev_close_channel(qep->in);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qrtr_mhi_dev_close_channels(struct qrtr_mhi_dev_ep *qep)
|
||||
{
|
||||
|
||||
mhi_dev_close_channel(qep->in);
|
||||
mhi_dev_close_channel(qep->out);
|
||||
}
|
||||
|
||||
static void qrtr_mhi_dev_state_cb(struct mhi_dev_client_cb_data *cb_data)
|
||||
{
|
||||
struct qrtr_mhi_dev_ep *qep;
|
||||
int rc;
|
||||
|
||||
if (!cb_data || !cb_data->user_data)
|
||||
return;
|
||||
qep = cb_data->user_data;
|
||||
|
||||
switch (cb_data->ctrl_info) {
|
||||
case MHI_STATE_CONNECTED:
|
||||
rc = qrtr_mhi_dev_open_channels(qep);
|
||||
if (rc) {
|
||||
dev_err(qep->dev, "open failed %d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = qrtr_endpoint_register(&qep->ep, qep->net_id, qep->rt);
|
||||
if (rc) {
|
||||
dev_err(qep->dev, "register failed %d\n", rc);
|
||||
qrtr_mhi_dev_close_channels(qep);
|
||||
}
|
||||
break;
|
||||
case MHI_STATE_DISCONNECTED:
|
||||
qrtr_endpoint_unregister(&qep->ep);
|
||||
qrtr_mhi_dev_close_channels(qep);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int qrtr_mhi_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qrtr_mhi_dev_ep *qep;
|
||||
struct device_node *node;
|
||||
int rc;
|
||||
struct mhi_dev_client_cb_data cb_data;
|
||||
|
||||
qep = devm_kzalloc(&pdev->dev, sizeof(*qep), GFP_KERNEL);
|
||||
if (!qep)
|
||||
return -ENOMEM;
|
||||
qep->dev = &pdev->dev;
|
||||
|
||||
node = pdev->dev.of_node;
|
||||
rc = of_property_read_u32(node, "qcom,net-id", &qep->net_id);
|
||||
if (rc < 0)
|
||||
qep->net_id = QRTR_EP_NET_ID_AUTO;
|
||||
qep->rt = of_property_read_bool(node, "qcom,low-latency");
|
||||
|
||||
qep->buf_in = devm_kzalloc(&pdev->dev, QRTR_MAX_PKT_SIZE, GFP_KERNEL);
|
||||
if (!qep->buf_in)
|
||||
return -ENOMEM;
|
||||
|
||||
qrtr_mhi_device_endpoint = qep;
|
||||
|
||||
mutex_init(&qep->out_lock);
|
||||
init_completion(&qep->out_tre);
|
||||
qep->ep.xmit = qrtr_mhi_dev_send;
|
||||
/* HOST init TX first followed by RX, so register for endpoint TX
|
||||
* which makes both channel ready by checking one channel state.
|
||||
*/
|
||||
rc = mhi_register_state_cb(qrtr_mhi_dev_state_cb, qep,
|
||||
QRTR_MHI_DEV_OUT);
|
||||
if (rc == -EEXIST) {
|
||||
/**
|
||||
* MHI stack will return -EEXIST if mhi channel is already
|
||||
* opend by the host and will not invoke reqistered callback.
|
||||
* But future state change notification will inform through
|
||||
* registered callback.
|
||||
*/
|
||||
complete_all(&qep->out_tre);
|
||||
cb_data.user_data = (void *)qep;
|
||||
cb_data.channel = QRTR_MHI_DEV_OUT;
|
||||
cb_data.ctrl_info = MHI_STATE_CONNECTED;
|
||||
qrtr_mhi_dev_state_cb(&cb_data);
|
||||
} else if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qrtr_mhi_dev_match_table[] = {
|
||||
{ .compatible = "qcom,qrtr-mhi-dev"},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver qrtr_mhi_dev_driver = {
|
||||
.probe = qrtr_mhi_dev_probe,
|
||||
.driver = {
|
||||
.name = "qrtr_mhi_dev",
|
||||
.of_match_table = qrtr_mhi_dev_match_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(qrtr_mhi_dev_driver);
|
||||
|
||||
MODULE_DESCRIPTION("QTI IPC-Router MHI device interface driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Loading…
Reference in a new issue