[PATCH] rpmsg: glink: Initialize the "intent_req_comp" completion variable
The "intent_req_comp" variable is used without initialization which results in NULL pointer derefernce in qcom_glink_request_intent(). we need to initialize the completion variable before using it. Fixes: 27b9c5b66b23 ("rpmsg: glink: Request for intents when unavailable") Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 5dcc9bf..fcd46ab 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -227,6 +227,7 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, init_completion(&channel->open_req); init_completion(&channel->open_ack); + init_completion(&channel->intent_req_comp); INIT_LIST_HEAD(&channel->done_intents); INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V1] rpmsg: glink: Initialize the "intent_req_comp" completion variable
The "intent_req_comp" variable is used without initialization which results in NULL pointer dereference in qcom_glink_request_intent(). we need to initialize the completion variable before using it. Fixes: 27b9c5b66b23 ("rpmsg: glink: Request for intents when unavailable") Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 5dcc9bf..fcd46ab 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -227,6 +227,7 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, init_completion(&channel->open_req); init_completion(&channel->open_ack); + init_completion(&channel->intent_req_comp); INIT_LIST_HEAD(&channel->done_intents); INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 0/9] Add chrdev and name query support for GLINK
Add support for the GLINK rpmsg transport to register a rpmsg chrdev. This will create the rpmsg_ctrl nodes for userspace clients to open rpmsg epts. Create a label property that will help userspace clients distinguish between the different GLINK links. The rpmsg chrdev allocation is done by allocating a local channel which also allocates an ept. We need to add some guards against edge cases for this chrdev because it will never fully open. Changes since v3: - Change to device_add_group for rpmsg name attr - Add patch to unregister the rpmsg device - Add patch to support compat ioctl for rpmsg char driver Changes since v2: - Revert change to make glink attribute table const Changes since v1: - Add explanation to dt-bindings commit message - Add patch complete_all the open_req/ack variables - Add patch to prevent null pointer dereference in chrdev channel release - Change chrdev allocation to use glink channel allocation - Change glink attr struct to const Arun Kumar Neelakantam (2): rpmsg: glink: unregister rpmsg device during endpoint destroy rpmsg: Add compat ioctl for rpmsg char driver Chris Lew (7): dt-bindings: soc: qcom: Add label for GLINK bindings rpmsg: glink: Store edge name for glink device rpmsg: glink: Use complete_all for open states rpmsg: Guard against null endpoint ops in destroy rpmsg: glink: Add support for rpmsg glink chrdev rpmsg: glink: Expose rpmsg name attr for glink rpmsg: glink: Remove chunk size word align warning .../devicetree/bindings/soc/qcom/qcom,glink.txt| 5 ++ drivers/rpmsg/qcom_glink_native.c | 88 -- drivers/rpmsg/rpmsg_char.c | 2 + drivers/rpmsg/rpmsg_core.c | 2 +- 4 files changed, 90 insertions(+), 7 deletions(-) -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 4/9] rpmsg: Guard against null endpoint ops in destroy
From: Chris Lew In RPMSG GLINK the chrdev device will allocate an ept as part of the rpdev creation. This device will not register endpoint ops even though it has an allocated ept. Protect against the case where the device is being destroyed. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index b714a54..0860328 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -80,7 +80,7 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev, */ void rpmsg_destroy_ept(struct rpmsg_endpoint *ept) { - if (ept) + if (ept && ept->ops) ept->ops->destroy_ept(ept); } EXPORT_SYMBOL(rpmsg_destroy_ept); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 5/9] rpmsg: glink: Add support for rpmsg glink chrdev
From: Chris Lew RPMSG provides a char device interface to userspace. Probe the rpmsg chrdev channel to enable the rpmsg_ctrl device creation on glink transports. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 40 ++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index f43341d..9f3d026 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1158,7 +1158,7 @@ static int qcom_glink_announce_create(struct rpmsg_device *rpdev) __be32 *val = defaults; int size; - if (glink->intentless) + if (glink->intentless || !completion_done(&channel->open_ack)) return 0; prop = of_find_property(np, "qcom,intents", NULL); @@ -1544,6 +1544,40 @@ static void qcom_glink_work(struct work_struct *work) } } +static void qcom_glink_device_release(struct device *dev) +{ + struct rpmsg_device *rpdev = to_rpmsg_device(dev); + struct glink_channel *channel = to_glink_channel(rpdev->ept); + + /* Release qcom_glink_alloc_channel() reference */ + kref_put(&channel->refcount, qcom_glink_channel_release); + kfree(rpdev); +} + +static int qcom_glink_create_chrdev(struct qcom_glink *glink) +{ + struct rpmsg_device *rpdev; + struct glink_channel *channel; + + rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); + if (!rpdev) + return -ENOMEM; + + channel = qcom_glink_alloc_channel(glink, "rpmsg_chrdev"); + if (IS_ERR(channel)) { + kfree(rpdev); + return PTR_ERR(channel); + } + channel->rpdev = rpdev; + + rpdev->ept = &channel->ept; + rpdev->ops = &glink_device_ops; + rpdev->dev.parent = glink->dev; + rpdev->dev.release = qcom_glink_device_release; + + return rpmsg_chrdev_register_device(rpdev); +} + struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, struct qcom_glink_pipe *rx, @@ -1603,6 +1637,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, if (ret) return ERR_PTR(ret); + ret = qcom_glink_create_chrdev(glink); + if (ret) + dev_err(glink->dev, "failed to register chrdev\n"); + return glink; } EXPORT_SYMBOL_GPL(qcom_glink_native_probe); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 1/9] dt-bindings: soc: qcom: Add label for GLINK bindings
From: Chris Lew There are GLINK clients who open the same channel on multiple GLINK links. These clients need a way to distinguish which remoteproc they are communicating to. Add a label property to identify the edge this node represents. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- Documentation/devicetree/bindings/soc/qcom/qcom,glink.txt | 5 + 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,glink.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,glink.txt index 9663cab..0b8cc53 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,glink.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,glink.txt @@ -10,6 +10,11 @@ edge. Value type: Definition: must be "qcom,glink-rpm" +- label: + Usage: optional + Value type: + Definition: should specify the subsystem name this edge corresponds to. + - interrupts: Usage: required Value type: -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 6/9] rpmsg: glink: Expose rpmsg name attr for glink
From: Chris Lew Expose the name field as an attr so clients listening to uevents for rpmsg can identify the edge the events correspond to. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 26 ++ 1 file changed, 26 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 9f3d026..7b4ffa0 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1544,6 +1544,26 @@ static void qcom_glink_work(struct work_struct *work) } } +static ssize_t rpmsg_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + const char *name; + + ret = of_property_read_string(dev->of_node, "label", &name); + if (ret < 0) + name = dev->of_node->name; + + return snprintf(buf, RPMSG_NAME_SIZE, "%s\n", name); +} +static DEVICE_ATTR_RO(rpmsg_name); + +static struct attribute *qcom_glink_attrs[] = { + &dev_attr_rpmsg_name.attr, + NULL +}; +ATTRIBUTE_GROUPS(qcom_glink); + static void qcom_glink_device_release(struct device *dev) { struct rpmsg_device *rpdev = to_rpmsg_device(dev); @@ -1593,6 +1613,12 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, return ERR_PTR(-ENOMEM); glink->dev = dev; + glink->dev->groups = qcom_glink_groups; + + ret = device_add_groups(dev, qcom_glink_groups); + if (ret) + dev_err(dev, "failed to add groups\n"); + glink->tx_pipe = tx; glink->rx_pipe = rx; -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 2/9] rpmsg: glink: Store edge name for glink device
From: Chris Lew Channels may need to identify the edge their channel was probed for. Store the edge name by reading the label property from device tree or default to the node name. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index f505f58..22a3cac 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -93,6 +93,8 @@ struct glink_core_rx_intent { struct qcom_glink { struct device *dev; + const char *name; + struct mbox_client mbox_client; struct mbox_chan *mbox_chan; @@ -1572,6 +1574,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, idr_init(&glink->lcids); idr_init(&glink->rcids); + ret = of_property_read_string(dev->of_node, "label", &glink->name); + if (ret < 0) + glink->name = dev->of_node->name; + glink->mbox_client.dev = dev; glink->mbox_client.knows_txdone = true; glink->mbox_chan = mbox_request_channel(&glink->mbox_client, 0); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 9/9] rpmsg: Add compat ioctl for rpmsg char driver
Add compat ioctl callback to support 32bit user space applications. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 76a4477..a76b963 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -285,6 +285,7 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, .write = rpmsg_eptdev_write, .poll = rpmsg_eptdev_poll, .unlocked_ioctl = rpmsg_eptdev_ioctl, + .compat_ioctl = rpmsg_eptdev_ioctl, }; static ssize_t name_show(struct device *dev, struct device_attribute *attr, @@ -445,6 +446,7 @@ static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd, .open = rpmsg_ctrldev_open, .release = rpmsg_ctrldev_release, .unlocked_ioctl = rpmsg_ctrldev_ioctl, + .compat_ioctl = rpmsg_ctrldev_ioctl, }; static void rpmsg_ctrldev_release_device(struct device *dev) -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 7/9] rpmsg: glink: Remove chunk size word align warning
From: Chris Lew It is possible for the chunk sizes coming from the non RPM remote procs to not be word aligned. Remove the alignment warning and continue to read from the FIFO so execution is not stalled. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 7b4ffa0..484b427 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -789,9 +789,6 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) return -EAGAIN; } - if (WARN(chunk_size % 4, "Incoming data must be word aligned\n")) - return -EINVAL; - rcid = le16_to_cpu(hdr.msg.param1); spin_lock_irqsave(&glink->idr_lock, flags); channel = idr_find(&glink->rcids, rcid); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 3/9] rpmsg: glink: Use complete_all for open states
From: Chris Lew The open_req and open_ack completion variables are the state variables to represet a remote channel as open. Use complete_all so there are no races with waiters and using completion_done. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 22a3cac..f43341d 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -949,7 +949,7 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return -EINVAL; } - complete(&channel->open_ack); + complete_all(&channel->open_ack); return 0; } @@ -1393,7 +1393,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, channel->rcid = ret; spin_unlock_irqrestore(&glink->idr_lock, flags); - complete(&channel->open_req); + complete_all(&channel->open_req); if (create_device) { rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 8/9] rpmsg: glink: unregister rpmsg device during endpoint destroy
Rpmsg device unregister is not happening if channel close is triggered from local side and causing re-registration of device failures. Unregister rpmsg device for local close in endpoint destroy path. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 484b427..d849bc4 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1184,6 +1184,7 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) { struct glink_channel *channel = to_glink_channel(ept); struct qcom_glink *glink = channel->glink; + struct rpmsg_channel_info chinfo; unsigned long flags; spin_lock_irqsave(&channel->recv_lock, flags); @@ -1191,6 +1192,13 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) spin_unlock_irqrestore(&channel->recv_lock, flags); /* Decouple the potential rpdev from the channel */ + if (channel->rpdev) { + strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = RPMSG_ADDR_ANY; + + rpmsg_unregister_device(glink->dev, &chinfo); + } channel->rpdev = NULL; qcom_glink_send_close_req(glink, channel); @@ -1456,6 +1464,7 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) rpmsg_unregister_device(glink->dev, &chinfo); } + channel->rpdev = NULL; qcom_glink_send_close_ack(glink, channel->rcid); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH 2/3] soc: qcom: Add AOSS QMP communication driver
On 12/27/2018 1:58 AM, Bjorn Andersson wrote: On Tue 20 Nov 04:22 PST 2018, Arun Kumar Neelakantam wrote: Thanks for the review Arun. On 11/12/2018 1:35 PM, Bjorn Andersson wrote: [..] +int qmp_send(struct qmp *qmp, const void *data, size_t len) +{ + int ret; + + if (WARN_ON(len + sizeof(u32) > qmp->size)) { + dev_err(qmp->dev, "message too long\n"); + return -EINVAL; + } + + if (WARN_ON(len % sizeof(u32))) { + dev_err(qmp->dev, "message not 32-bit aligned\n"); + return -EINVAL; + } + + mutex_lock(&qmp->tx_lock); + + if (!qmp_message_empty(qmp)) { + dev_err(qmp->dev, "mailbox left busy\n"); + ret = -EINVAL; should it be -EBUSY ? That makes more sense. And qmp_messge_empty will be done either by remote if it process the data else by this driver in TIMEOUT case, so does we need this check for every TX ? I think we can just reset to Zero once in open time. Didn't think about that, should we really make the QMP link ready again when we get a timeout? Can we expect that the firmware of the remote side is ready to serve future messages? Should we keep this check and remove the writel() below? I prefer we can just remove this check and keep writel() below same as down stream. + goto out_unlock; + } + + /* The message RAM only implements 32-bit accesses */ + __iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32), +data, len / sizeof(u32)); + writel(len, qmp->msgram + qmp->offset); + qmp_kick(qmp); + + ret = wait_event_interruptible_timeout(qmp->event, + qmp_message_empty(qmp), HZ); + if (!ret) { + dev_err(qmp->dev, "ucore did not ack channel\n"); + ret = -ETIMEDOUT; + + writel(0, qmp->msgram + qmp->offset); + } else { + ret = 0; + } + +out_unlock: + mutex_unlock(&qmp->tx_lock); + + return ret; +} Regards, Bjorn
[PATCH V2 2/4] rpmsg: glink: Add support to handle signals command
Remote peripherals send signal notifications over glink with commandID 15. Add support to send and receive the signal command and convert the signals from NATIVE to TIOCM while receiving and vice versa while sending. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 130 ++ 1 file changed, 130 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index e2ce4e6..35e2009 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* + * Copyright (c) 2018, The Linux Foundation. * Copyright (c) 2016-2017, Linaro Ltd */ @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -150,6 +152,8 @@ enum { * @intent_req_lock: Synchronises multiple intent requests * @intent_req_result: Result of intent request * @intent_req_comp: Completion for intent_req signalling + * @lsigs: local side signals + * @rsigs: remote side signals */ struct glink_channel { struct rpmsg_endpoint ept; @@ -181,6 +185,10 @@ struct glink_channel { struct mutex intent_req_lock; bool intent_req_result; struct completion intent_req_comp; + + unsigned int lsigs; + unsigned int rsigs; + }; #define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) @@ -201,9 +209,15 @@ struct glink_channel { #define RPM_CMD_TX_DATA_CONT 12 #define RPM_CMD_READ_NOTIF 13 #define RPM_CMD_RX_DONE_W_REUSE14 +#define RPM_CMD_SIGNALS15 #define GLINK_FEATURE_INTENTLESS BIT(1) +#define NATIVE_DTR_SIG BIT(31) +#define NATIVE_CTS_SIG BIT(30) +#define NATIVE_CD_SIG BIT(29) +#define NATIVE_RI_SIG BIT(28) + static void qcom_glink_rx_done_work(struct work_struct *work); static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, @@ -957,6 +971,76 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return 0; } +/** + * qcom_glink_send_signals() - convert a signal cmd to wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @sigs: The signals to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_send_signals(struct qcom_glink *glink, + struct glink_channel *channel, + u32 sigs) +{ + struct glink_msg msg; + + /* convert signals from TIOCM to NATIVE */ + sigs &= 0x0fff; + if (sigs & TIOCM_DTR) + sigs |= NATIVE_DTR_SIG; + if (sigs & TIOCM_RTS) + sigs |= NATIVE_CTS_SIG; + if (sigs & TIOCM_CD) + sigs |= NATIVE_CD_SIG; + if (sigs & TIOCM_RI) + sigs |= NATIVE_RI_SIG; + + msg.cmd = cpu_to_le16(RPM_CMD_SIGNALS); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(sigs); + + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static int qcom_glink_handle_signals(struct qcom_glink *glink, +unsigned int rcid, unsigned int signals) +{ + struct glink_channel *channel; + unsigned long flags; + u32 old; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "signal for non-existing channel\n"); + return -EINVAL; + } + + old = channel->rsigs; + + /* convert signals from NATIVE to TIOCM */ + if (signals & NATIVE_DTR_SIG) + signals |= TIOCM_DSR; + if (signals & NATIVE_CTS_SIG) + signals |= TIOCM_CTS; + if (signals & NATIVE_CD_SIG) + signals |= TIOCM_CD; + if (signals & NATIVE_RI_SIG) + signals |= TIOCM_RI; + signals &= 0x0fff; + + channel->rsigs = signals; + + if (channel->ept.sig_cb) { + channel->ept.sig_cb(channel->ept.rpdev, channel->ept.priv, + old, channel->rsigs); + } + + return 0; +} + static irqreturn_t qcom_glink_native_intr(int irq, void *data) { struct qcom_glink *glink = data; @@ -1018,6 +1102,10 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) qcom_glink_handle_intent_req_ack(glink, param1, param2); qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; + case RPM_CMD_SIGNALS: + qcom_glink_handle_signals(glink, param1, param2); +
[PATCH V2 4/4] rpmsg: char: Add signal callback and POLLPRI support
Register a callback to get the signal notifications from rpmsg and send POLLPRI mask to indicate the signal change in POLL system call. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 21 + 1 file changed, 21 insertions(+) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 9380c6e..194d54a 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -64,6 +64,7 @@ struct rpmsg_ctrldev { * @queue_lock:synchronization of @queue operations * @queue: incoming message queue * @readq: wait object for incoming queue + * @sig_pending:state of signal notification */ struct rpmsg_eptdev { struct device dev; @@ -78,6 +79,8 @@ struct rpmsg_eptdev { spinlock_t queue_lock; struct sk_buff_head queue; wait_queue_head_t readq; + + bool sig_pending; }; static int rpmsg_eptdev_destroy(struct device *dev, void *data) @@ -122,6 +125,18 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, return 0; } +static int rpmsg_sigs_cb(struct rpmsg_device *rpdev, void *priv, +u32 old, u32 new) +{ + struct rpmsg_eptdev *eptdev = priv; + + eptdev->sig_pending = true; + + /* wake up any blocking processes, waiting for signal notification */ + wake_up_interruptible(&eptdev->readq); + return 0; +} + static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) { struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); @@ -138,6 +153,7 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) return -EINVAL; } + ept->sig_cb = rpmsg_sigs_cb; eptdev->ept = ept; filp->private_data = eptdev; @@ -157,6 +173,7 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) eptdev->ept = NULL; } mutex_unlock(&eptdev->ept_lock); + eptdev->sig_pending = false; /* Discard all SKBs */ while (!skb_queue_empty(&eptdev->queue)) { @@ -263,6 +280,9 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) if (!skb_queue_empty(&eptdev->queue)) mask |= EPOLLIN | EPOLLRDNORM; + if (eptdev->sig_pending) + mask |= POLLPRI; + mask |= rpmsg_poll(eptdev->ept, filp, wait); return mask; @@ -307,6 +327,7 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, switch (cmd) { case TIOCMGET: + eptdev->sig_pending = false; ret = rpmsg_get_signals(eptdev->ept, &lsigs, &rsigs); if (!ret) ret = put_user(rsigs, (int __user *)arg); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V2 1/4] rpmsg: core: Add signal API support
Some transports like Glink support the state notifications between clients using signals similar to serial protocol signals. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_core.c | 42 ++ drivers/rpmsg/rpmsg_internal.h | 5 + include/linux/rpmsg.h | 27 +++ 3 files changed, 74 insertions(+) diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 8122807..9efcd26 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -2,6 +2,7 @@ /* * remote processor messaging bus * + * Copyright (c) 2018, The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * @@ -177,6 +178,43 @@ int rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, EXPORT_SYMBOL(rpmsg_send_offchannel); /** + * rpmsg_get_signals() - get the signals for this endpoint + * @ept: the rpmsg endpoint + * @sigs: serial signals bitmask + * + * Returns 0 on success and an appropriate error value on failure. + */ +int rpmsg_get_signals(struct rpmsg_endpoint *ept, u32 *lsigs, u32 *rsigs) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->get_signals) + return -EOPNOTSUPP; + + return ept->ops->get_signals(ept, lsigs, rsigs); +} +EXPORT_SYMBOL(rpmsg_get_signals); + +/** + * rpmsg_set_signals() - set the remote signals for this endpoint + * @ept: the rpmsg endpoint + * @set: set mask for signals + * @clear: clear mask for signals + * + * Returns 0 on success and an appropriate error value on failure. + */ +int rpmsg_set_signals(struct rpmsg_endpoint *ept, u32 set, u32 clear) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->set_signals) + return -EOPNOTSUPP; + + return ept->ops->set_signals(ept, set, clear); +} +EXPORT_SYMBOL(rpmsg_set_signals); + +/** * rpmsg_send() - send a message across to the remote processor * @ept: the rpmsg endpoint * @data: payload of message @@ -468,6 +506,10 @@ static int rpmsg_dev_probe(struct device *dev) rpdev->ept = ept; rpdev->src = ept->addr; + + if (rpdrv->signals) + ept->sig_cb = rpdrv->signals; + } err = rpdrv->probe(rpdev); diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index 0d791c3..0a37d9bf 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -2,6 +2,7 @@ /* * remote processor messaging bus internals * + * Copyright (c) 2018, The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * @@ -46,6 +47,8 @@ struct rpmsg_device_ops { * @trysend: see @rpmsg_trysend(), required * @trysendto: see @rpmsg_trysendto(), optional * @trysend_offchannel:see @rpmsg_trysend_offchannel(), optional + * @get_signals: see @rpmsg_get_signals(), optional + * @set_signals: see @rpmsg_set_signals(), optional * * Indirection table for the operations that a rpmsg backend should implement. * In addition to @destroy_ept, the backend must at least implement @send and @@ -65,6 +68,8 @@ struct rpmsg_endpoint_ops { void *data, int len); __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); + int (*get_signals)(struct rpmsg_endpoint *ept, u32 *lsigs, u32 *rsigs); + int (*set_signals)(struct rpmsg_endpoint *ept, u32 set, u32 clear); }; int rpmsg_register_device(struct rpmsg_device *rpdev); diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 9fe156d..4e50d57 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -2,6 +2,7 @@ /* * Remote processor messaging * + * Copyright (c) 2018 The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * All rights reserved. @@ -60,6 +61,7 @@ struct rpmsg_device { }; typedef int (*rpmsg_rx_cb_t)(struct rpmsg_device *, void *, int, void *, u32); +typedef int (*rpmsg_rx_sig_t)(struct rpmsg_device *, void *, u32, u32); /** * struct rpmsg_endpoint - binds a local rpmsg address to its user @@ -67,6 +69,7 @@ struct rpmsg_device { * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler * @cb_lock: must be taken before accessing/changing @cb + * @sig_cb: rx serial signal handler * @addr: local rpmsg address * @priv: private data for the driver's use * @@ -89,6 +92,7 @@ struct rpmsg_endpoint { struct kref refcount; rpmsg_rx_cb_t cb; struct mutex cb_lock; + rpmsg_rx_sig_t sig_cb; u32 addr; void *priv; @@ -102,6 +106,7 @@ struct rpmsg_endpoint { * @probe: invoked wh
[PATCH V2 3/4] rpmsg: char: Add TIOCMGET/TIOCMSET ioctl support
Add TICOMGET and TIOCMSET ioctl support for rpmsg char device nodes to get/set the low level transport signals. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 54 +++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index a76b963..9380c6e 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* + * Copyright (c) 2018, The Linux Foundation. * Copyright (c) 2016, Linaro Ltd. * Copyright (c) 2012, Michal Simek * Copyright (c) 2012, PetaLogix @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -266,15 +268,61 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) return mask; } +static int rpmsg_eptdev_tiocmset(struct file *fp, unsigned int cmd, +int __user *arg) +{ + struct rpmsg_eptdev *eptdev = fp->private_data; + u32 set, clear, val; + int ret; + + ret = get_user(val, arg); + if (ret) + return ret; + set = clear = 0; + switch (cmd) { + case TIOCMBIS: + set = val; + break; + case TIOCMBIC: + clear = val; + break; + case TIOCMSET: + set = val; + clear = ~val; + break; + } + + set &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI; + clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI; + + return rpmsg_set_signals(eptdev->ept, set, clear); +} + static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { struct rpmsg_eptdev *eptdev = fp->private_data; + u32 lsigs, rsigs; + int ret; - if (cmd != RPMSG_DESTROY_EPT_IOCTL) - return -EINVAL; + switch (cmd) { + case TIOCMGET: + ret = rpmsg_get_signals(eptdev->ept, &lsigs, &rsigs); + if (!ret) + ret = put_user(rsigs, (int __user *)arg); + break; + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + ret = rpmsg_eptdev_tiocmset(fp, cmd, (int __user *)arg); + break; + case RPMSG_DESTROY_EPT_IOCTL: + ret = rpmsg_eptdev_destroy(&eptdev->dev, NULL); + default: + ret = -EINVAL; + } - return rpmsg_eptdev_destroy(&eptdev->dev, NULL); + return ret; } static const struct file_operations rpmsg_eptdev_fops = { -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V2 0/4] Add TIOCM Signals support for RPMSG char devices
Glink transport support signals to exchange state notification between local and remote side clients. Adding support to send/receive the signal command and notify the clients through callback and POLL notification. Changes since v1: - Split the patches as per functional areas like core, char, glink - Add set, clear mask for TIOCMSET - Merge the char signal callback and POLLPRI patches Arun Kumar Neelakantam (4): rpmsg: core: Add signal API support rpmsg: glink: Add support to handle signals command rpmsg: char: Add TIOCMGET/TIOCMSET ioctl support rpmsg: char: Add signal callback and POLLPRI support drivers/rpmsg/qcom_glink_native.c | 130 ++ drivers/rpmsg/rpmsg_char.c| 75 +- drivers/rpmsg/rpmsg_core.c| 42 drivers/rpmsg/rpmsg_internal.h| 5 ++ include/linux/rpmsg.h | 27 5 files changed, 276 insertions(+), 3 deletions(-) -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V3 1/4] rpmsg: core: Add signal API support
Some transports like Glink support the state notifications between clients using signals similar to serial protocol signals. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_core.c | 41 + drivers/rpmsg/rpmsg_internal.h | 5 + include/linux/rpmsg.h | 26 ++ 3 files changed, 72 insertions(+) diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 8122807..e9b719b 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -2,6 +2,7 @@ /* * remote processor messaging bus * + * Copyright (c) 2018, The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * @@ -283,6 +284,42 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, } EXPORT_SYMBOL(rpmsg_trysend_offchannel); +/** + * rpmsg_get_signals() - get the signals for this endpoint + * @ept: the rpmsg endpoint + * + * Returns 0 on success and an appropriate error value on failure. + */ +int rpmsg_get_signals(struct rpmsg_endpoint *ept) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->get_signals) + return -EOPNOTSUPP; + + return ept->ops->get_signals(ept); +} +EXPORT_SYMBOL(rpmsg_get_signals); + +/** + * rpmsg_set_signals() - set the remote signals for this endpoint + * @ept: the rpmsg endpoint + * @set: set mask for signals + * @clear: clear mask for signals + * + * Returns 0 on success and an appropriate error value on failure. + */ +int rpmsg_set_signals(struct rpmsg_endpoint *ept, u32 set, u32 clear) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->set_signals) + return -EOPNOTSUPP; + + return ept->ops->set_signals(ept, set, clear); +} +EXPORT_SYMBOL(rpmsg_set_signals); + /* * match an rpmsg channel with a channel info struct. * this is used to make sure we're not creating rpmsg devices for channels @@ -468,6 +505,10 @@ static int rpmsg_dev_probe(struct device *dev) rpdev->ept = ept; rpdev->src = ept->addr; + + if (rpdrv->signals) + ept->sig_cb = rpdrv->signals; + } err = rpdrv->probe(rpdev); diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index 0d791c3..033656d 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -2,6 +2,7 @@ /* * remote processor messaging bus internals * + * Copyright (c) 2018, The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * @@ -46,6 +47,8 @@ struct rpmsg_device_ops { * @trysend: see @rpmsg_trysend(), required * @trysendto: see @rpmsg_trysendto(), optional * @trysend_offchannel:see @rpmsg_trysend_offchannel(), optional + * @get_signals: see @rpmsg_get_signals(), optional + * @set_signals: see @rpmsg_set_signals(), optional * * Indirection table for the operations that a rpmsg backend should implement. * In addition to @destroy_ept, the backend must at least implement @send and @@ -65,6 +68,8 @@ struct rpmsg_endpoint_ops { void *data, int len); __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); + int (*get_signals)(struct rpmsg_endpoint *ept); + int (*set_signals)(struct rpmsg_endpoint *ept, u32 set, u32 clear); }; int rpmsg_register_device(struct rpmsg_device *rpdev); diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 9fe156d..48c8ae3 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -2,6 +2,7 @@ /* * Remote processor messaging * + * Copyright (c) 2018 The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * All rights reserved. @@ -60,6 +61,7 @@ struct rpmsg_device { }; typedef int (*rpmsg_rx_cb_t)(struct rpmsg_device *, void *, int, void *, u32); +typedef int (*rpmsg_rx_sig_t)(struct rpmsg_device *, void *, u32, u32); /** * struct rpmsg_endpoint - binds a local rpmsg address to its user @@ -67,6 +69,7 @@ struct rpmsg_device { * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler * @cb_lock: must be taken before accessing/changing @cb + * @sig_cb: rx serial signal handler * @addr: local rpmsg address * @priv: private data for the driver's use * @@ -89,6 +92,7 @@ struct rpmsg_endpoint { struct kref refcount; rpmsg_rx_cb_t cb; struct mutex cb_lock; + rpmsg_rx_sig_t sig_cb; u32 addr; void *priv; @@ -102,6 +106,7 @@ struct rpmsg_endpoint { * @probe: invoked when a matching rpmsg channel (i.e. device) is found * @remove: invoked when the rpmsg ch
[PATCH V3 0/4] Add TIOCM Signals support for RPMSG char devices
Glink transport support signals to exchange state notification between local and remote side clients. Adding support to send/receive the signal command and notify the clients through callback and POLL notification. Changes since v1: - Split the patches as per functional areas like core, char, glink - Add set, clear mask for TIOCMSET - Merge the char signal callback and POLLPRI patches Changes since v2: - Modify the rpmsg_get_signals function prototype Arun Kumar Neelakantam (4): rpmsg: core: Add signal API support rpmsg: glink: Add support to handle signals command rpmsg: char: Add TIOCMGET/TIOCMSET ioctl support rpmsg: char: Add signal callback and POLLPRI support drivers/rpmsg/qcom_glink_native.c | 126 ++ drivers/rpmsg/rpmsg_char.c| 74 +- drivers/rpmsg/rpmsg_core.c| 41 + drivers/rpmsg/rpmsg_internal.h| 5 ++ include/linux/rpmsg.h | 26 5 files changed, 269 insertions(+), 3 deletions(-) -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V3 2/4] rpmsg: glink: Add support to handle signals command
Remote peripherals send signal notifications over glink with commandID 15. Add support to send and receive the signal command and convert the signals from NATIVE to TIOCM while receiving and vice versa while sending. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 126 ++ 1 file changed, 126 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index e2ce4e6..e90f543 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* + * Copyright (c) 2018, The Linux Foundation. * Copyright (c) 2016-2017, Linaro Ltd */ @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -150,6 +152,8 @@ enum { * @intent_req_lock: Synchronises multiple intent requests * @intent_req_result: Result of intent request * @intent_req_comp: Completion for intent_req signalling + * @lsigs: local side signals + * @rsigs: remote side signals */ struct glink_channel { struct rpmsg_endpoint ept; @@ -181,6 +185,10 @@ struct glink_channel { struct mutex intent_req_lock; bool intent_req_result; struct completion intent_req_comp; + + unsigned int lsigs; + unsigned int rsigs; + }; #define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) @@ -201,9 +209,15 @@ struct glink_channel { #define RPM_CMD_TX_DATA_CONT 12 #define RPM_CMD_READ_NOTIF 13 #define RPM_CMD_RX_DONE_W_REUSE14 +#define RPM_CMD_SIGNALS15 #define GLINK_FEATURE_INTENTLESS BIT(1) +#define NATIVE_DTR_SIG BIT(31) +#define NATIVE_CTS_SIG BIT(30) +#define NATIVE_CD_SIG BIT(29) +#define NATIVE_RI_SIG BIT(28) + static void qcom_glink_rx_done_work(struct work_struct *work); static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, @@ -957,6 +971,76 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return 0; } +/** + * qcom_glink_send_signals() - convert a signal cmd to wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @sigs: The signals to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_send_signals(struct qcom_glink *glink, + struct glink_channel *channel, + u32 sigs) +{ + struct glink_msg msg; + + /* convert signals from TIOCM to NATIVE */ + sigs &= 0x0fff; + if (sigs & TIOCM_DTR) + sigs |= NATIVE_DTR_SIG; + if (sigs & TIOCM_RTS) + sigs |= NATIVE_CTS_SIG; + if (sigs & TIOCM_CD) + sigs |= NATIVE_CD_SIG; + if (sigs & TIOCM_RI) + sigs |= NATIVE_RI_SIG; + + msg.cmd = cpu_to_le16(RPM_CMD_SIGNALS); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(sigs); + + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static int qcom_glink_handle_signals(struct qcom_glink *glink, +unsigned int rcid, unsigned int signals) +{ + struct glink_channel *channel; + unsigned long flags; + u32 old; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "signal for non-existing channel\n"); + return -EINVAL; + } + + old = channel->rsigs; + + /* convert signals from NATIVE to TIOCM */ + if (signals & NATIVE_DTR_SIG) + signals |= TIOCM_DSR; + if (signals & NATIVE_CTS_SIG) + signals |= TIOCM_CTS; + if (signals & NATIVE_CD_SIG) + signals |= TIOCM_CD; + if (signals & NATIVE_RI_SIG) + signals |= TIOCM_RI; + signals &= 0x0fff; + + channel->rsigs = signals; + + if (channel->ept.sig_cb) { + channel->ept.sig_cb(channel->ept.rpdev, channel->ept.priv, + old, channel->rsigs); + } + + return 0; +} + static irqreturn_t qcom_glink_native_intr(int irq, void *data) { struct qcom_glink *glink = data; @@ -1018,6 +1102,10 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) qcom_glink_handle_intent_req_ack(glink, param1, param2); qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; + case RPM_CMD_SIGNALS: + qcom_glink_handle_signals(glink, param1, param2); +
[PATCH V3 3/4] rpmsg: char: Add TIOCMGET/TIOCMSET ioctl support
Add TICOMGET and TIOCMSET ioctl support for rpmsg char device nodes to get/set the low level transport signals. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 53 +++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index a76b963..16978f7b 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* + * Copyright (c) 2018, The Linux Foundation. * Copyright (c) 2016, Linaro Ltd. * Copyright (c) 2012, Michal Simek * Copyright (c) 2012, PetaLogix @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -266,15 +268,60 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) return mask; } +static int rpmsg_eptdev_tiocmset(struct file *fp, unsigned int cmd, +int __user *arg) +{ + struct rpmsg_eptdev *eptdev = fp->private_data; + u32 set, clear, val; + int ret; + + ret = get_user(val, arg); + if (ret) + return ret; + set = clear = 0; + switch (cmd) { + case TIOCMBIS: + set = val; + break; + case TIOCMBIC: + clear = val; + break; + case TIOCMSET: + set = val; + clear = ~val; + break; + } + + set &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI; + clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI; + + return rpmsg_set_signals(eptdev->ept, set, clear); +} + static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { struct rpmsg_eptdev *eptdev = fp->private_data; + int ret; - if (cmd != RPMSG_DESTROY_EPT_IOCTL) - return -EINVAL; + switch (cmd) { + case TIOCMGET: + ret = rpmsg_get_signals(eptdev->ept); + if (!ret) + ret = put_user(ret, (int __user *)arg); + break; + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + ret = rpmsg_eptdev_tiocmset(fp, cmd, (int __user *)arg); + break; + case RPMSG_DESTROY_EPT_IOCTL: + ret = rpmsg_eptdev_destroy(&eptdev->dev, NULL); + default: + ret = -EINVAL; + } - return rpmsg_eptdev_destroy(&eptdev->dev, NULL); + return ret; } static const struct file_operations rpmsg_eptdev_fops = { -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V3 4/4] rpmsg: char: Add signal callback and POLLPRI support
Register a callback to get the signal notifications from rpmsg and send POLLPRI mask to indicate the signal change in POLL system call. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 21 + 1 file changed, 21 insertions(+) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 16978f7b..a5855f7 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -64,6 +64,7 @@ struct rpmsg_ctrldev { * @queue_lock:synchronization of @queue operations * @queue: incoming message queue * @readq: wait object for incoming queue + * @sig_pending:state of signal notification */ struct rpmsg_eptdev { struct device dev; @@ -78,6 +79,8 @@ struct rpmsg_eptdev { spinlock_t queue_lock; struct sk_buff_head queue; wait_queue_head_t readq; + + bool sig_pending; }; static int rpmsg_eptdev_destroy(struct device *dev, void *data) @@ -122,6 +125,18 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, return 0; } +static int rpmsg_sigs_cb(struct rpmsg_device *rpdev, void *priv, +u32 old, u32 new) +{ + struct rpmsg_eptdev *eptdev = priv; + + eptdev->sig_pending = true; + + /* wake up any blocking processes, waiting for signal notification */ + wake_up_interruptible(&eptdev->readq); + return 0; +} + static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) { struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); @@ -138,6 +153,7 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) return -EINVAL; } + ept->sig_cb = rpmsg_sigs_cb; eptdev->ept = ept; filp->private_data = eptdev; @@ -157,6 +173,7 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) eptdev->ept = NULL; } mutex_unlock(&eptdev->ept_lock); + eptdev->sig_pending = false; /* Discard all SKBs */ while (!skb_queue_empty(&eptdev->queue)) { @@ -263,6 +280,9 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) if (!skb_queue_empty(&eptdev->queue)) mask |= EPOLLIN | EPOLLRDNORM; + if (eptdev->sig_pending) + mask |= POLLPRI; + mask |= rpmsg_poll(eptdev->ept, filp, wait); return mask; @@ -306,6 +326,7 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, switch (cmd) { case TIOCMGET: + eptdev->sig_pending = false; ret = rpmsg_get_signals(eptdev->ept); if (!ret) ret = put_user(ret, (int __user *)arg); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 2/4] rpmsg: glink: Add support to handle signals command
Remote peripherals send signal notifications over glink with commandID 15. Add support to send and receive the signal command and convert the signals from NATIVE to TIOCM while receiving and vice versa while sending. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 126 ++ 1 file changed, 126 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index e2ce4e6..e90f543 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* + * Copyright (c) 2018, The Linux Foundation. * Copyright (c) 2016-2017, Linaro Ltd */ @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -150,6 +152,8 @@ enum { * @intent_req_lock: Synchronises multiple intent requests * @intent_req_result: Result of intent request * @intent_req_comp: Completion for intent_req signalling + * @lsigs: local side signals + * @rsigs: remote side signals */ struct glink_channel { struct rpmsg_endpoint ept; @@ -181,6 +185,10 @@ struct glink_channel { struct mutex intent_req_lock; bool intent_req_result; struct completion intent_req_comp; + + unsigned int lsigs; + unsigned int rsigs; + }; #define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) @@ -201,9 +209,15 @@ struct glink_channel { #define RPM_CMD_TX_DATA_CONT 12 #define RPM_CMD_READ_NOTIF 13 #define RPM_CMD_RX_DONE_W_REUSE14 +#define RPM_CMD_SIGNALS15 #define GLINK_FEATURE_INTENTLESS BIT(1) +#define NATIVE_DTR_SIG BIT(31) +#define NATIVE_CTS_SIG BIT(30) +#define NATIVE_CD_SIG BIT(29) +#define NATIVE_RI_SIG BIT(28) + static void qcom_glink_rx_done_work(struct work_struct *work); static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, @@ -957,6 +971,76 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return 0; } +/** + * qcom_glink_send_signals() - convert a signal cmd to wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @sigs: The signals to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_send_signals(struct qcom_glink *glink, + struct glink_channel *channel, + u32 sigs) +{ + struct glink_msg msg; + + /* convert signals from TIOCM to NATIVE */ + sigs &= 0x0fff; + if (sigs & TIOCM_DTR) + sigs |= NATIVE_DTR_SIG; + if (sigs & TIOCM_RTS) + sigs |= NATIVE_CTS_SIG; + if (sigs & TIOCM_CD) + sigs |= NATIVE_CD_SIG; + if (sigs & TIOCM_RI) + sigs |= NATIVE_RI_SIG; + + msg.cmd = cpu_to_le16(RPM_CMD_SIGNALS); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(sigs); + + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static int qcom_glink_handle_signals(struct qcom_glink *glink, +unsigned int rcid, unsigned int signals) +{ + struct glink_channel *channel; + unsigned long flags; + u32 old; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "signal for non-existing channel\n"); + return -EINVAL; + } + + old = channel->rsigs; + + /* convert signals from NATIVE to TIOCM */ + if (signals & NATIVE_DTR_SIG) + signals |= TIOCM_DSR; + if (signals & NATIVE_CTS_SIG) + signals |= TIOCM_CTS; + if (signals & NATIVE_CD_SIG) + signals |= TIOCM_CD; + if (signals & NATIVE_RI_SIG) + signals |= TIOCM_RI; + signals &= 0x0fff; + + channel->rsigs = signals; + + if (channel->ept.sig_cb) { + channel->ept.sig_cb(channel->ept.rpdev, channel->ept.priv, + old, channel->rsigs); + } + + return 0; +} + static irqreturn_t qcom_glink_native_intr(int irq, void *data) { struct qcom_glink *glink = data; @@ -1018,6 +1102,10 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) qcom_glink_handle_intent_req_ack(glink, param1, param2); qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; + case RPM_CMD_SIGNALS: + qcom_glink_handle_signals(glink, param1, param2); +
[PATCH V4 3/4] rpmsg: char: Add TIOCMGET/TIOCMSET ioctl support
Add TICOMGET and TIOCMSET ioctl support for rpmsg char device nodes to get/set the low level transport signals. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 53 +++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index a76b963..b136684 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* + * Copyright (c) 2018, The Linux Foundation. * Copyright (c) 2016, Linaro Ltd. * Copyright (c) 2012, Michal Simek * Copyright (c) 2012, PetaLogix @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -266,15 +268,60 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) return mask; } +static int rpmsg_eptdev_tiocmset(struct file *fp, unsigned int cmd, +int __user *arg) +{ + struct rpmsg_eptdev *eptdev = fp->private_data; + u32 set, clear, val; + int ret; + + ret = get_user(val, arg); + if (ret) + return ret; + set = clear = 0; + switch (cmd) { + case TIOCMBIS: + set = val; + break; + case TIOCMBIC: + clear = val; + break; + case TIOCMSET: + set = val; + clear = ~val; + break; + } + + set &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI; + clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI; + + return rpmsg_set_signals(eptdev->ept, set, clear); +} + static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { struct rpmsg_eptdev *eptdev = fp->private_data; + int ret; - if (cmd != RPMSG_DESTROY_EPT_IOCTL) - return -EINVAL; + switch (cmd) { + case TIOCMGET: + ret = rpmsg_get_signals(eptdev->ept); + if (ret >= 0) + ret = put_user(ret, (int __user *)arg); + break; + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + ret = rpmsg_eptdev_tiocmset(fp, cmd, (int __user *)arg); + break; + case RPMSG_DESTROY_EPT_IOCTL: + ret = rpmsg_eptdev_destroy(&eptdev->dev, NULL); + default: + ret = -EINVAL; + } - return rpmsg_eptdev_destroy(&eptdev->dev, NULL); + return ret; } static const struct file_operations rpmsg_eptdev_fops = { -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 1/4] rpmsg: core: Add signal API support
Some transports like Glink support the state notifications between clients using signals similar to serial protocol signals. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_core.c | 41 + drivers/rpmsg/rpmsg_internal.h | 5 + include/linux/rpmsg.h | 26 ++ 3 files changed, 72 insertions(+) diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 8122807..3d7458a 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -2,6 +2,7 @@ /* * remote processor messaging bus * + * Copyright (c) 2018, The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * @@ -283,6 +284,42 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, } EXPORT_SYMBOL(rpmsg_trysend_offchannel); +/** + * rpmsg_get_signals() - get the signals for this endpoint + * @ept: the rpmsg endpoint + * + * Returns signal bits on success and an appropriate error value on failure. + */ +int rpmsg_get_signals(struct rpmsg_endpoint *ept) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->get_signals) + return -EOPNOTSUPP; + + return ept->ops->get_signals(ept); +} +EXPORT_SYMBOL(rpmsg_get_signals); + +/** + * rpmsg_set_signals() - set the remote signals for this endpoint + * @ept: the rpmsg endpoint + * @set: set mask for signals + * @clear: clear mask for signals + * + * Returns 0 on success and an appropriate error value on failure. + */ +int rpmsg_set_signals(struct rpmsg_endpoint *ept, u32 set, u32 clear) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->set_signals) + return -EOPNOTSUPP; + + return ept->ops->set_signals(ept, set, clear); +} +EXPORT_SYMBOL(rpmsg_set_signals); + /* * match an rpmsg channel with a channel info struct. * this is used to make sure we're not creating rpmsg devices for channels @@ -468,6 +505,10 @@ static int rpmsg_dev_probe(struct device *dev) rpdev->ept = ept; rpdev->src = ept->addr; + + if (rpdrv->signals) + ept->sig_cb = rpdrv->signals; + } err = rpdrv->probe(rpdev); diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index 0d791c3..033656d 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -2,6 +2,7 @@ /* * remote processor messaging bus internals * + * Copyright (c) 2018, The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * @@ -46,6 +47,8 @@ struct rpmsg_device_ops { * @trysend: see @rpmsg_trysend(), required * @trysendto: see @rpmsg_trysendto(), optional * @trysend_offchannel:see @rpmsg_trysend_offchannel(), optional + * @get_signals: see @rpmsg_get_signals(), optional + * @set_signals: see @rpmsg_set_signals(), optional * * Indirection table for the operations that a rpmsg backend should implement. * In addition to @destroy_ept, the backend must at least implement @send and @@ -65,6 +68,8 @@ struct rpmsg_endpoint_ops { void *data, int len); __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); + int (*get_signals)(struct rpmsg_endpoint *ept); + int (*set_signals)(struct rpmsg_endpoint *ept, u32 set, u32 clear); }; int rpmsg_register_device(struct rpmsg_device *rpdev); diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 9fe156d..48c8ae3 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -2,6 +2,7 @@ /* * Remote processor messaging * + * Copyright (c) 2018 The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * All rights reserved. @@ -60,6 +61,7 @@ struct rpmsg_device { }; typedef int (*rpmsg_rx_cb_t)(struct rpmsg_device *, void *, int, void *, u32); +typedef int (*rpmsg_rx_sig_t)(struct rpmsg_device *, void *, u32, u32); /** * struct rpmsg_endpoint - binds a local rpmsg address to its user @@ -67,6 +69,7 @@ struct rpmsg_device { * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler * @cb_lock: must be taken before accessing/changing @cb + * @sig_cb: rx serial signal handler * @addr: local rpmsg address * @priv: private data for the driver's use * @@ -89,6 +92,7 @@ struct rpmsg_endpoint { struct kref refcount; rpmsg_rx_cb_t cb; struct mutex cb_lock; + rpmsg_rx_sig_t sig_cb; u32 addr; void *priv; @@ -102,6 +106,7 @@ struct rpmsg_endpoint { * @probe: invoked when a matching rpmsg channel (i.e. device) is found * @remove: invoked when the
[PATCH V4 4/4] rpmsg: char: Add signal callback and POLLPRI support
Register a callback to get the signal notifications from rpmsg and send POLLPRI mask to indicate the signal change in POLL system call. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 21 + 1 file changed, 21 insertions(+) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index b136684..8a3cbe9 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -64,6 +64,7 @@ struct rpmsg_ctrldev { * @queue_lock:synchronization of @queue operations * @queue: incoming message queue * @readq: wait object for incoming queue + * @sig_pending:state of signal notification */ struct rpmsg_eptdev { struct device dev; @@ -78,6 +79,8 @@ struct rpmsg_eptdev { spinlock_t queue_lock; struct sk_buff_head queue; wait_queue_head_t readq; + + bool sig_pending; }; static int rpmsg_eptdev_destroy(struct device *dev, void *data) @@ -122,6 +125,18 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, return 0; } +static int rpmsg_sigs_cb(struct rpmsg_device *rpdev, void *priv, +u32 old, u32 new) +{ + struct rpmsg_eptdev *eptdev = priv; + + eptdev->sig_pending = true; + + /* wake up any blocking processes, waiting for signal notification */ + wake_up_interruptible(&eptdev->readq); + return 0; +} + static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) { struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); @@ -138,6 +153,7 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) return -EINVAL; } + ept->sig_cb = rpmsg_sigs_cb; eptdev->ept = ept; filp->private_data = eptdev; @@ -157,6 +173,7 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) eptdev->ept = NULL; } mutex_unlock(&eptdev->ept_lock); + eptdev->sig_pending = false; /* Discard all SKBs */ while (!skb_queue_empty(&eptdev->queue)) { @@ -263,6 +280,9 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) if (!skb_queue_empty(&eptdev->queue)) mask |= EPOLLIN | EPOLLRDNORM; + if (eptdev->sig_pending) + mask |= POLLPRI; + mask |= rpmsg_poll(eptdev->ept, filp, wait); return mask; @@ -306,6 +326,7 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, switch (cmd) { case TIOCMGET: + eptdev->sig_pending = false; ret = rpmsg_get_signals(eptdev->ept); if (ret >= 0) ret = put_user(ret, (int __user *)arg); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V4 0/4] Add TIOCM Signals support for RPMSG char devices
Glink transport support signals to exchange state notification between local and remote side clients. Adding support to send/receive the signal command and notify the clients through callback and POLL notification. Changes since v1: - Split the patches as per functional areas like core, char, glink - Add set, clear mask for TIOCMSET - Merge the char signal callback and POLLPRI patches Changes since v2: - Modify the rpmsg_get_signals function prototype Changes since v3: - Correct the TICOMGET case handling as per new rpmsg_get_signals prototype - Update the rpmsg_get_signals function header Arun Kumar Neelakantam (4): rpmsg: core: Add signal API support rpmsg: glink: Add support to handle signals command rpmsg: char: Add TIOCMGET/TIOCMSET ioctl support rpmsg: char: Add signal callback and POLLPRI support drivers/rpmsg/qcom_glink_native.c | 126 ++ drivers/rpmsg/rpmsg_char.c| 74 +- drivers/rpmsg/rpmsg_core.c| 41 + drivers/rpmsg/rpmsg_internal.h| 5 ++ include/linux/rpmsg.h | 26 5 files changed, 269 insertions(+), 3 deletions(-) -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH V5 1/5] rpmsg: glink: Use complete_all for open states
On 5/14/2020 2:29 AM, Mathieu Poirier wrote: Hi Arun, On Wed, May 13, 2020 at 10:40:02AM +0530, Arun Kumar Neelakantam wrote: From: Chris Lew The open_req and open_ack completion variables are the state variables to represet a remote channel as open. Use complete_all so there are no s/represet/represent done added in patch set 6 races with waiters and using completion_done. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 1995f5b..604f11f 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -970,7 +970,7 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return -EINVAL; } - complete(&channel->open_ack); + complete_all(&channel->open_ack); If you do this and as per the note in the comment section above completion_done(), there shouldn't be a need to call completion_done() in qcom_glink_announce_create(). Thanks, Mathieu the completion_done() check still required to avoid sending intent request on channel which only opened by remote. return 0; } @@ -1413,7 +1413,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, channel->rcid = ret; spin_unlock_irqrestore(&glink->idr_lock, flags); - complete(&channel->open_req); + complete_all(&channel->open_req); if (create_device) { rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); -- 2.7.4
Re: [PATCH V5 5/5] rpmsg: glink: unregister rpmsg device during endpoint destroy
On 5/14/2020 3:43 AM, Mathieu Poirier wrote: On Wed, May 13, 2020 at 10:40:06AM +0530, Arun Kumar Neelakantam wrote: Rpmsg device unregister is not happening if channel close is triggered from local side and causing re-registration of device failures. Unregister rpmsg device for local close in endpoint destroy path. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 0e8a28c0..fc8ef66 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1207,6 +1207,7 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) { struct glink_channel *channel = to_glink_channel(ept); struct qcom_glink *glink = channel->glink; + struct rpmsg_channel_info chinfo; unsigned long flags; spin_lock_irqsave(&channel->recv_lock, flags); @@ -1214,6 +1215,13 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) spin_unlock_irqrestore(&channel->recv_lock, flags); /* Decouple the potential rpdev from the channel */ + if (channel->rpdev) { If we proceed this way no other channel can have an rpdev. I would hope that unregistration of rpdev would be more symetrical to what is done in patch 03. Thanks, Mathieu Unregister here also required along with in qcom_glink_rx_close() otherwise if the remote open the channel again it map to stale rpmsg device. + strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = RPMSG_ADDR_ANY; + + rpmsg_unregister_device(glink->dev, &chinfo); + } channel->rpdev = NULL; qcom_glink_send_close_req(glink, channel); @@ -1477,6 +1485,7 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) rpmsg_unregister_device(glink->dev, &chinfo); } + channel->rpdev = NULL; qcom_glink_send_close_ack(glink, channel->rcid); -- 2.7.4
[PATCH V5 0/5] Add chrdev and name query support for GLINK
Add support for the GLINK rpmsg transport to register a rpmsg chrdev. This will create the rpmsg_ctrl nodes for userspace clients to open rpmsg epts. The rpmsg chrdev allocation is done by allocating a local channel which also allocates an ept. We need to add some guards against edge cases for this chrdev because it will never fully open. Changes since v5: - Re-orange the completion_done code Changes since v4: - Resending by removing approved patches Changes since v3: - Change to device_add_group for rpmsg name attr - Add patch to unregister the rpmsg device - Add patch to support compat ioctl for rpmsg char driver Changes since v2: - Revert change to make glink attribute table const Changes since v1: - Add explanation to dt-bindings commit message - Add patch complete_all the open_req/ack variables - Add patch to prevent null pointer dereference in chrdev channel release - Change chrdev allocation to use glink channel allocation - Change glink attr struct to const Arun Kumar Neelakantam (1): rpmsg: glink: unregister rpmsg device during endpoint destroy Chris Lew (4): rpmsg: glink: Use complete_all for open states rpmsg: Guard against null endpoint ops in destroy rpmsg: glink: Add support for rpmsg glink chrdev rpmsg: glink: Expose rpmsg name attr for glink drivers/rpmsg/qcom_glink_native.c | 79 +-- drivers/rpmsg/rpmsg_core.c| 2 +- 2 files changed, 77 insertions(+), 4 deletions(-) -- 2.7.4
[PATCH V6 4/5] rpmsg: glink: Expose rpmsg name attr for glink
From: Chris Lew Expose the name field as an attr so clients listening to uevents for rpmsg can identify the edge the events correspond to. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 26 ++ 1 file changed, 26 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index b85433c..2b5368b 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1574,6 +1574,26 @@ static void qcom_glink_cancel_rx_work(struct qcom_glink *glink) kfree(dcmd); } +static ssize_t rpmsg_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + const char *name; + + ret = of_property_read_string(dev->of_node, "label", &name); + if (ret < 0) + name = dev->of_node->name; + + return snprintf(buf, RPMSG_NAME_SIZE, "%s\n", name); +} +static DEVICE_ATTR_RO(rpmsg_name); + +static struct attribute *qcom_glink_attrs[] = { + &dev_attr_rpmsg_name.attr, + NULL +}; +ATTRIBUTE_GROUPS(qcom_glink); + static void qcom_glink_device_release(struct device *dev) { struct rpmsg_device *rpdev = to_rpmsg_device(dev); @@ -1638,6 +1658,12 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, idr_init(&glink->lcids); idr_init(&glink->rcids); + glink->dev->groups = qcom_glink_groups; + + ret = device_add_groups(dev, qcom_glink_groups); + if (ret) + dev_err(dev, "failed to add groups\n"); + ret = of_property_read_string(dev->of_node, "label", &glink->name); if (ret < 0) glink->name = dev->of_node->name; -- 2.7.4
[PATCH V6 5/5] rpmsg: glink: unregister rpmsg device during endpoint destroy
Rpmsg device unregister is not happening if channel close is triggered from local side and causing re-registration of device failures. Unregister rpmsg device for local close in endpoint destroy path. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 2b5368b..53b90a1 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1207,6 +1207,7 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) { struct glink_channel *channel = to_glink_channel(ept); struct qcom_glink *glink = channel->glink; + struct rpmsg_channel_info chinfo; unsigned long flags; spin_lock_irqsave(&channel->recv_lock, flags); @@ -1214,6 +1215,13 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) spin_unlock_irqrestore(&channel->recv_lock, flags); /* Decouple the potential rpdev from the channel */ + if (channel->rpdev) { + strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = RPMSG_ADDR_ANY; + + rpmsg_unregister_device(glink->dev, &chinfo); + } channel->rpdev = NULL; qcom_glink_send_close_req(glink, channel); @@ -1477,6 +1485,7 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) rpmsg_unregister_device(glink->dev, &chinfo); } + channel->rpdev = NULL; qcom_glink_send_close_ack(glink, channel->rcid); -- 2.7.4
[PATCH V6 1/5] rpmsg: glink: Use complete_all for open states
From: Chris Lew The open_req and open_ack completion variables are the state variables to represent a remote channel as open. Use complete_all so there are no races with waiters and using completion_done. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 1995f5b..ea2f33f 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -970,7 +970,7 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return -EINVAL; } - complete(&channel->open_ack); + complete_all(&channel->open_ack); return 0; } @@ -1178,7 +1178,7 @@ static int qcom_glink_announce_create(struct rpmsg_device *rpdev) __be32 *val = defaults; int size; - if (glink->intentless) +if (glink->intentless || !completion_done(&channel->open_ack)) return 0; prop = of_find_property(np, "qcom,intents", NULL); @@ -1413,7 +1413,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, channel->rcid = ret; spin_unlock_irqrestore(&glink->idr_lock, flags); - complete(&channel->open_req); + complete_all(&channel->open_req); if (create_device) { rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); -- 2.7.4
[PATCH V6 3/5] rpmsg: glink: Add support for rpmsg glink chrdev
From: Chris Lew RPMSG provides a char device interface to userspace. Probe the rpmsg chrdev channel to enable the rpmsg_ctrl device creation on glink transports. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 38 ++ 1 file changed, 38 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index ea2f33f..b85433c 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1574,6 +1574,40 @@ static void qcom_glink_cancel_rx_work(struct qcom_glink *glink) kfree(dcmd); } +static void qcom_glink_device_release(struct device *dev) +{ + struct rpmsg_device *rpdev = to_rpmsg_device(dev); + struct glink_channel *channel = to_glink_channel(rpdev->ept); + + /* Release qcom_glink_alloc_channel() reference */ + kref_put(&channel->refcount, qcom_glink_channel_release); + kfree(rpdev); +} + +static int qcom_glink_create_chrdev(struct qcom_glink *glink) +{ + struct rpmsg_device *rpdev; + struct glink_channel *channel; + + rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); + if (!rpdev) + return -ENOMEM; + + channel = qcom_glink_alloc_channel(glink, "rpmsg_chrdev"); + if (IS_ERR(channel)) { + kfree(rpdev); + return PTR_ERR(channel); + } + channel->rpdev = rpdev; + + rpdev->ept = &channel->ept; + rpdev->ops = &glink_device_ops; + rpdev->dev.parent = glink->dev; + rpdev->dev.release = qcom_glink_device_release; + + return rpmsg_chrdev_register_device(rpdev); +} + struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, struct qcom_glink_pipe *rx, @@ -1633,6 +1667,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, if (ret) return ERR_PTR(ret); + ret = qcom_glink_create_chrdev(glink); + if (ret) + dev_err(glink->dev, "failed to register chrdev\n"); + return glink; } EXPORT_SYMBOL_GPL(qcom_glink_native_probe); -- 2.7.4
[PATCH V6 2/5] rpmsg: Guard against null endpoint ops in destroy
From: Chris Lew In RPMSG GLINK the chrdev device will allocate an ept as part of the rpdev creation. This device will not register endpoint ops even though it has an allocated ept. Protect against the case where the device is being destroyed. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index e330ec4..d6c3275 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -81,7 +81,7 @@ EXPORT_SYMBOL(rpmsg_create_ept); */ void rpmsg_destroy_ept(struct rpmsg_endpoint *ept) { - if (ept) + if (ept && ept->ops) ept->ops->destroy_ept(ept); } EXPORT_SYMBOL(rpmsg_destroy_ept); -- 2.7.4
Re: [PATCH v7 2/4] soc: qcom: Add AOSS QMP driver
Hi Bjorn, On 5/1/2019 10:07 AM, Bjorn Andersson wrote: The Always On Subsystem (AOSS) Qualcomm Messaging Protocol (QMP) driver is used to communicate with the AOSS for certain side-channel requests, that are not available through the RPMh interface. The communication is a very simple synchronous mechanism of messages being written in message RAM and a doorbell in the AOSS is rung. As the AOSS has processed the message length is cleared and an interrupt is fired by the AOSS as acknowledgment. The driver exposes the QDSS clock as a clock and the low-power state associated with the remoteprocs in the system as a set of power-domains. Signed-off-by: Bjorn Andersson --- Changes since v6: - Squash the pd into the same driver as the communication, to simplify the interaction. - Representing the QDSS clocks as a clock/power domain turns out to cascade into a request to make all Coresight drivers have a secondary compatible to replace the required bus clock with a required power domain. So in v7 this is exposed as a clock instead. - Some error checking updates, as reported by Doug. drivers/soc/qcom/Kconfig | 11 + drivers/soc/qcom/Makefile| 1 + drivers/soc/qcom/qcom_aoss.c | 473 +++ 3 files changed, 485 insertions(+) create mode 100644 drivers/soc/qcom/qcom_aoss.c diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 1ee298f6bf17..3e460b334b47 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -3,6 +3,17 @@ # menu "Qualcomm SoC drivers" +config QCOM_AOSS_QMP + tristate "Qualcomm AOSS Driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on MAILBOX + select PM_GENERIC_DOMAINS + help + This driver provides the means of communicating with and controlling + the low-power state for resources related to the remoteproc + subsystems as well as controlling the debug clocks exposed by the Always On + Subsystem (AOSS) using Qualcomm Messaging Protocol (QMP). + config QCOM_COMMAND_DB bool "Qualcomm Command DB" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index ffe519b0cb66..eeb088beb15f 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 CFLAGS_rpmh-rsc.o := -I$(src) +obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c new file mode 100644 index ..f1fc26ab2e36 --- /dev/null +++ b/drivers/soc/qcom/qcom_aoss.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019, Linaro Ltd + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define QMP_DESC_MAGIC 0x0 +#define QMP_DESC_VERSION 0x4 +#define QMP_DESC_FEATURES 0x8 + +/* AOP-side offsets */ +#define QMP_DESC_UCORE_LINK_STATE 0xc +#define QMP_DESC_UCORE_LINK_STATE_ACK 0x10 +#define QMP_DESC_UCORE_CH_STATE0x14 +#define QMP_DESC_UCORE_CH_STATE_ACK0x18 +#define QMP_DESC_UCORE_MBOX_SIZE 0x1c +#define QMP_DESC_UCORE_MBOX_OFFSET 0x20 + +/* Linux-side offsets */ +#define QMP_DESC_MCORE_LINK_STATE 0x24 +#define QMP_DESC_MCORE_LINK_STATE_ACK 0x28 +#define QMP_DESC_MCORE_CH_STATE0x2c +#define QMP_DESC_MCORE_CH_STATE_ACK0x30 +#define QMP_DESC_MCORE_MBOX_SIZE 0x34 +#define QMP_DESC_MCORE_MBOX_OFFSET 0x38 + +#define QMP_STATE_UP 0x +#define QMP_STATE_DOWN 0x + +#define QMP_MAGIC 0x4d41494c +#define QMP_VERSION1 + +/* Requests are expected to be 96 bytes long */ +#define QMP_MSG_LEN96 + +/** + * struct qmp - driver state for QMP implementation + * @msgram: iomem referencing the message RAM used for communication + * @dev: reference to QMP device + * @mbox_client: mailbox client used to ring the doorbell on transmit + * @mbox_chan: mailbox channel used to ring the doorbell on transmit + * @offset: offset within @msgram where messages should be written + * @size: maximum size of the messages to be transmitted + * @event: wait_queue for synchronization with the IRQ + * @tx_lock: provides syncrhonization between multiple callers of qmp_send() + * @qdss_clk: QDSS clock hw struct + * @pd_data: genpd data + */ +struct qmp { + void __iomem *msgram; + struct device *dev; + + struct mbox_client mbox_client; + struct mbox_chan *mbox_chan; + + size_t offset; + size_t size; + + wait_queue_head_t event; + + struct mutex tx_lock; + + struct clk_hw qdss_clk; + struct genpd_onecell_data pd_data; +}; + +struct qmp_pd { + struct qmp *qmp; + struct generic_pm_domain pd; +}; + +#define t
Re: [PATCH v2 2/7] soc: qcom: Add AOSS QMP communication driver
On 1/6/2019 1:39 PM, Bjorn Andersson wrote: The AOSS QMP driver is used to communicate with the AOSS for certain side-channel requests, that are not enabled through the RPMh interface. The communication is a very simple synchronous mechanism of messages being written in message RAM and a doorbell in the AOSS is rung. As the AOSS has processed the message length is cleared and an interrupt is fired by the AOSS as acknowledgment. Signed-off-by: Bjorn Andersson --- Reviewed-by: Arun Kumar Neelakantam Changes since v1: - Skip check in send for empty TX buffer - Don't follow WARN_ON() with dev_err() - Register platform_device rather than populate based on of-children drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/aoss-qmp.c | 313 ++ include/linux/soc/qcom/aoss-qmp.h | 12 ++ 4 files changed, 335 insertions(+) create mode 100644 drivers/soc/qcom/aoss-qmp.c create mode 100644 include/linux/soc/qcom/aoss-qmp.h diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a51458022d21..dda19471057f 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -3,6 +3,15 @@ # menu "Qualcomm SoC drivers" +config QCOM_AOSS_QMP + tristate "Qualcomm AOSS Messaging Driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on MAILBOX + help + This driver provides the means for communicating with the + micro-controller in the AOSS, using QMP, to control certain resource + that are not exposed through RPMh. + config QCOM_COMMAND_DB bool "Qualcomm Command DB" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 67cb85d0373c..d0d7fdc94d9a 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 CFLAGS_rpmh-rsc.o := -I$(src) +obj-$(CONFIG_QCOM_AOSS_QMP) += aoss-qmp.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o diff --git a/drivers/soc/qcom/aoss-qmp.c b/drivers/soc/qcom/aoss-qmp.c new file mode 100644 index ..de52703b96b6 --- /dev/null +++ b/drivers/soc/qcom/aoss-qmp.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, Linaro Ltd + */ +#include +#include +#include +#include +#include +#include + +#define QMP_DESC_MAGIC 0x0 +#define QMP_DESC_VERSION 0x4 +#define QMP_DESC_FEATURES 0x8 + +#define QMP_DESC_UCORE_LINK_STATE 0xc +#define QMP_DESC_UCORE_LINK_STATE_ACK 0x10 +#define QMP_DESC_UCORE_CH_STATE0x14 +#define QMP_DESC_UCORE_CH_STATE_ACK0x18 +#define QMP_DESC_UCORE_MBOX_SIZE 0x1c +#define QMP_DESC_UCORE_MBOX_OFFSET 0x20 + +#define QMP_DESC_MCORE_LINK_STATE 0x24 +#define QMP_DESC_MCORE_LINK_STATE_ACK 0x28 +#define QMP_DESC_MCORE_CH_STATE0x2c +#define QMP_DESC_MCORE_CH_STATE_ACK0x30 +#define QMP_DESC_MCORE_MBOX_SIZE 0x34 +#define QMP_DESC_MCORE_MBOX_OFFSET 0x38 + +#define QMP_STATE_UP 0x +#define QMP_STATE_DOWN 0x + +#define QMP_MAGIC 0x4d41494c +#define QMP_VERSION1 + +/** + * struct qmp - driver state for QMP implementation + * @msgram: iomem referencing the message RAM used for communication + * @dev: reference to QMP device + * @mbox_client: mailbox client used to ring the doorbell on transmit + * @mbox_chan: mailbox channel used to ring the doorbell on transmit + * @offset: offset within @msgram where messages should be written + * @size: maximum size of the messages to be transmitted + * @event: wait_queue for synchronization with the IRQ + * @tx_lock: provides syncrhonization between multiple callers of qmp_send() + * @pd_pdev: platform device for the power-domain child device + */ +struct qmp { + void __iomem *msgram; + struct device *dev; + + struct mbox_client mbox_client; + struct mbox_chan *mbox_chan; + + size_t offset; + size_t size; + + wait_queue_head_t event; + + struct mutex tx_lock; + + struct platform_device *pd_pdev; +}; + +static void qmp_kick(struct qmp *qmp) +{ + mbox_send_message(qmp->mbox_chan, NULL); + mbox_client_txdone(qmp->mbox_chan, 0); +} + +static bool qmp_magic_valid(struct qmp *qmp) +{ + return readl(qmp->msgram + QMP_DESC_MAGIC) == QMP_MAGIC; +} + +static bool qmp_link_acked(struct qmp *qmp) +{ + return readl(qmp->msgram + QMP_DESC_MCORE_LINK_STATE_ACK) == QMP_STATE_UP; +} + +static bool qmp_mcore_channel_acked(struct qmp *qmp) +{ + return readl(qmp->msgram + QMP_DESC_MCORE_CH_STATE_ACK) == QMP_STATE_UP; +} + +static bool qmp_ucore_channel_up(struct qmp *qmp) +{ + return readl(qmp->msgram + QMP_DESC_UCORE_CH_STATE) == QMP_STATE_UP; +} + +st
[PATCH 1/5] rpmsg: glink: Add GLINK signal support for RPMSG
Add support to handle SMD signals to RPMSG over GLINK. SMD signals mimic serial protocol signals to notify of ports opening and closing. This change affects the rpmsg core, rpmsg char and glink drivers. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 80 +++ drivers/rpmsg/rpmsg_core.c| 41 drivers/rpmsg/rpmsg_internal.h| 5 +++ include/linux/rpmsg.h | 25 4 files changed, 151 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index f6988fd..ff8751b 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016-2017, Linaro Ltd + * Copyright (c) 2018, The Linux Foundation. */ #include @@ -150,6 +151,8 @@ enum { * @intent_req_lock: Synchronises multiple intent requests * @intent_req_result: Result of intent request * @intent_req_comp: Completion for intent_req signalling + * @lsigs: Local side signals + * @rsigs: Remote side signals */ struct glink_channel { struct rpmsg_endpoint ept; @@ -181,6 +184,9 @@ struct glink_channel { struct mutex intent_req_lock; bool intent_req_result; struct completion intent_req_comp; + + unsigned int lsigs; + unsigned int rsigs; }; #define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) @@ -201,6 +207,7 @@ struct glink_channel { #define RPM_CMD_TX_DATA_CONT 12 #define RPM_CMD_READ_NOTIF 13 #define RPM_CMD_RX_DONE_W_REUSE14 +#define RPM_CMD_SIGNALS15 #define GLINK_FEATURE_INTENTLESS BIT(1) @@ -954,6 +961,52 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return 0; } +/** + * qcom_glink_send_signals() - convert a signal cmd to wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @sigs: The signals to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_send_signals(struct qcom_glink *glink, + struct glink_channel *channel, + u32 sigs) +{ + struct glink_msg msg; + + msg.cmd = cpu_to_le16(RPM_CMD_SIGNALS); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(sigs); + + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static int qcom_glink_handle_signals(struct qcom_glink *glink, +unsigned int rcid, unsigned int signals) +{ + struct glink_channel *channel; + unsigned long flags; + u32 old; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "signal for non-existing channel\n"); + return -EINVAL; + } + + old = channel->rsigs; + channel->rsigs = signals; + + if (channel->ept.sig_cb) + channel->ept.sig_cb(channel->ept.rpdev, channel->ept.priv, + old, channel->rsigs); + + return 0; +} + static irqreturn_t qcom_glink_native_intr(int irq, void *data) { struct qcom_glink *glink = data; @@ -1015,6 +1068,10 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) qcom_glink_handle_intent_req_ack(glink, param1, param2); qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; + case RPM_CMD_SIGNALS: + qcom_glink_handle_signals(glink, param1, param2); + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + break; default: dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd); ret = -EINVAL; @@ -1312,6 +1369,27 @@ static int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len) return __qcom_glink_send(channel, data, len, false); } +static int qcom_glink_get_sigs(struct rpmsg_endpoint *ept, + u32 *lsigs, u32 *rsigs) +{ + struct glink_channel *channel = to_glink_channel(ept); + + *lsigs = channel->lsigs; + *rsigs = channel->rsigs; + + return 0; +} + +static int qcom_glink_set_sigs(struct rpmsg_endpoint *ept, u32 sigs) +{ + struct glink_channel *channel = to_glink_channel(ept); + struct qcom_glink *glink = channel->glink; + + channel->lsigs = sigs; + + return qcom_glink_send_signals(glink, channel, sigs); +} + /* * Finds
[PATCH 0/5] Add TIOCM Signals support for RPMSG char devices
Glink transport support signals to exchange state notification between local and remote side clients. Adding support to send/receive the signal command and notify the clients through callback and POLL notification. Arun Kumar Neelakantam (5): rpmsg: glink: Add GLINK signal support for RPMSG rpmsg: Add signal callback to rpmsg char device rpmsg: Add TIOCMGET/TIOCMSET ioctl support rpmsg: wakeup poll to notify signal update rpmsg: glink: Convert the native signals to TIOCM drivers/rpmsg/qcom_glink_native.c | 109 ++ drivers/rpmsg/rpmsg_char.c| 76 -- drivers/rpmsg/rpmsg_core.c| 41 ++ drivers/rpmsg/rpmsg_internal.h| 5 ++ include/linux/rpmsg.h | 25 + 5 files changed, 253 insertions(+), 3 deletions(-) -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH 2/5] rpmsg: Add signal callback to rpmsg char device
Register a callback to get the signal notifications from rpmsg. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 8 1 file changed, 8 insertions(+) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index a76b963..86003d5 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -5,6 +5,7 @@ * Copyright (c) 2012, PetaLogix * Copyright (c) 2011, Texas Instruments, Inc. * Copyright (c) 2011, Google, Inc. + * Copyright (c) 2018, The Linux Foundation. * * Based on rpmsg performance statistics driver by Michal Simek, which in turn * was based on TI & Google OMX rpmsg driver. @@ -120,6 +121,12 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, return 0; } +static int rpmsg_sigs_cb(struct rpmsg_device *rpdev, void *priv, +u32 old, u32 new) +{ + return 0; +} + static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) { struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); @@ -136,6 +143,7 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) return -EINVAL; } + ept->sig_cb = rpmsg_sigs_cb; eptdev->ept = ept; filp->private_data = eptdev; -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH 5/5] rpmsg: glink: Convert the native signals to TIOCM
Native signals over SMEM transport are different from Linux TIOCM signals. Add a macro to convert signals when sent or received from clients. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 29 + 1 file changed, 29 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index ff8751b..3054c86 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -211,6 +212,11 @@ struct glink_channel { #define GLINK_FEATURE_INTENTLESS BIT(1) +#define NATIVE_DTR_SIG BIT(31) +#define NATIVE_CTS_SIG BIT(30) +#define NATIVE_CD_SIG BIT(29) +#define NATIVE_RI_SIG BIT(28) + static void qcom_glink_rx_done_work(struct work_struct *work); static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, @@ -998,6 +1004,18 @@ static int qcom_glink_handle_signals(struct qcom_glink *glink, } old = channel->rsigs; + + /* convert signals from NATIVE to TIOCM */ + if (signals & NATIVE_DTR_SIG) + signals |= TIOCM_DSR; + if (signals & NATIVE_CTS_SIG) + signals |= TIOCM_CTS; + if (signals & NATIVE_CD_SIG) + signals |= TIOCM_CD; + if (signals & NATIVE_RI_SIG) + signals |= TIOCM_RI; + signals &= 0x0fff; + channel->rsigs = signals; if (channel->ept.sig_cb) @@ -1387,6 +1405,17 @@ static int qcom_glink_set_sigs(struct rpmsg_endpoint *ept, u32 sigs) channel->lsigs = sigs; + /* convert signals from TIOCM to NATIVE */ + sigs &= 0x0fff; + if (sigs & TIOCM_DTR) + sigs |= NATIVE_DTR_SIG; + if (sigs & TIOCM_RTS) + sigs |= NATIVE_CTS_SIG; + if (sigs & TIOCM_CD) + sigs |= NATIVE_CD_SIG; + if (sigs & TIOCM_RI) + sigs |= NATIVE_RI_SIG; + return qcom_glink_send_signals(glink, channel, sigs); } -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH 4/5] rpmsg: wakeup poll to notify signal update
Add support to wait on poll to get signal notifications. Send POLLPRI mask to indicate the signal change. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 14 ++ 1 file changed, 14 insertions(+) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index c983d6c..aee5561 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -64,6 +64,7 @@ struct rpmsg_ctrldev { * @queue_lock:synchronization of @queue operations * @queue: incoming message queue * @readq: wait object for incoming queue + * @sig_pending:state of signal notification */ struct rpmsg_eptdev { struct device dev; @@ -78,6 +79,8 @@ struct rpmsg_eptdev { spinlock_t queue_lock; struct sk_buff_head queue; wait_queue_head_t readq; + + atomic_t sig_pending; }; static int rpmsg_eptdev_destroy(struct device *dev, void *data) @@ -125,6 +128,12 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, static int rpmsg_sigs_cb(struct rpmsg_device *rpdev, void *priv, u32 old, u32 new) { + struct rpmsg_eptdev *eptdev = priv; + + atomic_set(&eptdev->sig_pending, 1); + + /* wake up any blocking processes, waiting for signal notification */ + wake_up_interruptible(&eptdev->readq); return 0; } @@ -164,6 +173,7 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) eptdev->ept = NULL; } mutex_unlock(&eptdev->ept_lock); + atomic_set(&eptdev->sig_pending, 0); /* Discard all SKBs */ while (!skb_queue_empty(&eptdev->queue)) { @@ -270,6 +280,9 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) if (!skb_queue_empty(&eptdev->queue)) mask |= EPOLLIN | EPOLLRDNORM; + if (atomic_read(&eptdev->sig_pending)) + mask |= POLLPRI; + mask |= rpmsg_poll(eptdev->ept, filp, wait); return mask; @@ -318,6 +331,7 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, ret = rpmsg_get_sigs(eptdev->ept, &lsigs, &rsigs); if (!ret) ret = put_user(rsigs, (u32 *)arg); + atomic_set(&eptdev->sig_pending, 0); break; case TIOCMSET: case TIOCMBIS: -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH 3/5] rpmsg: Add TIOCMGET/TIOCMSET ioctl support
Add TICOMGET and TIOCMSET ioctl support for rpmsg char device nodes to get/set the low level transport signals. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 54 +++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 86003d5..c983d6c 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -274,15 +275,62 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) return mask; } +static int rpmsg_eptdev_tiocmset(struct file *fp, unsigned int cmd, +unsigned long arg) +{ + struct rpmsg_eptdev *eptdev = fp->private_data; + u32 lsigs, rsigs, val; + int ret; + + ret = get_user(val, (u32 *)arg); + if (ret) + return ret; + + ret = rpmsg_get_sigs(eptdev->ept, &lsigs, &rsigs); + if (ret < 0) + return ret; + + switch (cmd) { + case TIOCMBIS: + lsigs |= val; + break; + case TIOCMBIC: + lsigs &= ~val; + break; + case TIOCMSET: + lsigs = val; + break; + } + + ret = rpmsg_set_sigs(eptdev->ept, lsigs); + return ret; +} + static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { struct rpmsg_eptdev *eptdev = fp->private_data; + u32 lsigs, rsigs; + int ret; - if (cmd != RPMSG_DESTROY_EPT_IOCTL) - return -EINVAL; + switch (cmd) { + case TIOCMGET: + ret = rpmsg_get_sigs(eptdev->ept, &lsigs, &rsigs); + if (!ret) + ret = put_user(rsigs, (u32 *)arg); + break; + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + ret = rpmsg_eptdev_tiocmset(fp, cmd, arg); + break; + case RPMSG_DESTROY_EPT_IOCTL: + ret = rpmsg_eptdev_destroy(&eptdev->dev, NULL); + default: + ret = -EINVAL; + } - return rpmsg_eptdev_destroy(&eptdev->dev, NULL); + return ret; } static const struct file_operations rpmsg_eptdev_fops = { -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH] rpmsg: glink: smem: Support rx peak for size less than 4 bytes
The current rx peak function fails to read the data if size is less than 4bytes. Use memcpy_fromio to support data reads of size less than 4 bytes. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_smem.c | 12 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c index 2b5cf27..7b65443 100644 --- a/drivers/rpmsg/qcom_glink_smem.c +++ b/drivers/rpmsg/qcom_glink_smem.c @@ -89,15 +89,11 @@ static void glink_smem_rx_peak(struct qcom_glink_pipe *np, tail -= pipe->native.length; len = min_t(size_t, count, pipe->native.length - tail); - if (len) { - __ioread32_copy(data, pipe->fifo + tail, - len / sizeof(u32)); - } + if (len) + memcpy_fromio(data, pipe->fifo + tail, len); - if (len != count) { - __ioread32_copy(data + len, pipe->fifo, - (count - len) / sizeof(u32)); - } + if (len != count) + memcpy_fromio(data + len, pipe->fifo, (count - len)); } static void glink_smem_rx_advance(struct qcom_glink_pipe *np, -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
[PATCH V5 4/5] rpmsg: glink: Expose rpmsg name attr for glink
From: Chris Lew Expose the name field as an attr so clients listening to uevents for rpmsg can identify the edge the events correspond to. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 26 ++ 1 file changed, 26 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 3a7f87c..0e8a28c0 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1574,6 +1574,26 @@ static void qcom_glink_cancel_rx_work(struct qcom_glink *glink) kfree(dcmd); } +static ssize_t rpmsg_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + const char *name; + + ret = of_property_read_string(dev->of_node, "label", &name); + if (ret < 0) + name = dev->of_node->name; + + return snprintf(buf, RPMSG_NAME_SIZE, "%s\n", name); +} +static DEVICE_ATTR_RO(rpmsg_name); + +static struct attribute *qcom_glink_attrs[] = { + &dev_attr_rpmsg_name.attr, + NULL +}; +ATTRIBUTE_GROUPS(qcom_glink); + static void qcom_glink_device_release(struct device *dev) { struct rpmsg_device *rpdev = to_rpmsg_device(dev); @@ -1638,6 +1658,12 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, idr_init(&glink->lcids); idr_init(&glink->rcids); + glink->dev->groups = qcom_glink_groups; + + ret = device_add_groups(dev, qcom_glink_groups); + if (ret) + dev_err(dev, "failed to add groups\n"); + ret = of_property_read_string(dev->of_node, "label", &glink->name); if (ret < 0) glink->name = dev->of_node->name; -- 2.7.4
[PATCH V5 5/5] rpmsg: glink: unregister rpmsg device during endpoint destroy
Rpmsg device unregister is not happening if channel close is triggered from local side and causing re-registration of device failures. Unregister rpmsg device for local close in endpoint destroy path. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 0e8a28c0..fc8ef66 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1207,6 +1207,7 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) { struct glink_channel *channel = to_glink_channel(ept); struct qcom_glink *glink = channel->glink; + struct rpmsg_channel_info chinfo; unsigned long flags; spin_lock_irqsave(&channel->recv_lock, flags); @@ -1214,6 +1215,13 @@ static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) spin_unlock_irqrestore(&channel->recv_lock, flags); /* Decouple the potential rpdev from the channel */ + if (channel->rpdev) { + strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = RPMSG_ADDR_ANY; + + rpmsg_unregister_device(glink->dev, &chinfo); + } channel->rpdev = NULL; qcom_glink_send_close_req(glink, channel); @@ -1477,6 +1485,7 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) rpmsg_unregister_device(glink->dev, &chinfo); } + channel->rpdev = NULL; qcom_glink_send_close_ack(glink, channel->rcid); -- 2.7.4
[PATCH V5 0/5] Add chrdev and name query support for GLINK
Add support for the GLINK rpmsg transport to register a rpmsg chrdev. This will create the rpmsg_ctrl nodes for userspace clients to open rpmsg epts. The rpmsg chrdev allocation is done by allocating a local channel which also allocates an ept. We need to add some guards against edge cases for this chrdev because it will never fully open. Changes since v4: - Resending by removing approved patches Changes since v3: - Change to device_add_group for rpmsg name attr - Add patch to unregister the rpmsg device - Add patch to support compat ioctl for rpmsg char driver Changes since v2: - Revert change to make glink attribute table const Changes since v1: - Add explanation to dt-bindings commit message - Add patch complete_all the open_req/ack variables - Add patch to prevent null pointer dereference in chrdev channel release - Change chrdev allocation to use glink channel allocation - Change glink attr struct to const Arun Kumar Neelakantam (1): rpmsg: glink: unregister rpmsg device during endpoint destroy Chris Lew (4): rpmsg: glink: Use complete_all for open states rpmsg: Guard against null endpoint ops in destroy rpmsg: glink: Add support for rpmsg glink chrdev rpmsg: glink: Expose rpmsg name attr for glink drivers/rpmsg/qcom_glink_native.c | 79 +-- drivers/rpmsg/rpmsg_core.c| 2 +- 2 files changed, 77 insertions(+), 4 deletions(-) -- 2.7.4
[PATCH V5 2/5] rpmsg: Guard against null endpoint ops in destroy
From: Chris Lew In RPMSG GLINK the chrdev device will allocate an ept as part of the rpdev creation. This device will not register endpoint ops even though it has an allocated ept. Protect against the case where the device is being destroyed. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index e330ec4..d6c3275 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -81,7 +81,7 @@ EXPORT_SYMBOL(rpmsg_create_ept); */ void rpmsg_destroy_ept(struct rpmsg_endpoint *ept) { - if (ept) + if (ept && ept->ops) ept->ops->destroy_ept(ept); } EXPORT_SYMBOL(rpmsg_destroy_ept); -- 2.7.4
[PATCH V5 3/5] rpmsg: glink: Add support for rpmsg glink chrdev
From: Chris Lew RPMSG provides a char device interface to userspace. Probe the rpmsg chrdev channel to enable the rpmsg_ctrl device creation on glink transports. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 40 ++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 604f11f..3a7f87c 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1178,7 +1178,7 @@ static int qcom_glink_announce_create(struct rpmsg_device *rpdev) __be32 *val = defaults; int size; - if (glink->intentless) + if (glink->intentless || !completion_done(&channel->open_ack)) return 0; prop = of_find_property(np, "qcom,intents", NULL); @@ -1574,6 +1574,40 @@ static void qcom_glink_cancel_rx_work(struct qcom_glink *glink) kfree(dcmd); } +static void qcom_glink_device_release(struct device *dev) +{ + struct rpmsg_device *rpdev = to_rpmsg_device(dev); + struct glink_channel *channel = to_glink_channel(rpdev->ept); + + /* Release qcom_glink_alloc_channel() reference */ + kref_put(&channel->refcount, qcom_glink_channel_release); + kfree(rpdev); +} + +static int qcom_glink_create_chrdev(struct qcom_glink *glink) +{ + struct rpmsg_device *rpdev; + struct glink_channel *channel; + + rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); + if (!rpdev) + return -ENOMEM; + + channel = qcom_glink_alloc_channel(glink, "rpmsg_chrdev"); + if (IS_ERR(channel)) { + kfree(rpdev); + return PTR_ERR(channel); + } + channel->rpdev = rpdev; + + rpdev->ept = &channel->ept; + rpdev->ops = &glink_device_ops; + rpdev->dev.parent = glink->dev; + rpdev->dev.release = qcom_glink_device_release; + + return rpmsg_chrdev_register_device(rpdev); +} + struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, struct qcom_glink_pipe *rx, @@ -1633,6 +1667,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, if (ret) return ERR_PTR(ret); + ret = qcom_glink_create_chrdev(glink); + if (ret) + dev_err(glink->dev, "failed to register chrdev\n"); + return glink; } EXPORT_SYMBOL_GPL(qcom_glink_native_probe); -- 2.7.4
[PATCH V5 1/5] rpmsg: glink: Use complete_all for open states
From: Chris Lew The open_req and open_ack completion variables are the state variables to represet a remote channel as open. Use complete_all so there are no races with waiters and using completion_done. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 1995f5b..604f11f 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -970,7 +970,7 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return -EINVAL; } - complete(&channel->open_ack); + complete_all(&channel->open_ack); return 0; } @@ -1413,7 +1413,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, channel->rcid = ret; spin_unlock_irqrestore(&glink->idr_lock, flags); - complete(&channel->open_req); + complete_all(&channel->open_req); if (create_device) { rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); -- 2.7.4
[PATCH V4 3/4] rpmsg: char: Add TIOCMGET/TIOCMSET ioctl support
Add TICOMGET and TIOCMSET ioctl support for rpmsg char device nodes to get/set the low level transport signals. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 53 +++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 4bbbacd..e2f92f3 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* + * Copyright (c) 2018, The Linux Foundation. * Copyright (c) 2016, Linaro Ltd. * Copyright (c) 2012, Michal Simek * Copyright (c) 2012, PetaLogix @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -269,15 +271,60 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) return mask; } +static int rpmsg_eptdev_tiocmset(struct file *fp, unsigned int cmd, +int __user *arg) +{ + struct rpmsg_eptdev *eptdev = fp->private_data; + u32 set, clear, val; + int ret; + + ret = get_user(val, arg); + if (ret) + return ret; + set = clear = 0; + switch (cmd) { + case TIOCMBIS: + set = val; + break; + case TIOCMBIC: + clear = val; + break; + case TIOCMSET: + set = val; + clear = ~val; + break; + } + + set &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI; + clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI; + + return rpmsg_set_signals(eptdev->ept, set, clear); +} + static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { struct rpmsg_eptdev *eptdev = fp->private_data; + int ret; - if (cmd != RPMSG_DESTROY_EPT_IOCTL) - return -EINVAL; + switch (cmd) { + case TIOCMGET: + ret = rpmsg_get_signals(eptdev->ept); + if (ret >= 0) + ret = put_user(ret, (int __user *)arg); + break; + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + ret = rpmsg_eptdev_tiocmset(fp, cmd, (int __user *)arg); + break; + case RPMSG_DESTROY_EPT_IOCTL: + ret = rpmsg_eptdev_destroy(&eptdev->dev, NULL); + default: + ret = -EINVAL; + } - return rpmsg_eptdev_destroy(&eptdev->dev, NULL); + return ret; } static const struct file_operations rpmsg_eptdev_fops = { -- 2.7.4
[PATCH V4 4/4] rpmsg: char: Add signal callback and POLLPRI support
Register a callback to get the signal notifications from rpmsg and send POLLPRI mask to indicate the signal change in POLL system call. Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_char.c | 22 ++ 1 file changed, 22 insertions(+) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index e2f92f3..ae15d4f 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -64,6 +64,7 @@ struct rpmsg_ctrldev { * @queue_lock:synchronization of @queue operations * @queue: incoming message queue * @readq: wait object for incoming queue + * @sig_pending:state of signal notification */ struct rpmsg_eptdev { struct device dev; @@ -78,6 +79,8 @@ struct rpmsg_eptdev { spinlock_t queue_lock; struct sk_buff_head queue; wait_queue_head_t readq; + + bool sig_pending; }; static int rpmsg_eptdev_destroy(struct device *dev, void *data) @@ -122,6 +125,19 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, return 0; } +static int rpmsg_sigs_cb(struct rpmsg_device *rpdev, void *priv, +u32 old, u32 new) +{ + struct rpmsg_eptdev *eptdev = priv; + + eptdev->sig_pending = true; + + /* wake up any blocking processes, waiting for signal notification */ + wake_up_interruptible(&eptdev->readq); + return 0; +} + + static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) { struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); @@ -138,6 +154,7 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) return -EINVAL; } + ept->sig_cb = rpmsg_sigs_cb; eptdev->ept = ept; filp->private_data = eptdev; @@ -156,6 +173,7 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) eptdev->ept = NULL; } mutex_unlock(&eptdev->ept_lock); + eptdev->sig_pending = false; /* Discard all SKBs */ skb_queue_purge(&eptdev->queue); @@ -266,6 +284,9 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) if (!skb_queue_empty(&eptdev->queue)) mask |= EPOLLIN | EPOLLRDNORM; + if (eptdev->sig_pending) + mask |= POLLPRI; + mask |= rpmsg_poll(eptdev->ept, filp, wait); return mask; @@ -309,6 +330,7 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, switch (cmd) { case TIOCMGET: + eptdev->sig_pending = false; ret = rpmsg_get_signals(eptdev->ept); if (ret >= 0) ret = put_user(ret, (int __user *)arg); -- 2.7.4
[PATCH V4 2/4] rpmsg: glink: Add support to handle signals command
Remote peripherals send signal notifications over glink with commandID 15. Add support to send and receive the signal command and convert the signals from NATIVE to TIOCM while receiving and vice versa while sending. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_glink_native.c | 126 ++ 1 file changed, 126 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index fc8ef66..68e039a 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* + * Copyright (c) 2018, The Linux Foundation. * Copyright (c) 2016-2017, Linaro Ltd */ @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -150,6 +152,8 @@ enum { * @intent_req_lock: Synchronises multiple intent requests * @intent_req_result: Result of intent request * @intent_req_comp: Completion for intent_req signalling + * @lsigs: local side signals + * @rsigs: remote side signals */ struct glink_channel { struct rpmsg_endpoint ept; @@ -181,6 +185,10 @@ struct glink_channel { struct mutex intent_req_lock; bool intent_req_result; struct completion intent_req_comp; + + unsigned int lsigs; + unsigned int rsigs; + }; #define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) @@ -201,9 +209,15 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops; #define RPM_CMD_TX_DATA_CONT 12 #define RPM_CMD_READ_NOTIF 13 #define RPM_CMD_RX_DONE_W_REUSE14 +#define RPM_CMD_SIGNALS15 #define GLINK_FEATURE_INTENTLESS BIT(1) +#define NATIVE_DTR_SIG BIT(31) +#define NATIVE_CTS_SIG BIT(30) +#define NATIVE_CD_SIG BIT(29) +#define NATIVE_RI_SIG BIT(28) + static void qcom_glink_rx_done_work(struct work_struct *work); static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, @@ -975,6 +989,76 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return 0; } +/** + * qcom_glink_send_signals() - convert a signal cmd to wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @sigs: The signals to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_send_signals(struct qcom_glink *glink, + struct glink_channel *channel, + u32 sigs) +{ + struct glink_msg msg; + + /* convert signals from TIOCM to NATIVE */ + sigs &= 0x0fff; + if (sigs & TIOCM_DTR) + sigs |= NATIVE_DTR_SIG; + if (sigs & TIOCM_RTS) + sigs |= NATIVE_CTS_SIG; + if (sigs & TIOCM_CD) + sigs |= NATIVE_CD_SIG; + if (sigs & TIOCM_RI) + sigs |= NATIVE_RI_SIG; + + msg.cmd = cpu_to_le16(RPM_CMD_SIGNALS); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(sigs); + + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static int qcom_glink_handle_signals(struct qcom_glink *glink, +unsigned int rcid, unsigned int signals) +{ + struct glink_channel *channel; + unsigned long flags; + u32 old; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "signal for non-existing channel\n"); + return -EINVAL; + } + + old = channel->rsigs; + + /* convert signals from NATIVE to TIOCM */ + if (signals & NATIVE_DTR_SIG) + signals |= TIOCM_DSR; + if (signals & NATIVE_CTS_SIG) + signals |= TIOCM_CTS; + if (signals & NATIVE_CD_SIG) + signals |= TIOCM_CD; + if (signals & NATIVE_RI_SIG) + signals |= TIOCM_RI; + signals &= 0x0fff; + + channel->rsigs = signals; + + if (channel->ept.sig_cb) { + channel->ept.sig_cb(channel->ept.rpdev, channel->ept.priv, + old, channel->rsigs); + } + + return 0; +} + static irqreturn_t qcom_glink_native_intr(int irq, void *data) { struct qcom_glink *glink = data; @@ -1036,6 +1120,10 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) qcom_glink_handle_intent_req_ack(glink, param1, param2); qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; + case RPM_CMD_SIGNALS: + qcom_glink_handle_sig
[PATCH V4 1/4] rpmsg: core: Add signal API support
Some transports like Glink support the state notifications between clients using signals similar to serial protocol signals. Signed-off-by: Chris Lew Signed-off-by: Arun Kumar Neelakantam --- drivers/rpmsg/rpmsg_core.c | 41 + drivers/rpmsg/rpmsg_internal.h | 5 + include/linux/rpmsg.h | 26 ++ 3 files changed, 72 insertions(+) diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index d6c3275..453790b 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -2,6 +2,7 @@ /* * remote processor messaging bus * + * Copyright (c) 2018, The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * @@ -283,6 +284,42 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, } EXPORT_SYMBOL(rpmsg_trysend_offchannel); +/** + * rpmsg_get_signals() - get the signals for this endpoint + * @ept: the rpmsg endpoint + * + * Returns signal bits on success and an appropriate error value on failure. + */ +int rpmsg_get_signals(struct rpmsg_endpoint *ept) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->get_signals) + return -EOPNOTSUPP; + + return ept->ops->get_signals(ept); +} +EXPORT_SYMBOL(rpmsg_get_signals); + +/** + * rpmsg_set_signals() - set the remote signals for this endpoint + * @ept: the rpmsg endpoint + * @set: set mask for signals + * @clear: clear mask for signals + * + * Returns 0 on success and an appropriate error value on failure. + */ +int rpmsg_set_signals(struct rpmsg_endpoint *ept, u32 set, u32 clear) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->set_signals) + return -EOPNOTSUPP; + + return ept->ops->set_signals(ept, set, clear); +} +EXPORT_SYMBOL(rpmsg_set_signals); + /* * match an rpmsg channel with a channel info struct. * this is used to make sure we're not creating rpmsg devices for channels @@ -468,6 +505,10 @@ static int rpmsg_dev_probe(struct device *dev) rpdev->ept = ept; rpdev->src = ept->addr; + + if (rpdrv->signals) + ept->sig_cb = rpdrv->signals; + } err = rpdrv->probe(rpdev); diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index 3fc83cd..8958d6c 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -2,6 +2,7 @@ /* * remote processor messaging bus internals * + * Copyright (c) 2018, The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * @@ -47,6 +48,8 @@ struct rpmsg_device_ops { * @trysendto: see @rpmsg_trysendto(), optional * @trysend_offchannel:see @rpmsg_trysend_offchannel(), optional * @poll: see @rpmsg_poll(), optional + * @get_signals: see @rpmsg_get_signals(), optional + * @set_signals: see @rpmsg_set_signals(), optional * * Indirection table for the operations that a rpmsg backend should implement. * In addition to @destroy_ept, the backend must at least implement @send and @@ -66,6 +69,8 @@ struct rpmsg_endpoint_ops { void *data, int len); __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); + int (*get_signals)(struct rpmsg_endpoint *ept); + int (*set_signals)(struct rpmsg_endpoint *ept, u32 set, u32 clear); }; int rpmsg_register_device(struct rpmsg_device *rpdev); diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 9fe156d..48c8ae3 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -2,6 +2,7 @@ /* * Remote processor messaging * + * Copyright (c) 2018 The Linux Foundation. * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * All rights reserved. @@ -60,6 +61,7 @@ struct rpmsg_device { }; typedef int (*rpmsg_rx_cb_t)(struct rpmsg_device *, void *, int, void *, u32); +typedef int (*rpmsg_rx_sig_t)(struct rpmsg_device *, void *, u32, u32); /** * struct rpmsg_endpoint - binds a local rpmsg address to its user @@ -67,6 +69,7 @@ typedef int (*rpmsg_rx_cb_t)(struct rpmsg_device *, void *, int, void *, u32); * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler * @cb_lock: must be taken before accessing/changing @cb + * @sig_cb: rx serial signal handler * @addr: local rpmsg address * @priv: private data for the driver's use * @@ -89,6 +92,7 @@ struct rpmsg_endpoint { struct kref refcount; rpmsg_rx_cb_t cb; struct mutex cb_lock; + rpmsg_rx_sig_t sig_cb; u32 addr; void *priv; @@ -102,6 +106,7 @@ struct rpmsg_endpoint { * @probe: invoked when a matching rpmsg chann
[RESEND PATCH V4 0/4] Add TIOCM Signals support for RPMSG char devices
Glink transport support signals to exchange state notification between local and remote side clients. Adding support to send/receive the signal command and notify the clients through callback and POLL notification. Changes since v3: - Correct the TICOMGET case handling as per new rpmsg_get_signals prototype - Update the rpmsg_get_signals function header Changes since v2: - Modify the rpmsg_get_signals function prototype Changes since v1: - Split the patches as per functional areas like core, char, glink - Add set, clear mask for TIOCMSET - Merge the char signal callback and POLLPRI patches Arun Kumar Neelakantam (4): rpmsg: core: Add signal API support rpmsg: glink: Add support to handle signals command rpmsg: char: Add TIOCMGET/TIOCMSET ioctl support rpmsg: char: Add signal callback and POLLPRI support drivers/rpmsg/qcom_glink_native.c | 126 ++ drivers/rpmsg/rpmsg_char.c| 75 ++- drivers/rpmsg/rpmsg_core.c| 41 + drivers/rpmsg/rpmsg_internal.h| 5 ++ include/linux/rpmsg.h | 26 5 files changed, 270 insertions(+), 3 deletions(-) -- 2.7.4
Re: [PATCH] rpmsg: qcom_smd: Access APCS through mailbox framework
On 11/16/2017 12:38 PM, Bjorn Andersson wrote: Attempt to acquire the APCS IPC through the mailbox framework and fall back to the old syscon based approach, to allow us to move away from using the syscon. Signed-off-by: Bjorn Andersson Reviewed-by: Arun Kumar Neelakantam --- drivers/rpmsg/qcom_smd.c | 62 +--- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c index b01774e9fac0..ef2a526ebc8f 100644 --- a/drivers/rpmsg/qcom_smd.c +++ b/drivers/rpmsg/qcom_smd.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -107,6 +108,8 @@ static const struct { * @ipc_regmap: regmap handle holding the outgoing ipc register * @ipc_offset: offset within @ipc_regmap of the register for ipc * @ipc_bit: bit in the register at @ipc_offset of @ipc_regmap + * @mbox_client: mailbox client handle + * @mbox_chan: apcs ipc mailbox channel handle * @channels: list of all channels detected on this edge * @channels_lock:guard for modifications of @channels * @allocated:array of bitmaps representing already allocated channels @@ -129,6 +132,9 @@ struct qcom_smd_edge { int ipc_offset; int ipc_bit; + struct mbox_client mbox_client; + struct mbox_chan *mbox_chan; + struct list_head channels; spinlock_t channels_lock; @@ -365,7 +371,12 @@ static void qcom_smd_signal_channel(struct qcom_smd_channel *channel) { struct qcom_smd_edge *edge = channel->edge; - regmap_write(edge->ipc_regmap, edge->ipc_offset, BIT(edge->ipc_bit)); + if (edge->mbox_chan) { + mbox_send_message(edge->mbox_chan, NULL); + mbox_client_txdone(edge->mbox_chan, 0); + } else { + regmap_write(edge->ipc_regmap, edge->ipc_offset, BIT(edge->ipc_bit)); + } } /* @@ -1268,27 +1279,37 @@ static int qcom_smd_parse_edge(struct device *dev, key = "qcom,remote-pid"; of_property_read_u32(node, key, &edge->remote_pid); - syscon_np = of_parse_phandle(node, "qcom,ipc", 0); - if (!syscon_np) { - dev_err(dev, "no qcom,ipc node\n"); - return -ENODEV; - } + edge->mbox_client.dev = dev; + edge->mbox_client.knows_txdone = true; + edge->mbox_chan = mbox_request_channel(&edge->mbox_client, 0); + if (IS_ERR(edge->mbox_chan)) { + if (PTR_ERR(edge->mbox_chan) != -ENODEV) + return PTR_ERR(edge->mbox_chan); - edge->ipc_regmap = syscon_node_to_regmap(syscon_np); - if (IS_ERR(edge->ipc_regmap)) - return PTR_ERR(edge->ipc_regmap); + edge->mbox_chan = NULL; - key = "qcom,ipc"; - ret = of_property_read_u32_index(node, key, 1, &edge->ipc_offset); - if (ret < 0) { - dev_err(dev, "no offset in %s\n", key); - return -EINVAL; - } + syscon_np = of_parse_phandle(node, "qcom,ipc", 0); + if (!syscon_np) { + dev_err(dev, "no qcom,ipc node\n"); + return -ENODEV; + } - ret = of_property_read_u32_index(node, key, 2, &edge->ipc_bit); - if (ret < 0) { - dev_err(dev, "no bit in %s\n", key); - return -EINVAL; + edge->ipc_regmap = syscon_node_to_regmap(syscon_np); + if (IS_ERR(edge->ipc_regmap)) + return PTR_ERR(edge->ipc_regmap); + + key = "qcom,ipc"; + ret = of_property_read_u32_index(node, key, 1, &edge->ipc_offset); + if (ret < 0) { + dev_err(dev, "no offset in %s\n", key); + return -EINVAL; + } + + ret = of_property_read_u32_index(node, key, 2, &edge->ipc_bit); + if (ret < 0) { + dev_err(dev, "no bit in %s\n", key); + return -EINVAL; + } } ret = of_property_read_string(node, "label", &edge->name); @@ -1394,6 +1415,8 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, return edge; unregister_dev: + if (!IS_ERR_OR_NULL(edge->mbox_chan)) + mbox_free_channel(edge->mbox_chan); put_device(&edge->dev); return ERR_PTR(ret); } @@ -1422,6 +1445,7 @@ int qcom_smd_unregister_edge(struct qcom_smd_edge *edge) if (ret) dev_warn(&edge->dev, "can't remove smd device: %d\n", ret); + mbox_free_channel(edge->mbox_chan); device_unregister(&edge->dev); return 0;
Re: [PATCH] soc: qcom: smp2p: Access APCS as mailbox client
On 11/16/2017 12:48 PM, Bjorn Andersson wrote: Attempt to acquire the APCS IPC through the mailbox framework and fall back to the old syscon based approach, to allow us to move away from using the syscon. Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 1 + drivers/soc/qcom/smp2p.c | 38 -- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index b00bccddcd3b..e67d9534fca0 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -64,6 +64,7 @@ config QCOM_SMEM_STATE config QCOM_SMP2P tristate "Qualcomm Shared Memory Point to Point support" + depends on MAILBOX depends on QCOM_SMEM select QCOM_SMEM_STATE help diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index f51fb2ea7200..7fdbb64142a0 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -126,6 +127,8 @@ struct smp2p_entry { * @ipc_regmap: regmap for the outbound ipc * @ipc_offset: offset within the regmap * @ipc_bit: bit in regmap@offset to kick to signal remote processor + * @mbox_client: mailbox client handle + * @mbox_chan: apcs ipc mailbox channel handle * @inbound: list of inbound entries * @outbound: list of outbound entries */ @@ -146,6 +149,9 @@ struct qcom_smp2p { int ipc_offset; int ipc_bit; + struct mbox_client mbox_client; + struct mbox_chan *mbox_chan; + struct list_head inbound; struct list_head outbound; }; @@ -154,7 +160,13 @@ static void qcom_smp2p_kick(struct qcom_smp2p *smp2p) { /* Make sure any updated data is written before the kick */ wmb(); - regmap_write(smp2p->ipc_regmap, smp2p->ipc_offset, BIT(smp2p->ipc_bit)); + + if (smp2p->mbox_chan) { + mbox_send_message(smp2p->mbox_chan, NULL); + mbox_client_txdone(smp2p->mbox_chan, 0); + } else { + regmap_write(smp2p->ipc_regmap, smp2p->ipc_offset, BIT(smp2p->ipc_bit)); + } } /** @@ -453,10 +465,6 @@ static int qcom_smp2p_probe(struct platform_device *pdev) platform_set_drvdata(pdev, smp2p); - ret = smp2p_parse_ipc(smp2p); - if (ret) - return ret; - key = "qcom,smem"; ret = of_property_read_u32_array(pdev->dev.of_node, key, smp2p->smem_items, 2); @@ -483,9 +491,22 @@ static int qcom_smp2p_probe(struct platform_device *pdev) return irq; } + smp2p->mbox_client.dev = &pdev->dev; + smp2p->mbox_chan = mbox_request_channel(&smp2p->mbox_client, 0); any reason why we are not using "knows_txdone = true" for SMP2P but using for only SMD drivers + if (IS_ERR(smp2p->mbox_chan)) { + if (PTR_ERR(smp2p->mbox_chan) != -ENODEV) + return PTR_ERR(smp2p->mbox_chan); + + smp2p->mbox_chan = NULL; + + ret = smp2p_parse_ipc(smp2p); + if (ret) + return ret; + } + ret = qcom_smp2p_alloc_outbound_item(smp2p); if (ret < 0) - return ret; + goto release_mbox; for_each_available_child_of_node(pdev->dev.of_node, node) { entry = devm_kzalloc(&pdev->dev, sizeof(*entry), GFP_KERNEL); @@ -540,6 +561,9 @@ static int qcom_smp2p_probe(struct platform_device *pdev) smp2p->out->valid_entries = 0; +release_mbox: + mbox_free_channel(smp2p->mbox_chan); + return ret; } @@ -554,6 +578,8 @@ static int qcom_smp2p_remove(struct platform_device *pdev) list_for_each_entry(entry, &smp2p->outbound, node) qcom_smem_state_unregister(entry->state); + mbox_free_channel(smp2p->mbox_chan); + smp2p->out->valid_entries = 0; return 0;
Re: [PATCH 10/18] rpmsg: glink: Add support for TX intents
On 8/16/2017 10:49 PM, Sricharan R wrote: Intents are nothing but pre-allocated buffers of appropriate size that are allocated on the local side and communicated to the remote side and the remote stores the list of intent ids that it is informed. Later when remote side is intenting to send data, it picks up a right intent (based on the size) and sends the data buffer and the intent id. Local side receives the data and copies it to the local intent buffer. The whole idea is to avoid stalls on the transport for allocating memory, used for copy based transports. When the remote request to allocate buffers using CMD_RX_INTENT_REQ, we allocate buffers of requested size, store the buffer id locally and also communicate the intent id to the remote. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson --- drivers/rpmsg/qcom_glink_native.c | 161 +- drivers/rpmsg/qcom_glink_native.h | 3 +- drivers/rpmsg/qcom_glink_rpm.c| 3 +- drivers/rpmsg/qcom_glink_smem.c | 5 +- 4 files changed, 167 insertions(+), 5 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index acf5558..cbc9f9e 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -60,6 +60,25 @@ struct glink_defer_cmd { }; /** + * RX intent + * + * data: pointer to the data (may be NULL for zero-copy) + * id: remote or local intent ID + * size: size of the original intent (do not modify) + * reuse: To mark if the intent can be reused after first use + * in_use: To mark if intent is already in use for the channel + * offset: next write offset (initially 0) + */ +struct glink_core_rx_intent { + void *data; + u32 id; + size_t size; + bool reuse; + bool in_use; + u32 offset; +}; + +/** * struct glink_rpm - driver context, relates to one remote subsystem * @dev: reference to the associated struct device * @doorbell: "rpm_hlos" ipc doorbell @@ -116,6 +135,8 @@ enum { * @name: unique channel name/identifier * @lcid: channel id, in local space * @rcid: channel id, in remote space + * @intent_lock: lock for protection of @liids + * @liids: idr of all local intents * @buf: receive buffer, for gathering fragments * @buf_offset: write offset in @buf * @buf_size: size of current @buf @@ -136,6 +157,9 @@ struct glink_channel { unsigned int lcid; unsigned int rcid; + spinlock_t intent_lock; + struct idr liids; + void *buf; int buf_offset; int buf_size; @@ -153,6 +177,9 @@ struct glink_channel { #define RPM_CMD_OPEN 2 #define RPM_CMD_CLOSE 3 #define RPM_CMD_OPEN_ACK 4 +#define RPM_CMD_INTENT 5 +#define RPM_CMD_RX_INTENT_REQ 7 +#define RPM_CMD_RX_INTENT_REQ_ACK 8 #define RPM_CMD_TX_DATA 9 #define RPM_CMD_CLOSE_ACK 11 #define RPM_CMD_TX_DATA_CONT 12 @@ -177,6 +204,7 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, init_completion(&channel->open_req); init_completion(&channel->open_ack); + idr_init(&channel->liids); spinlock intent_lock initialization is missed ? kref_init(&channel->refcount); return channel; @@ -187,6 +215,7 @@ static void qcom_glink_channel_release(struct kref *ref) struct glink_channel *channel = container_of(ref, struct glink_channel, refcount); + idr_destroy(&channel->liids); idr_destroy shouldn`t be covered by intent_lock ? kfree(channel->name); kfree(channel); } @@ -423,6 +452,130 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink, } } +/** + * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to + wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @granted: The request response to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink, + struct glink_channel *channel, + bool granted) +{ + struct glink_msg msg; + + msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(granted); + + qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); + + return 0; +} + +/** + * tx_cmd_local_rx_intent() - convert an rx intent cmd to wire format and + *transmit copy-paste mistake + * @glink: The transport to transmit on. + * @channel: The local channel + * @size: The intent to pass on to remote. + * + * Return: 0 on success or standard Linux er
Re: [PATCH 11/18] rpmsg: glink: Use the local intents when receiving data
On 8/16/2017 10:49 PM, Sricharan R wrote: So previously on request from remote side, we allocated local intent buffers and passed the ids to the remote. Now when we receive data buffers from remote directed to that intent id, copy the data to the corresponding preallocated intent buffer. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson --- drivers/rpmsg/qcom_glink_native.c | 75 ++- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index cbc9f9e..d6aa589 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -160,7 +160,7 @@ struct glink_channel { spinlock_t intent_lock; struct idr liids; - void *buf; + struct glink_core_rx_intent *buf; int buf_offset; int buf_size; @@ -607,6 +607,7 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) { + struct glink_core_rx_intent *intent; struct glink_channel *channel; struct { struct glink_msg msg; @@ -616,6 +617,8 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) unsigned int chunk_size; unsigned int left_size; unsigned int rcid; + unsigned int liid; + int ret = 0; unsigned long flags; if (avail < sizeof(hdr)) { @@ -643,56 +646,78 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) dev_dbg(glink->dev, "Data on non-existing channel\n"); /* Drop the message */ - qcom_glink_rx_advance(glink, - ALIGN(sizeof(hdr) + chunk_size, 8)); - return 0; + goto advance_rx; } - /* Might have an ongoing, fragmented, message to append */ - if (!channel->buf) { - channel->buf = kmalloc(chunk_size + left_size, GFP_ATOMIC); - if (!channel->buf) - return -ENOMEM; + if (glink->intentless) { + /* Might have an ongoing, fragmented, message to append */ + if (!channel->buf) { + intent = kzalloc(sizeof(*intent), GFP_ATOMIC); + if (!intent) + return -ENOMEM; + + intent->data = kmalloc(chunk_size + left_size, + GFP_ATOMIC); Who is supposed to free the intent and intent->data memory ? + if (!intent->data) { + kfree(intent); + return -ENOMEM; + } + + intent->id = 0xbabababa; + intent->size = chunk_size + left_size; + intent->offset = 0; + + channel->buf = intent; + } else { + intent = channel->buf; + } + } else { + liid = le32_to_cpu(hdr.msg.param2); - channel->buf_size = chunk_size + left_size; - channel->buf_offset = 0; - } + spin_lock_irqsave(&channel->intent_lock, flags); + intent = idr_find(&channel->liids, liid); + spin_unlock_irqrestore(&channel->intent_lock, flags); - qcom_glink_rx_advance(glink, sizeof(hdr)); + if (!intent) { + dev_err(glink->dev, + "no intent found for channel %s intent %d", + channel->name, liid); + goto advance_rx; + } + } - if (channel->buf_size - channel->buf_offset < chunk_size) { - dev_err(glink->dev, "Insufficient space in input buffer\n"); + if (intent->size - intent->offset < chunk_size) { + dev_err(glink->dev, "Insufficient space in intent\n"); /* The packet header lied, drop payload */ - qcom_glink_rx_advance(glink, chunk_size); - return -ENOMEM; + goto advance_rx; } - qcom_glink_rx_peak(glink, channel->buf + channel->buf_offset, + qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr), 8)); + qcom_glink_rx_peak(glink, intent->data + intent->offset, chunk_size); - channel->buf_offset += chunk_size; + intent->offset += chunk_size; /* Handle message when no fragments remain to be received */ if (!left_size) { spin_lock(&channel->recv_lock); if (channel->ept.cb) { channel->ept.cb(channel->ept.rpdev, - channel->buf, - channel->buf_offset, + intent->data, + in
Re: [PATCH 13/18] rpmsg: glink: Add rx done command
On 8/16/2017 10:49 PM, Sricharan R wrote: Send RX data receive ack to remote and also inform that local intent buffer is used and freed. This informs the remote to request for next set of intent buffers before doing a send operation. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson --- drivers/rpmsg/qcom_glink_native.c | 83 +++ 1 file changed, 83 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index b2a583a..b8db74a 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -76,6 +76,8 @@ struct glink_core_rx_intent { bool reuse; bool in_use; u32 offset; + + struct list_head node; }; /** @@ -137,6 +139,8 @@ enum { * @rcid: channel id, in remote space * @intent_lock: lock for protection of @liids * @liids:idr of all local intents + * @intent_work: worker responsible for transmitting rx_done packets + * @done_intents: list of intents that needs to be announced rx_done * @buf: receive buffer, for gathering fragments * @buf_offset: write offset in @buf * @buf_size: size of current @buf @@ -159,6 +163,8 @@ struct glink_channel { spinlock_t intent_lock; struct idr liids; + struct work_struct intent_work; + struct list_head done_intents; struct glink_core_rx_intent *buf; int buf_offset; @@ -178,15 +184,19 @@ struct glink_channel { #define RPM_CMD_CLOSE 3 #define RPM_CMD_OPEN_ACK 4 #define RPM_CMD_INTENT5 +#define RPM_CMD_RX_DONE6 #define RPM_CMD_RX_INTENT_REQ 7 #define RPM_CMD_RX_INTENT_REQ_ACK 8 #define RPM_CMD_TX_DATA 9 #define RPM_CMD_CLOSE_ACK 11 #define RPM_CMD_TX_DATA_CONT 12 #define RPM_CMD_READ_NOTIF13 +#define RPM_CMD_RX_DONE_W_REUSE14 #define GLINK_FEATURE_INTENTLESS BIT(1) +static void qcom_glink_rx_done_work(struct work_struct *work); + static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, const char *name) { @@ -198,12 +208,16 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, /* Setup glink internal glink_channel data */ spin_lock_init(&channel->recv_lock); + channel->glink = glink; channel->name = kstrdup(name, GFP_KERNEL); init_completion(&channel->open_req); init_completion(&channel->open_ack); + INIT_LIST_HEAD(&channel->done_intents); + INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work); + idr_init(&channel->liids); kref_init(&channel->refcount); @@ -395,6 +409,70 @@ static void qcom_glink_send_close_ack(struct qcom_glink *glink, qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true); } +static void qcom_glink_rx_done_work(struct work_struct *work) +{ + struct glink_channel *channel = container_of(work, struct glink_channel, +intent_work); + struct qcom_glink *glink = channel->glink; + struct glink_core_rx_intent *intent, *tmp; + struct { + u16 id; + u16 lcid; + u32 liid; + } __packed cmd; + + unsigned int cid = channel->lcid; + unsigned int iid; + bool reuse; + unsigned long flags; + + spin_lock_irqsave(&channel->intent_lock, flags); + list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) { + list_del(&intent->node); + spin_unlock_irqrestore(&channel->intent_lock, flags); + iid = intent->id; + reuse = intent->reuse; + + cmd.id = reuse ? RPM_CMD_RX_DONE_W_REUSE : RPM_CMD_RX_DONE; + cmd.lcid = cid; + cmd.liid = iid; + + qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); + if (!reuse) { + kfree(intent->data); + kfree(intent); + } + spin_lock_irqsave(&channel->intent_lock, flags); + } + spin_unlock_irqrestore(&channel->intent_lock, flags); +} + +static void qcom_glink_rx_done(struct qcom_glink *glink, + struct glink_channel *channel, + struct glink_core_rx_intent *intent) +{ + /* We don't send RX_DONE to intentless systems */ + if (glink->intentless) { + kfree(intent->data); + kfree(intent); + return; + } + + /* Take it off the tree of receive intents */ + if (!intent->reuse) { + spin_lock(&channel->intent_lock); + idr_remove(&channel->liids, intent->id); + spin_unlock(&channel->intent_lock); + } +
Re: [PATCH 15/18] rpmsg: glink: Receive and store the remote intent buffers
On 8/16/2017 10:49 PM, Sricharan R wrote: Just like we allocating and sending intent ids to remote, remote side allocates and sends us the intents as well. So save the intent ids and use it later while sending data targeting the appropriate intents based on the size. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson --- drivers/rpmsg/qcom_glink_native.c | 69 +++ 1 file changed, 69 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index c111046..738303e 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -139,6 +139,7 @@ enum { * @rcid: channel id, in remote space * @intent_lock: lock for protection of @liids * @liids:idr of all local intents + * @riids: idr of all remote intents * @intent_work: worker responsible for transmitting rx_done packets * @done_intents: list of intents that needs to be announced rx_done * @buf: receive buffer, for gathering fragments @@ -163,6 +164,7 @@ struct glink_channel { spinlock_t intent_lock; struct idr liids; + struct idr riids; struct work_struct intent_work; struct list_head done_intents; @@ -219,6 +221,7 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work); idr_init(&channel->liids); + idr_init(&channel->riids); kref_init(&channel->refcount); return channel; @@ -230,6 +233,7 @@ static void qcom_glink_channel_release(struct kref *ref) refcount); idr_destroy(&channel->liids); + idr_destroy(&channel->riids); kfree(channel->name); kfree(channel); } @@ -799,6 +803,68 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) return ret; } +static void qcom_glink_handle_intent(struct qcom_glink *glink, +unsigned int cid, +unsigned int count, +size_t avail) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + struct intent_pair { + __le32 size; + __le32 iid; + }; + + struct { + struct glink_msg msg; + struct intent_pair intents[]; + } __packed *msg; + + const size_t msglen = sizeof(*msg) + sizeof(struct intent_pair) * count; + int ret; + int i; + unsigned long flags; + + if (avail < msglen) { + dev_dbg(glink->dev, "Not enough data in fifo\n"); + return; + } + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "intents for non-existing channel\n"); + return; + } + + msg = kmalloc(msglen, GFP_ATOMIC); Why kmalloc() instead of kzalloc() ? + if (!msg) + return; + + qcom_glink_rx_peak(glink, msg, 0, msglen); + + for (i = 0; i < count; ++i) { + intent = kzalloc(sizeof(*intent), GFP_ATOMIC); + if (!intent) + break; + + intent->id = le32_to_cpu(msg->intents[i].iid); + intent->size = le32_to_cpu(msg->intents[i].size); + + spin_lock_irqsave(&channel->intent_lock, flags); + ret = idr_alloc(&channel->riids, intent, + intent->id, intent->id + 1, GFP_ATOMIC); + spin_unlock_irqrestore(&channel->intent_lock, flags); + + if (ret < 0) + dev_err(glink->dev, "failed to store remote intent\n"); + } + + kfree(msg); + qcom_glink_rx_advance(glink, ALIGN(msglen, 8)); +} + static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) { struct glink_channel *channel; @@ -864,6 +930,9 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) ret = 0; break; + case RPM_CMD_INTENT: + qcom_glink_handle_intent(glink, param1, param2, avail); + break; default: qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd);
Re: [PATCH 13/18] rpmsg: glink: Add rx done command
On 8/22/2017 7:46 PM, Sricharan R wrote: Hi, +Â Â Â /* Take it off the tree of receive intents */ +Â Â Â if (!intent->reuse) { +Â Â Â spin_lock(&channel->intent_lock); +Â Â Â idr_remove(&channel->liids, intent->id); +Â Â Â spin_unlock(&channel->intent_lock); +Â Â Â } + +Â Â Â /* Schedule the sending of a rx_done indication */ +Â Â Â spin_lock(&channel->intent_lock); +Â Â Â list_add_tail(&intent->node, &channel->done_intents); +Â Â Â spin_unlock(&channel->intent_lock); + +Â Â Â schedule_work(&channel->intent_work); Adding one more parallel path will hit performance, if this worker could not get CPU cycles or blocked by other RT or HIGH_PRIO worker on global worker pool. The idea is, by design to have parallel non-blocking paths for rx and tx (that is done as a part of rx by sending the rx_done command), otherwise trying to send the rx_done command in the rx isr context is a problem since the tx can wait for the FIFO space and in worst case, can even lead to a potential deadlock if both the local and remote try the same. Having said that, instead of queuing this work in to the global queue, this can be put in to a local glink edge owned queue (or) a threaded isr ?, downstream does the rx_done in a client specific worker. Yes, mixing RX and TX path will cause dead lock. I am okay to use specific queue with HIGH_PRIO or a threaded isr. down stream uses both client specific worker and client RX cb [this mix the TX and RX path] which want to avoid. Regards, Sricharan
Re: [PATCH v2 01/20] rpmsg: glink: Rename glink_rpm_xx functions to qcom_glink_xx
On 8/24/2017 12:51 PM, Sricharan R wrote: From: Bjorn Andersson Renaming the glink_rpm_xx functions and structs to qcom_glink_xx equivalents helps to reuse the core glink protocol while adding support for smem based glink transport in the later patches. Signed-off-by: Bjorn Andersson Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Reagrds Arun N --- drivers/rpmsg/qcom_glink_rpm.c | 248 + 1 file changed, 128 insertions(+), 120 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c index 3559a3e..56a0a66 100644 --- a/drivers/rpmsg/qcom_glink_rpm.c +++ b/drivers/rpmsg/qcom_glink_rpm.c @@ -101,7 +101,7 @@ struct glink_defer_cmd { * @lcids:idr of all channels with a known local channel id * @rcids:idr of all channels with a known remote channel id */ -struct glink_rpm { +struct qcom_glink { struct device *dev; struct mbox_client mbox_client; @@ -134,7 +134,7 @@ enum { * struct glink_channel - internal representation of a channel * @rpdev:rpdev reference, only used for primary endpoints * @ept: rpmsg endpoint this channel is associated with - * @glink: glink_rpm context handle + * @glink: qcom_glink context handle * @refcount: refcount for the channel object * @recv_lock:guard for @ept.cb * @name: unique channel name/identifier @@ -150,7 +150,7 @@ struct glink_channel { struct rpmsg_endpoint ept; struct rpmsg_device *rpdev; - struct glink_rpm *glink; + struct qcom_glink *glink; struct kref refcount; @@ -184,8 +184,8 @@ struct glink_channel { #define GLINK_FEATURE_INTENTLESS BIT(1) -static struct glink_channel *glink_rpm_alloc_channel(struct glink_rpm *glink, -const char *name) +static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, + const char *name) { struct glink_channel *channel; @@ -206,7 +206,7 @@ static struct glink_channel *glink_rpm_alloc_channel(struct glink_rpm *glink, return channel; } -static void glink_rpm_channel_release(struct kref *ref) +static void qcom_glink_channel_release(struct kref *ref) { struct glink_channel *channel = container_of(ref, struct glink_channel, refcount); @@ -215,7 +215,7 @@ static void glink_rpm_channel_release(struct kref *ref) kfree(channel); } -static size_t glink_rpm_rx_avail(struct glink_rpm *glink) +static size_t qcom_glink_rx_avail(struct qcom_glink *glink) { struct glink_rpm_pipe *pipe = &glink->rx_pipe; unsigned int head; @@ -230,8 +230,8 @@ static size_t glink_rpm_rx_avail(struct glink_rpm *glink) return head - tail; } -static void glink_rpm_rx_peak(struct glink_rpm *glink, - void *data, size_t count) +static void qcom_glink_rx_peak(struct qcom_glink *glink, + void *data, size_t count) { struct glink_rpm_pipe *pipe = &glink->rx_pipe; unsigned int tail; @@ -251,8 +251,8 @@ static void glink_rpm_rx_peak(struct glink_rpm *glink, } } -static void glink_rpm_rx_advance(struct glink_rpm *glink, -size_t count) +static void qcom_glink_rx_advance(struct qcom_glink *glink, + size_t count) { struct glink_rpm_pipe *pipe = &glink->rx_pipe; unsigned int tail; @@ -266,7 +266,7 @@ static void glink_rpm_rx_advance(struct glink_rpm *glink, writel(tail, pipe->tail); } -static size_t glink_rpm_tx_avail(struct glink_rpm *glink) +static size_t qcom_glink_tx_avail(struct qcom_glink *glink) { struct glink_rpm_pipe *pipe = &glink->tx_pipe; unsigned int head; @@ -281,9 +281,9 @@ static size_t glink_rpm_tx_avail(struct glink_rpm *glink) return tail - head; } -static unsigned int glink_rpm_tx_write(struct glink_rpm *glink, - unsigned int head, - const void *data, size_t count) +static unsigned int qcom_glink_tx_write(struct qcom_glink *glink, + unsigned int head, + const void *data, size_t count) { struct glink_rpm_pipe *pipe = &glink->tx_pipe; size_t len; @@ -306,8 +306,8 @@ static unsigned int glink_rpm_tx_write(struct glink_rpm *glink, return head; } -static int glink_rpm_tx(struct glink_rpm *glink, - const void *hdr, size_t hlen, +static int qcom_glink_tx(struct qcom_glink *glink, +const void *hdr, size_t hlen, const void *data, size_t dlen, bool wait) { struct glink_rpm_p
Re: [PATCH v2 02/20] rpmsg: glink: Associate indirections for pipe fifo accessor's
On 8/24/2017 12:51 PM, Sricharan R wrote: From: Bjorn Andersson With the intention of reusing the glink core protocol commands and code across both rpm and smem based transports, the only thing different is way of accessing the shared-memory of the transport (FIFO). So put the fifo accessor's of the transport's pipe (rx/tx) behind indirections, so that the rest of the code can be shared. For this, have a qcom_glink_pipe that can be used in the common code containing the indirections and wrap it with glink_rpm_pipe that contains the transport specific members. Signed-off-by: Bjorn Andersson Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_rpm.c | 144 ++--- 1 file changed, 106 insertions(+), 38 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c index 56a0a66..870ce32 100644 --- a/drivers/rpmsg/qcom_glink_rpm.c +++ b/drivers/rpmsg/qcom_glink_rpm.c @@ -41,12 +41,28 @@ #define RPM_GLINK_CID_MIN 1 #define RPM_GLINK_CID_MAX 65536 +#define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native) + struct rpm_toc_entry { __le32 id; __le32 offset; __le32 size; } __packed; +struct qcom_glink; + +struct qcom_glink_pipe { + size_t length; + + size_t (*avail)(struct qcom_glink_pipe *glink_pipe); + void (*peak)(struct qcom_glink_pipe *glink_pipe, void *data, +size_t count); + void (*advance)(struct qcom_glink_pipe *glink_pipe, size_t count); + void (*write)(struct qcom_glink_pipe *glink_pipe, + const void *hdr, size_t hlen, + const void *data, size_t dlen); +}; + struct rpm_toc { __le32 magic; __le32 count; @@ -62,12 +78,12 @@ struct glink_msg { } __packed; struct glink_rpm_pipe { + struct qcom_glink_pipe native; + void __iomem *tail; void __iomem *head; void __iomem *fifo; - - size_t length; }; /** @@ -107,8 +123,8 @@ struct qcom_glink { struct mbox_client mbox_client; struct mbox_chan *mbox_chan; - struct glink_rpm_pipe rx_pipe; - struct glink_rpm_pipe tx_pipe; + struct qcom_glink_pipe *rx_pipe; + struct qcom_glink_pipe *tx_pipe; int irq; @@ -215,9 +231,9 @@ static void qcom_glink_channel_release(struct kref *ref) kfree(channel); } -static size_t qcom_glink_rx_avail(struct qcom_glink *glink) +static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe) { - struct glink_rpm_pipe *pipe = &glink->rx_pipe; + struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); unsigned int head; unsigned int tail; @@ -225,21 +241,26 @@ static size_t qcom_glink_rx_avail(struct qcom_glink *glink) tail = readl(pipe->tail); if (head < tail) - return pipe->length - tail + head; + return pipe->native.length - tail + head; else return head - tail; } -static void qcom_glink_rx_peak(struct qcom_glink *glink, - void *data, size_t count) +static size_t qcom_glink_rx_avail(struct qcom_glink *glink) +{ + return glink->rx_pipe->avail(glink->rx_pipe); +} + +static void glink_rpm_rx_peak(struct qcom_glink_pipe *glink_pipe, + void *data, size_t count) { - struct glink_rpm_pipe *pipe = &glink->rx_pipe; + struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); unsigned int tail; size_t len; tail = readl(pipe->tail); - len = min_t(size_t, count, pipe->length - tail); + len = min_t(size_t, count, pipe->native.length - tail); if (len) { __ioread32_copy(data, pipe->fifo + tail, len / sizeof(u32)); @@ -251,24 +272,35 @@ static void qcom_glink_rx_peak(struct qcom_glink *glink, } } -static void qcom_glink_rx_advance(struct qcom_glink *glink, - size_t count) +static void qcom_glink_rx_peak(struct qcom_glink *glink, + void *data, size_t count) { - struct glink_rpm_pipe *pipe = &glink->rx_pipe; + glink->rx_pipe->peak(glink->rx_pipe, data, count); +} + +static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe, +size_t count) +{ + struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); unsigned int tail; tail = readl(pipe->tail); tail += count; - if (tail >= pipe->length) - tail -= pipe->length; + if (tail >= pipe->native.length) + tail -= pipe->native.length; writel(tail, pipe->tail); } -static size_t qcom_glink_tx_avail(struct qcom_glink *glink) +static void qcom_glink_rx_
Re: [PATCH v2 03/20] rpmsg: glink: Split rpm_probe to reuse the common code
On 8/24/2017 12:51 PM, Sricharan R wrote: From: Bjorn Andersson There is quite some code common in glink_rpm_probe that can reused for glink-smem based transport as well. So split the function and move the code to glink_native_probe that can be used later when we add the support for glink-smem based transport. Also reuse driver's remove as well. Signed-off-by: Bjorn Andersson Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_rpm.c | 85 -- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c index 870ce32..5f0fa0d 100644 --- a/drivers/rpmsg/qcom_glink_rpm.c +++ b/drivers/rpmsg/qcom_glink_rpm.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1155,29 +1156,21 @@ static int glink_rpm_parse_toc(struct device *dev, return -EINVAL; } -static int glink_rpm_probe(struct platform_device *pdev) +struct qcom_glink *qcom_glink_native_probe(struct device *dev, + struct qcom_glink_pipe *rx, + struct qcom_glink_pipe *tx) { - struct qcom_glink *glink; - struct glink_rpm_pipe *rx_pipe; - struct glink_rpm_pipe *tx_pipe; - struct device_node *np; - void __iomem *msg_ram; - size_t msg_ram_size; - struct device *dev = &pdev->dev; - struct resource r; int irq; int ret; + struct qcom_glink *glink; glink = devm_kzalloc(dev, sizeof(*glink), GFP_KERNEL); if (!glink) - return -ENOMEM; + return ERR_PTR(-ENOMEM); glink->dev = dev; - - rx_pipe = devm_kzalloc(&pdev->dev, sizeof(*rx_pipe), GFP_KERNEL); - tx_pipe = devm_kzalloc(&pdev->dev, sizeof(*tx_pipe), GFP_KERNEL); - if (!rx_pipe || !tx_pipe) - return -ENOMEM; + glink->tx_pipe = tx; + glink->rx_pipe = rx; mutex_init(&glink->tx_lock); spin_lock_init(&glink->rx_lock); @@ -1188,14 +1181,48 @@ static int glink_rpm_probe(struct platform_device *pdev) idr_init(&glink->lcids); idr_init(&glink->rcids); - glink->mbox_client.dev = &pdev->dev; + glink->mbox_client.dev = dev; glink->mbox_chan = mbox_request_channel(&glink->mbox_client, 0); if (IS_ERR(glink->mbox_chan)) { if (PTR_ERR(glink->mbox_chan) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to acquire IPC channel\n"); - return PTR_ERR(glink->mbox_chan); + dev_err(dev, "failed to acquire IPC channel\n"); + return ERR_CAST(glink->mbox_chan); + } + + irq = of_irq_get(dev->of_node, 0); + ret = devm_request_irq(dev, irq, + qcom_glink_intr, + IRQF_NO_SUSPEND | IRQF_SHARED, + "glink-native", glink); + if (ret) { + dev_err(dev, "failed to request IRQ\n"); + return ERR_PTR(ret); } + ret = qcom_glink_send_version(glink); + if (ret) + return ERR_PTR(ret); + + return glink; +} + +static int glink_rpm_probe(struct platform_device *pdev) +{ + struct qcom_glink *glink; + struct glink_rpm_pipe *rx_pipe; + struct glink_rpm_pipe *tx_pipe; + struct device_node *np; + void __iomem *msg_ram; + size_t msg_ram_size; + struct device *dev = &pdev->dev; + struct resource r; + int ret; + + rx_pipe = devm_kzalloc(&pdev->dev, sizeof(*rx_pipe), GFP_KERNEL); + tx_pipe = devm_kzalloc(&pdev->dev, sizeof(*tx_pipe), GFP_KERNEL); + if (!rx_pipe || !tx_pipe) + return -ENOMEM; + np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", 0); ret = of_address_to_resource(np, 0, &r); of_node_put(np); @@ -1219,27 +1246,13 @@ static int glink_rpm_probe(struct platform_device *pdev) tx_pipe->native.avail = glink_rpm_tx_avail; tx_pipe->native.write = glink_rpm_tx_write; - glink->tx_pipe = &tx_pipe->native; - glink->rx_pipe = &rx_pipe->native; - writel(0, tx_pipe->head); writel(0, rx_pipe->tail); - irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(dev, irq, - qcom_glink_intr, - IRQF_NO_SUSPEND | IRQF_SHARED, - "glink-rpm", glink); - if (ret) { - dev_err(dev, "Failed to request IRQ\n"); - return ret; - } - - glink->irq = irq; - -
Re: [PATCH v2 04/20] rpmsg: glink: Move the common glink protocol implementation to glink_native.c
On 8/24/2017 12:51 PM, Sricharan R wrote: From: Bjorn Andersson Move the common part of glink core protocol implementation to glink_native.c that can be shared with the smem based glink transport in the later patches. Signed-off-by: Bjorn Andersson Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/Kconfig |6 +- drivers/rpmsg/Makefile|1 + drivers/rpmsg/qcom_glink_native.c | 1017 + drivers/rpmsg/qcom_glink_native.h | 38 ++ drivers/rpmsg/qcom_glink_rpm.c| 995 +--- 5 files changed, 1064 insertions(+), 993 deletions(-) create mode 100644 drivers/rpmsg/qcom_glink_native.c create mode 100644 drivers/rpmsg/qcom_glink_native.h diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index 2a5d2b4..ac33688 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -13,9 +13,13 @@ config RPMSG_CHAR in /dev. They make it possible for user-space programs to send and receive rpmsg packets. +config RPMSG_QCOM_GLINK_NATIVE + tristate + select RPMSG + config RPMSG_QCOM_GLINK_RPM tristate "Qualcomm RPM Glink driver" - select RPMSG +select RPMSG_QCOM_GLINK_NATIVE depends on HAS_IOMEM depends on MAILBOX help diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 28cc190..09a756c 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_RPMSG) += rpmsg_core.o obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o +obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o obj-$(CONFIG_RPMSG_VIRTIO)+= virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c new file mode 100644 index 000..ffdf88e --- /dev/null +++ b/drivers/rpmsg/qcom_glink_native.c @@ -0,0 +1,1017 @@ +/* + * Copyright (c) 2016-2017, Linaro Ltd + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rpmsg_internal.h" +#include "qcom_glink_native.h" + +#define GLINK_NAME_SIZE32 + +#define RPM_GLINK_CID_MIN 1 +#define RPM_GLINK_CID_MAX 65536 + +struct glink_msg { + __le16 cmd; + __le16 param1; + __le32 param2; + u8 data[]; +} __packed; + +/** + * struct glink_defer_cmd - deferred incoming control message + * @node: list node + * @msg: message header + * data: payload of the message + * + * Copy of a received control message, to be added to @rx_queue and processed + * by @rx_work of @qcom_glink. + */ +struct glink_defer_cmd { + struct list_head node; + + struct glink_msg msg; + u8 data[]; +}; + +/** + * struct qcom_glink - driver context, relates to one remote subsystem + * @dev: reference to the associated struct device + * @mbox_client: mailbox client + * @mbox_chan: mailbox channel + * @rx_pipe: pipe object for receive FIFO + * @tx_pipe: pipe object for transmit FIFO + * @irq: IRQ for signaling incoming events + * @rx_work: worker for handling received control messages + * @rx_lock: protects the @rx_queue + * @rx_queue: queue of received control messages to be processed in @rx_work + * @tx_lock: synchronizes operations on the tx fifo + * @idr_lock: synchronizes @lcids and @rcids modifications + * @lcids: idr of all channels with a known local channel id + * @rcids: idr of all channels with a known remote channel id + */ +struct qcom_glink { + struct device *dev; + + struct mbox_client mbox_client; + struct mbox_chan *mbox_chan; + + struct qcom_glink_pipe *rx_pipe; + struct qcom_glink_pipe *tx_pipe; + + int irq; + + struct work_struct rx_work; + spinlock_t rx_lock; + struct list_head rx_queue; + + struct mutex tx_lock; + + struct mutex idr_lock; + struct idr lcids; + struct idr rcids; +}; + +enum { + GLINK_STATE_CLOSED, + GLINK_STATE_OPENING, + GLINK_STATE_OPEN, + GLINK_STATE_CLOSING, +}; + +/** + * struct glink_channel - internal representation of a channel + * @rpdev: rpdev reference, only used for primary endpoints + * @ept: rpmsg endpo
Re: [PATCH v2 05/20] rpmsg: glink: Allow unaligned data access
On 8/24/2017 12:51 PM, Sricharan R wrote: From: Bjorn Andersson Glink protocol requires that each message is aligned on a 8 byte offset. This is purely a restriction from glink, so in order to support clients which do not adher to this, allow data packets of any size, but align the head index accordingly, effectively removing the alignment restriction. Signed-off-by: Bjorn Andersson Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 6 -- drivers/rpmsg/qcom_glink_rpm.c| 22 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index ffdf88e..a6394cd 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -227,9 +227,6 @@ static int qcom_glink_tx(struct qcom_glink *glink, if (tlen >= glink->tx_pipe->length) return -EINVAL; - if (WARN(tlen % 8, "Unaligned TX request")) - return -EINVAL; - ret = mutex_lock_interruptible(&glink->tx_lock); if (ret) return ret; @@ -695,9 +692,6 @@ static int __qcom_glink_send(struct glink_channel *channel, __le32 left_size; } __packed req; - if (WARN(len % 8, "RPM GLINK expects 8 byte aligned messages\n")) - return -EINVAL; - req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA); req.msg.param1 = cpu_to_le16(channel->lcid); req.msg.param2 = cpu_to_le32(channel->rcid); diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c index 33daa32..cc73af0 100644 --- a/drivers/rpmsg/qcom_glink_rpm.c +++ b/drivers/rpmsg/qcom_glink_rpm.c @@ -156,11 +156,31 @@ static void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe, const void *data, size_t dlen) { struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); + size_t tlen = hlen + dlen; + size_t aligned_dlen; unsigned int head; + char padding[8] = {0}; + size_t pad; + + /* Header length comes from glink native and is always 4 byte aligned */ + if (WARN(hlen % 4, "Glink Header length must be 4 bytes aligned\n")) + return; + + /* +* Move the unaligned tail of the message to the padding chunk, to +* ensure word aligned accesses +*/ + aligned_dlen = ALIGN_DOWN(dlen, 4); + if (aligned_dlen != dlen) + memcpy(padding, data + aligned_dlen, dlen - aligned_dlen); head = readl(pipe->head); head = glink_rpm_tx_write_one(pipe, head, hdr, hlen); - head = glink_rpm_tx_write_one(pipe, head, data, dlen); + head = glink_rpm_tx_write_one(pipe, head, data, aligned_dlen); + + pad = ALIGN(tlen, 8) - ALIGN_DOWN(tlen, 4); + if (pad) + head = glink_rpm_tx_write_one(pipe, head, padding, pad); writel(head, pipe->head); }
Re: [PATCH v2 06/20] rpmsg: glink: Return -EAGAIN when there is no FIFO space
On 8/24/2017 12:51 PM, Sricharan R wrote: The TX FIFO can be full, if the remote client has not read enough data (or) reading it slowly. So its nessecary to return -EAGAIN to the local client to enable retry. Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index a6394cd..94b79e8 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -233,7 +233,7 @@ static int qcom_glink_tx(struct qcom_glink *glink, while (qcom_glink_tx_avail(glink) < tlen) { if (!wait) { - ret = -ENOMEM; + ret = -EAGAIN; goto out; }
Re: [PATCH v2 07/20] rpmsg: glink: Do a mbox_free_channel in remove
On 8/24/2017 12:51 PM, Sricharan R wrote: mbox_request_channel is done in probe, so free the channel in remove. Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 94b79e8..21adde3 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1008,4 +1008,5 @@ void qcom_glink_native_remove(struct qcom_glink *glink) idr_destroy(&glink->lcids); idr_destroy(&glink->rcids); + mbox_free_channel(glink->mbox_chan); }
Re: [PATCH v2 08/20] rpmsg: glink: Introduce glink smem based transport
On 8/24/2017 12:51 PM, Sricharan R wrote: From: Bjorn Andersson The glink protocol supports different types of transports (shared memory). With the core protocol remaining the same, the way the transport's memory is probed and accessed is different. So add support for glink's smem based transports. Adding a new smem transport register function and the fifo accessors for the same. Signed-off-by: Bjorn Andersson Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/Kconfig | 10 ++ drivers/rpmsg/Makefile| 1 + drivers/rpmsg/qcom_glink_native.c | 5 + drivers/rpmsg/qcom_glink_native.h | 1 + drivers/rpmsg/qcom_glink_smem.c | 311 ++ include/linux/rpmsg/qcom_glink.h | 27 6 files changed, 355 insertions(+) create mode 100644 drivers/rpmsg/qcom_glink_smem.c create mode 100644 include/linux/rpmsg/qcom_glink.h diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index ac33688..4bd9ba3 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -27,6 +27,16 @@ config RPMSG_QCOM_GLINK_RPM which serves as a channel for communication with the RPM in GLINK enabled systems. +config RPMSG_QCOM_GLINK_SMEM + tristate "Qualcomm SMEM Glink driver" + select RPMSG_QCOM_GLINK_NATIVE + depends on HAS_IOMEM + depends on MAILBOX + help + Say y here to enable support for the GLINK SMEM communication driver, + which provides support for using the GLINK communication protocol + over SMEM. + config RPMSG_QCOM_SMD tristate "Qualcomm Shared Memory Driver (SMD)" depends on QCOM_SMEM diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 09a756c..c71f4ab 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_RPMSG) += rpmsg_core.o obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o +obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o obj-$(CONFIG_RPMSG_VIRTIO)+= virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 21adde3..50a8008 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1010,3 +1010,8 @@ void qcom_glink_native_remove(struct qcom_glink *glink) idr_destroy(&glink->rcids); mbox_free_channel(glink->mbox_chan); } + +void qcom_glink_native_unregister(struct qcom_glink *glink) +{ + device_unregister(glink->dev); +} diff --git a/drivers/rpmsg/qcom_glink_native.h b/drivers/rpmsg/qcom_glink_native.h index d5627a4..197bb9d 100644 --- a/drivers/rpmsg/qcom_glink_native.h +++ b/drivers/rpmsg/qcom_glink_native.h @@ -35,4 +35,5 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, struct qcom_glink_pipe *tx); void qcom_glink_native_remove(struct qcom_glink *glink); +void qcom_glink_native_unregister(struct qcom_glink *glink); #endif diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c new file mode 100644 index 000..19179a1 --- /dev/null +++ b/drivers/rpmsg/qcom_glink_smem.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2016, Linaro Ltd + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "qcom_glink_native.h" + +#define FIFO_FULL_RESERVE 8 +#define FIFO_ALIGNMENT 8 +#define TX_BLOCKED_CMD_RESERVE 8 /* size of struct read_notif_request */ + +#define SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR 478 +#define SMEM_GLINK_NATIVE_XPRT_FIFO_0 479 +#define SMEM_GLINK_NATIVE_XPRT_FIFO_1 480 + +struct glink_smem_pipe { + struct qcom_glink_pipe native; + + __le32 *tail; + __le32 *head; + + void *fifo; + + int remote_pid; +}; + +#define to_smem_pipe(p) container_of(p, struct glink_smem_pipe, native) + +static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np) +{ + struct glink_smem_pipe *pipe = to_smem_pipe(np); + size_t len; + void *fifo; + u32 head; + u32 tail; + + if (!pipe->fifo)
Re: [PATCH v2 09/20] rpmsg: glink: Fix default case while handling received commands
On 8/24/2017 12:51 PM, Sricharan R wrote: Currently if we receive a command that we still do not support, then its simply discarded. While doing so, the RX FIFO pointer also needs to be incremented. Fixing this. Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 50a8008..9a58925 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -541,6 +541,7 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) ret = 0; break; default: + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd); ret = -EINVAL; break;
Re: [PATCH v2 12/20] rpmsg: glink: Add support for TX intents
On 8/24/2017 12:51 PM, Sricharan R wrote: Intents are nothing but pre-allocated buffers of appropriate size that are allocated on the local side and communicated to the remote side and the remote stores the list of intent ids that it is informed. Later when remote side is intenting to send data, it picks up a right intent (based on the size) and sends the data buffer and the intent id. Local side receives the data and copies it to the local intent buffer. The whole idea is to avoid stalls on the transport for allocating memory, used for copy based transports. When the remote request to allocate buffers using CMD_RX_INTENT_REQ, we allocate buffers of requested size, store the buffer id locally and also communicate the intent id to the remote. Signed-off-by: Bjorn Andersson Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 167 +- drivers/rpmsg/qcom_glink_native.h | 3 +- drivers/rpmsg/qcom_glink_rpm.c| 3 +- drivers/rpmsg/qcom_glink_smem.c | 5 +- 4 files changed, 173 insertions(+), 5 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 588a56c..7778e57 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -60,6 +60,26 @@ struct glink_defer_cmd { }; /** + * struct glink_core_rx_intent - RX intent + * RX intent + * + * data: pointer to the data (may be NULL for zero-copy) + * id: remote or local intent ID + * size: size of the original intent (do not modify) + * reuse: To mark if the intent can be reused after first use + * in_use: To mark if intent is already in use for the channel + * offset: next write offset (initially 0) + */ +struct glink_core_rx_intent { + void *data; + u32 id; + size_t size; + bool reuse; + bool in_use; + u32 offset; +}; + +/** * struct qcom_glink - driver context, relates to one remote subsystem * @dev: reference to the associated struct device * @mbox_client: mailbox client @@ -117,6 +137,8 @@ enum { * @name: unique channel name/identifier * @lcid: channel id, in local space * @rcid: channel id, in remote space + * @intent_lock: lock for protection of @liids + * @liids: idr of all local intents * @buf: receive buffer, for gathering fragments * @buf_offset: write offset in @buf * @buf_size: size of current @buf @@ -137,6 +159,9 @@ struct glink_channel { unsigned int lcid; unsigned int rcid; + spinlock_t intent_lock; + struct idr liids; + void *buf; int buf_offset; int buf_size; @@ -154,6 +179,9 @@ struct glink_channel { #define RPM_CMD_OPEN 2 #define RPM_CMD_CLOSE 3 #define RPM_CMD_OPEN_ACK 4 +#define RPM_CMD_INTENT 5 +#define RPM_CMD_RX_INTENT_REQ 7 +#define RPM_CMD_RX_INTENT_REQ_ACK 8 #define RPM_CMD_TX_DATA 9 #define RPM_CMD_CLOSE_ACK 11 #define RPM_CMD_TX_DATA_CONT 12 @@ -172,12 +200,14 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, /* Setup glink internal glink_channel data */ spin_lock_init(&channel->recv_lock); + spin_lock_init(&channel->intent_lock); channel->glink = glink; channel->name = kstrdup(name, GFP_KERNEL); init_completion(&channel->open_req); init_completion(&channel->open_ack); + idr_init(&channel->liids); kref_init(&channel->refcount); return channel; @@ -187,6 +217,11 @@ static void qcom_glink_channel_release(struct kref *ref) { struct glink_channel *channel = container_of(ref, struct glink_channel, refcount); + unsigned long flags; + + spin_lock_irqsave(&channel->intent_lock, flags); + idr_destroy(&channel->liids); + spin_unlock_irqrestore(&channel->intent_lock, flags); kfree(channel->name); kfree(channel); @@ -424,6 +459,130 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink, } } +/** + * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to + wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @granted: The request response to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink, + struct glink_channel *channel, + bool granted) +{ + struct glink_msg msg; + + msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK); + msg.param1 = cpu_to_le16(channel->lcid); +
Re: [PATCH v2 10/20] rpmsg: glink: Add support for transport version negotiation
On 8/24/2017 12:51 PM, Sricharan R wrote: G-link supports a version number and feature flags for each transport. A combination of the version number and feature flags enable/disable: (*) G-Link software updates for each edge (*) Individual features for each edge Endpoints negotiate both the version and the supported flags when the transport is opened and they cannot be changed after negotiation has been completed. Each full implementation of G-Link must support a minimum of the current version, the previous version, and the base negotiation version called v0. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 75 --- drivers/rpmsg/qcom_glink_native.h | 5 +++ drivers/rpmsg/qcom_glink_rpm.c| 4 ++- drivers/rpmsg/qcom_glink_smem.c | 1 + 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 9a58925..777ac6b 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -31,6 +31,7 @@ #include "qcom_glink_native.h" #define GLINK_NAME_SIZE 32 +#define GLINK_VERSION_11 #define RPM_GLINK_CID_MIN 1 #define RPM_GLINK_CID_MAX 65536 @@ -94,6 +95,9 @@ struct qcom_glink { struct mutex idr_lock; struct idr lcids; struct idr rcids; + unsigned long features; + + bool intentless; }; enum { @@ -256,8 +260,8 @@ static int qcom_glink_send_version(struct qcom_glink *glink) struct glink_msg msg; msg.cmd = cpu_to_le16(RPM_CMD_VERSION); - msg.param1 = cpu_to_le16(1); - msg.param2 = cpu_to_le32(GLINK_FEATURE_INTENTLESS); + msg.param1 = cpu_to_le16(GLINK_VERSION_1); + msg.param2 = cpu_to_le32(glink->features); return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); } @@ -267,8 +271,8 @@ static void qcom_glink_send_version_ack(struct qcom_glink *glink) struct glink_msg msg; msg.cmd = cpu_to_le16(RPM_CMD_VERSION_ACK); - msg.param1 = cpu_to_le16(1); - msg.param2 = cpu_to_le32(0); + msg.param1 = cpu_to_le16(GLINK_VERSION_1); + msg.param2 = cpu_to_le32(glink->features); qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); } @@ -362,6 +366,63 @@ static void qcom_glink_send_close_ack(struct qcom_glink *glink, qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true); } +/** + * qcom_glink_receive_version() - receive version/features from remote system + * + * @glink: pointer to transport interface + * @r_version: remote version + * @r_features:remote features + * + * This function is called in response to a remote-initiated version/feature + * negotiation sequence. + */ +static void qcom_glink_receive_version(struct qcom_glink *glink, + u32 version, + u32 features) +{ + switch (version) { + case 0: + break; + case GLINK_VERSION_1: + glink->features &= features; + /* FALLTHROUGH */ + default: + qcom_glink_send_version_ack(glink); + break; + } +} + +/** + * qcom_glink_receive_version_ack() - receive negotiation ack from remote system + * + * @glink: pointer to transport interface + * @r_version: remote version response + * @r_features:remote features response + * + * This function is called in response to a local-initiated version/feature + * negotiation sequence and is the counter-offer from the remote side based + * upon the initial version and feature set requested. + */ +static void qcom_glink_receive_version_ack(struct qcom_glink *glink, + u32 version, + u32 features) +{ + switch (version) { + case 0: + /* Version negotiation failed */ + break; + case GLINK_VERSION_1: + if (features == glink->features) + break; + + glink->features &= features; + /* FALLTHROUGH */ + default: + qcom_glink_send_version(glink); + break; + } +} + static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) { struct glink_defer_cmd *dcmd; @@ -909,9 +970,10 @@ static void qcom_glink_work(struct work_struct *work) switch (cmd) { case RPM_CMD_VERSION: - qcom_glink_send_version_ack(glink); + qcom_glink_receive_version(glink, param1, param2); break; case RPM_CMD_VERSION_ACK: + qcom_glink_receive_version_ack(glink, param1, param2);
Re: [PATCH v2 14/20] rpmsg: glink: Make RX FIFO peak accessor to take an offset
On 8/24/2017 12:51 PM, Sricharan R wrote: From: Bjorn Andersson To fully read the received rx data from FIFO both the command and data has to be read. Currently we read command, data separately and process them. By adding an offset parameter to RX FIFO peak accessor, command and data can be read together, simplifying things. So introduce this. Signed-off-by: Bjorn Andersson Signed-off-by: Sricharan R Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 15 +++ drivers/rpmsg/qcom_glink_native.h | 2 +- drivers/rpmsg/qcom_glink_rpm.c| 5 - drivers/rpmsg/qcom_glink_smem.c | 5 - 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 67eeb86..ff83cbb 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -233,9 +233,9 @@ static size_t qcom_glink_rx_avail(struct qcom_glink *glink) } static void qcom_glink_rx_peak(struct qcom_glink *glink, - void *data, size_t count) + void *data, unsigned int offset, size_t count) { - glink->rx_pipe->peak(glink->rx_pipe, data, count); + glink->rx_pipe->peak(glink->rx_pipe, data, offset, count); } static void qcom_glink_rx_advance(struct qcom_glink *glink, size_t count) @@ -600,7 +600,7 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) INIT_LIST_HEAD(&dcmd->node); - qcom_glink_rx_peak(glink, &dcmd->msg, sizeof(dcmd->msg) + extra); + qcom_glink_rx_peak(glink, &dcmd->msg, 0, sizeof(dcmd->msg) + extra); spin_lock(&glink->rx_lock); list_add_tail(&dcmd->node, &glink->rx_queue); @@ -633,7 +633,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) return -EAGAIN; } - qcom_glink_rx_peak(glink, &hdr, sizeof(hdr)); + qcom_glink_rx_peak(glink, &hdr, 0, sizeof(hdr)); chunk_size = le32_to_cpu(hdr.chunk_size); left_size = le32_to_cpu(hdr.left_size); @@ -700,9 +700,8 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) goto advance_rx; } - qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr), 8)); qcom_glink_rx_peak(glink, intent->data + intent->offset, - chunk_size); + sizeof(hdr), chunk_size); intent->offset += chunk_size; /* Handle message when no fragments remain to be received */ @@ -722,7 +721,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) } advance_rx: - qcom_glink_rx_advance(glink, ALIGN(chunk_size, 8)); + qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr) + chunk_size, 8)); return ret; } @@ -759,7 +758,7 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) if (avail < sizeof(msg)) break; - qcom_glink_rx_peak(glink, &msg, sizeof(msg)); + qcom_glink_rx_peak(glink, &msg, 0, sizeof(msg)); cmd = le16_to_cpu(msg.cmd); param1 = le16_to_cpu(msg.param1); diff --git a/drivers/rpmsg/qcom_glink_native.h b/drivers/rpmsg/qcom_glink_native.h index 92e0881..0cae8a8 100644 --- a/drivers/rpmsg/qcom_glink_native.h +++ b/drivers/rpmsg/qcom_glink_native.h @@ -24,7 +24,7 @@ struct qcom_glink_pipe { size_t (*avail)(struct qcom_glink_pipe *glink_pipe); void (*peak)(struct qcom_glink_pipe *glink_pipe, void *data, -size_t count); +unsigned int offset, size_t count); void (*advance)(struct qcom_glink_pipe *glink_pipe, size_t count); void (*write)(struct qcom_glink_pipe *glink_pipe, diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c index 5a86e08..69b25d1 100644 --- a/drivers/rpmsg/qcom_glink_rpm.c +++ b/drivers/rpmsg/qcom_glink_rpm.c @@ -77,13 +77,16 @@ static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe) } static void glink_rpm_rx_peak(struct qcom_glink_pipe *glink_pipe, - void *data, size_t count) + void *data, unsigned int offset, size_t count) { struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); unsigned int tail; size_t len; tail = readl(pipe->tail); + tail += offset; + if (tail >= pipe->native.length) + tail -= pipe->native.length; len = min_t(size_t, count, pipe->native.length - tail); if (len) { diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c index 9d73ced..5cdaa5f 100644 --- a/drivers/rpmsg/qcom_glink_smem.c +++ b/drivers/rpmsg/qcom_glink_smem.c @@ -87,13 +87,16 @@ static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np)
Re: [PATCH v2 15/20] rpmsg: glink: Add rx done command
On 8/24/2017 12:51 PM, Sricharan R wrote: Send RX data receive ack to remote and also inform that local intent buffer is used and freed. This informs the remote to request for next set of intent buffers before doing a send operation. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 83 +++ 1 file changed, 83 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index ff83cbb..1458bdb 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -77,6 +77,8 @@ struct glink_core_rx_intent { bool reuse; bool in_use; u32 offset; + + struct list_head node; }; /** @@ -139,6 +141,8 @@ enum { * @rcid: channel id, in remote space * @intent_lock: lock for protection of @liids * @liids:idr of all local intents + * @intent_work: worker responsible for transmitting rx_done packets + * @done_intents: list of intents that needs to be announced rx_done * @buf: receive buffer, for gathering fragments * @buf_offset: write offset in @buf * @buf_size: size of current @buf @@ -161,6 +165,8 @@ struct glink_channel { spinlock_t intent_lock; struct idr liids; + struct work_struct intent_work; + struct list_head done_intents; struct glink_core_rx_intent *buf; int buf_offset; @@ -180,15 +186,19 @@ struct glink_channel { #define RPM_CMD_CLOSE 3 #define RPM_CMD_OPEN_ACK 4 #define RPM_CMD_INTENT5 +#define RPM_CMD_RX_DONE6 #define RPM_CMD_RX_INTENT_REQ 7 #define RPM_CMD_RX_INTENT_REQ_ACK 8 #define RPM_CMD_TX_DATA 9 #define RPM_CMD_CLOSE_ACK 11 #define RPM_CMD_TX_DATA_CONT 12 #define RPM_CMD_READ_NOTIF13 +#define RPM_CMD_RX_DONE_W_REUSE14 #define GLINK_FEATURE_INTENTLESS BIT(1) +static void qcom_glink_rx_done_work(struct work_struct *work); + static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, const char *name) { @@ -201,12 +211,16 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, /* Setup glink internal glink_channel data */ spin_lock_init(&channel->recv_lock); spin_lock_init(&channel->intent_lock); + channel->glink = glink; channel->name = kstrdup(name, GFP_KERNEL); init_completion(&channel->open_req); init_completion(&channel->open_ack); + INIT_LIST_HEAD(&channel->done_intents); + INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work); + idr_init(&channel->liids); kref_init(&channel->refcount); @@ -402,6 +416,70 @@ static void qcom_glink_send_close_ack(struct qcom_glink *glink, qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true); } +static void qcom_glink_rx_done_work(struct work_struct *work) +{ + struct glink_channel *channel = container_of(work, struct glink_channel, +intent_work); + struct qcom_glink *glink = channel->glink; + struct glink_core_rx_intent *intent, *tmp; + struct { + u16 id; + u16 lcid; + u32 liid; + } __packed cmd; + + unsigned int cid = channel->lcid; + unsigned int iid; + bool reuse; + unsigned long flags; + + spin_lock_irqsave(&channel->intent_lock, flags); + list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) { + list_del(&intent->node); + spin_unlock_irqrestore(&channel->intent_lock, flags); + iid = intent->id; + reuse = intent->reuse; + + cmd.id = reuse ? RPM_CMD_RX_DONE_W_REUSE : RPM_CMD_RX_DONE; + cmd.lcid = cid; + cmd.liid = iid; + + qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); + if (!reuse) { + kfree(intent->data); + kfree(intent); + } + spin_lock_irqsave(&channel->intent_lock, flags); + } + spin_unlock_irqrestore(&channel->intent_lock, flags); +} + +static void qcom_glink_rx_done(struct qcom_glink *glink, + struct glink_channel *channel, + struct glink_core_rx_intent *intent) +{ + /* We don't send RX_DONE to intentless systems */ + if (glink->intentless) { + kfree(intent->data); + kfree(intent); + return; +
Re: [PATCH v2 13/20] rpmsg: glink: Use the local intents when receiving data
On 8/24/2017 12:51 PM, Sricharan R wrote: So previously on request from remote side, we allocated local intent buffers and passed the ids to the remote. Now when we receive data buffers from remote directed to that intent id, copy the data to the corresponding preallocated intent buffer. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 75 ++- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 7778e57..67eeb86 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -162,7 +162,7 @@ struct glink_channel { spinlock_t intent_lock; struct idr liids; - void *buf; + struct glink_core_rx_intent *buf; int buf_offset; int buf_size; @@ -614,6 +614,7 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) { + struct glink_core_rx_intent *intent; struct glink_channel *channel; struct { struct glink_msg msg; @@ -623,6 +624,8 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) unsigned int chunk_size; unsigned int left_size; unsigned int rcid; + unsigned int liid; + int ret = 0; unsigned long flags; if (avail < sizeof(hdr)) { @@ -650,56 +653,78 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) dev_dbg(glink->dev, "Data on non-existing channel\n"); /* Drop the message */ - qcom_glink_rx_advance(glink, - ALIGN(sizeof(hdr) + chunk_size, 8)); - return 0; + goto advance_rx; } - /* Might have an ongoing, fragmented, message to append */ - if (!channel->buf) { - channel->buf = kmalloc(chunk_size + left_size, GFP_ATOMIC); - if (!channel->buf) - return -ENOMEM; + if (glink->intentless) { + /* Might have an ongoing, fragmented, message to append */ + if (!channel->buf) { + intent = kzalloc(sizeof(*intent), GFP_ATOMIC); + if (!intent) + return -ENOMEM; + + intent->data = kmalloc(chunk_size + left_size, + GFP_ATOMIC); + if (!intent->data) { + kfree(intent); + return -ENOMEM; + } + + intent->id = 0xbabababa; + intent->size = chunk_size + left_size; + intent->offset = 0; + + channel->buf = intent; + } else { + intent = channel->buf; + } + } else { + liid = le32_to_cpu(hdr.msg.param2); - channel->buf_size = chunk_size + left_size; - channel->buf_offset = 0; - } + spin_lock_irqsave(&channel->intent_lock, flags); + intent = idr_find(&channel->liids, liid); + spin_unlock_irqrestore(&channel->intent_lock, flags); - qcom_glink_rx_advance(glink, sizeof(hdr)); + if (!intent) { + dev_err(glink->dev, + "no intent found for channel %s intent %d", + channel->name, liid); + goto advance_rx; + } + } - if (channel->buf_size - channel->buf_offset < chunk_size) { - dev_err(glink->dev, "Insufficient space in input buffer\n"); + if (intent->size - intent->offset < chunk_size) { + dev_err(glink->dev, "Insufficient space in intent\n"); /* The packet header lied, drop payload */ - qcom_glink_rx_advance(glink, chunk_size); - return -ENOMEM; + goto advance_rx; } - qcom_glink_rx_peak(glink, channel->buf + channel->buf_offset, + qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr), 8)); + qcom_glink_rx_peak(glink, intent->data + intent->offset, chunk_size); - channel->buf_offset += chunk_size; + intent->offset += chunk_size; /* Handle message when no fragments remain to be received */ if (!left_size) { spin_lock(&channel->recv_lock); if (channel->ept.cb) { channel->ept.cb(channel->ept.rpdev, - ch
Re: [PATCH v2 16/20] rpmsg: glink: Add announce_create ops and preallocate intents
On 8/24/2017 12:51 PM, Sricharan R wrote: Preallocate local intent buffers and pass the intent ids to the remote. This way there are some default intents available for the remote to start sending data without having to wait by sending intent requests. Do this by adding the rpmsg announce_create ops, which gets called right after the rpmsg device gets probed. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 20 1 file changed, 20 insertions(+) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 1458bdb..76ee86e 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1002,6 +1002,25 @@ static struct rpmsg_endpoint *qcom_glink_create_ept(struct rpmsg_device *rpdev, return ept; } +static int qcom_glink_announce_create(struct rpmsg_device *rpdev) +{ + struct glink_channel *channel = to_glink_channel(rpdev->ept); + struct glink_core_rx_intent *intent; + struct qcom_glink *glink = channel->glink; + int num_intents = glink->intentless ? 0 : 5; + + /* Channel is now open, advertise base set of intents */ + while (num_intents--) { + intent = qcom_glink_alloc_intent(glink, channel, SZ_1K, true); + if (!intent) + break; + + qcom_glink_advertise_intent(glink, channel, intent); + } + + return 0; +} + static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) { struct glink_channel *channel = to_glink_channel(ept); @@ -1077,6 +1096,7 @@ static struct device_node *qcom_glink_match_channel(struct device_node *node, static const struct rpmsg_device_ops glink_device_ops = { .create_ept = qcom_glink_create_ept, + .announce_create = qcom_glink_announce_create, }; static const struct rpmsg_endpoint_ops glink_endpoint_ops = {
Re: [PATCH v2 11/20] rpmsg: glink: Fix idr_lock from mutex to spinlock
On 8/24/2017 12:51 PM, Sricharan R wrote: The channel members lcids, rcids synchronised using the idr_lock is accessed in both atomic/non-atomic contexts. The readers are not currently synchronised. That no correct, so add the readers as well under the lock and use a spinlock. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 58 +++ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 777ac6b..588a56c 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -92,7 +92,7 @@ struct qcom_glink { struct mutex tx_lock; - struct mutex idr_lock; + spinlock_t idr_lock; struct idr lcids; struct idr rcids; unsigned long features; @@ -309,14 +309,15 @@ static int qcom_glink_send_open_req(struct qcom_glink *glink, int name_len = strlen(channel->name) + 1; int req_len = ALIGN(sizeof(req.msg) + name_len, 8); int ret; + unsigned long flags; kref_get(&channel->refcount); - mutex_lock(&glink->idr_lock); + spin_lock_irqsave(&glink->idr_lock, flags); ret = idr_alloc_cyclic(&glink->lcids, channel, RPM_GLINK_CID_MIN, RPM_GLINK_CID_MAX, GFP_KERNEL); - mutex_unlock(&glink->idr_lock); + spin_unlock_irqrestore(&glink->idr_lock, flags); if (ret < 0) return ret; @@ -334,10 +335,10 @@ static int qcom_glink_send_open_req(struct qcom_glink *glink, return 0; remove_idr: - mutex_lock(&glink->idr_lock); + spin_lock_irqsave(&glink->idr_lock, flags); idr_remove(&glink->lcids, channel->lcid); channel->lcid = 0; - mutex_unlock(&glink->idr_lock); + spin_unlock_irqrestore(&glink->idr_lock, flags); return ret; } @@ -463,6 +464,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) unsigned int chunk_size; unsigned int left_size; unsigned int rcid; + unsigned long flags; if (avail < sizeof(hdr)) { dev_dbg(glink->dev, "Not enough data in fifo\n"); @@ -482,7 +484,9 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) return -EINVAL; rcid = le16_to_cpu(hdr.msg.param1); + spin_lock_irqsave(&glink->idr_lock, flags); channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); if (!channel) { dev_dbg(glink->dev, "Data on non-existing channel\n"); @@ -543,11 +547,13 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) { struct glink_channel *channel; + spin_lock(&glink->idr_lock); channel = idr_find(&glink->lcids, lcid); if (!channel) { dev_err(glink->dev, "Invalid open ack packet\n"); return -EINVAL; } + spin_unlock(&glink->idr_lock); complete(&channel->open_ack); @@ -621,6 +627,7 @@ static struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink, { struct glink_channel *channel; int ret; + unsigned long flags; channel = qcom_glink_alloc_channel(glink, name); if (IS_ERR(channel)) @@ -644,9 +651,9 @@ static struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink, err_timeout: /* qcom_glink_send_open_req() did register the channel in lcids*/ - mutex_lock(&glink->idr_lock); + spin_lock_irqsave(&glink->idr_lock, flags); idr_remove(&glink->lcids, channel->lcid); - mutex_unlock(&glink->idr_lock); + spin_unlock_irqrestore(&glink->idr_lock, flags); release_channel: /* Release qcom_glink_send_open_req() reference */ @@ -703,11 +710,14 @@ static struct rpmsg_endpoint *qcom_glink_create_ept(struct rpmsg_device *rpdev, const char *name = chinfo.name; int cid; int ret; + unsigned long flags; + spin_lock_irqsave(&glink->idr_lock, flags); idr_for_each_entry(&glink->rcids, channel, cid) { if (!strcmp(channel->name, name)) break; } + spin_unlock_irqrestore(&glink->idr_lock, flags); if (!channel) { channel = qcom_glink_create_local(glink, name); @@ -829,11 +839,14 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, struct device_node *node; int lcid; int ret; + unsigned long flags; + spin_lock_irqsave(
Re: [PATCH v2 17/20] rpmsg: glink: Receive and store the remote intent buffers
On 8/24/2017 12:51 PM, Sricharan R wrote: Just like we allocating and sending intent ids to remote, remote side allocates and sends us the intents as well. So save the intent ids and use it later while sending data targeting the appropriate intents based on the size. Signed-off-by: Sricharan R Signed-off-by: Bjorn Andersson Acked-by: Arun Kumar Neelakantam Regards, Arun N --- drivers/rpmsg/qcom_glink_native.c | 71 ++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 76ee86e..d93d651 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -139,8 +139,9 @@ enum { * @name: unique channel name/identifier * @lcid: channel id, in local space * @rcid: channel id, in remote space - * @intent_lock: lock for protection of @liids + * @intent_lock: lock for protection of @liids, @riids * @liids:idr of all local intents + * @riids: idr of all remote intents * @intent_work: worker responsible for transmitting rx_done packets * @done_intents: list of intents that needs to be announced rx_done * @buf: receive buffer, for gathering fragments @@ -165,6 +166,7 @@ struct glink_channel { spinlock_t intent_lock; struct idr liids; + struct idr riids; struct work_struct intent_work; struct list_head done_intents; @@ -222,6 +224,7 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work); idr_init(&channel->liids); + idr_init(&channel->riids); kref_init(&channel->refcount); return channel; @@ -235,6 +238,7 @@ static void qcom_glink_channel_release(struct kref *ref) spin_lock_irqsave(&channel->intent_lock, flags); idr_destroy(&channel->liids); + idr_destroy(&channel->riids); spin_unlock_irqrestore(&channel->intent_lock, flags); kfree(channel->name); @@ -806,6 +810,68 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) return ret; } +static void qcom_glink_handle_intent(struct qcom_glink *glink, +unsigned int cid, +unsigned int count, +size_t avail) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + struct intent_pair { + __le32 size; + __le32 iid; + }; + + struct { + struct glink_msg msg; + struct intent_pair intents[]; + } __packed * msg; + + const size_t msglen = sizeof(*msg) + sizeof(struct intent_pair) * count; + int ret; + int i; + unsigned long flags; + + if (avail < msglen) { + dev_dbg(glink->dev, "Not enough data in fifo\n"); + return; + } + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "intents for non-existing channel\n"); + return; + } + + msg = kmalloc(msglen, GFP_ATOMIC); + if (!msg) + return; + + qcom_glink_rx_peak(glink, msg, 0, msglen); + + for (i = 0; i < count; ++i) { + intent = kzalloc(sizeof(*intent), GFP_ATOMIC); + if (!intent) + break; + + intent->id = le32_to_cpu(msg->intents[i].iid); + intent->size = le32_to_cpu(msg->intents[i].size); + + spin_lock_irqsave(&channel->intent_lock, flags); + ret = idr_alloc(&channel->riids, intent, + intent->id, intent->id + 1, GFP_ATOMIC); + spin_unlock_irqrestore(&channel->intent_lock, flags); + + if (ret < 0) + dev_err(glink->dev, "failed to store remote intent\n"); + } + + kfree(msg); + qcom_glink_rx_advance(glink, ALIGN(msglen, 8)); +} + static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) { struct glink_channel *channel; @@ -871,6 +937,9 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data) ret = 0; break; + case RPM_CMD_INTENT: + qcom_glink_handle_intent(glink, param1, param2, avail); + break; default: qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd);
Re: [PATCH 1/3] soc: qcom: smem: Support global partition
On 8/18/2017 6:45 AM, Chris Lew wrote: @@ -782,7 +855,10 @@ static int qcom_smem_probe(struct platform_device *pdev) } version = qcom_smem_get_sbl_version(smem); - if (version >> 16 != SMEM_EXPECTED_VERSION) { + switch (version >> 16) { + case SMEM_GLOBAL_PART_VERSION: + case SMEM_GLOBAL_HEAP_VERSION: break statement is needed for supported versions + default: dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version); return -EINVAL; }
Re: [PATCH 2/3] soc: qcom: smem: Support dynamic item limit
On 8/18/2017 6:45 AM, Chris Lew wrote: In V12 SMEM, SBL writes SMEM parameter information after the TOC. Use the SBL provided item count as the max item number. Signed-off-by: Chris Lew --- drivers/soc/qcom/smem.c | 41 +++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index fed2934d6bda..a51f4ba42173 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -225,6 +225,24 @@ struct smem_private_entry { #define SMEM_PRIVATE_CANARY 0xa5a5 /** + * struct smem_info - smem region info located after the table of contents + * @magic: magic number, must be SMEM_INFO_MAGIC + * @size: size of the smem region + * @base_addr: base address of the smem region + * @reserved: for now reserved entry + * @num_items: highest accepted item number + */ +struct smem_info { + u8 magic[4]; + __le32 size; + __le32 base_addr; + __le32 reserved; + __le32 num_items; The num_items is first 16bits of reserved field, we dont have 32bit reserved and 32 bit for num_items +}; + +static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */ + +/** * struct smem_region - representation of a chunk of memory used for smem * @aux_base: identifier of aux_mem base * @virt_base:virtual base address of memory with this aux_mem identifier @@ -245,6 +263,7 @@ struct smem_region { * @partitions: list of pointers to partitions affecting the current *processor/host * @cacheline:list of cacheline sizes for each host + * @item_count: max accepted item number * @num_regions: number of @regions * @regions: list of the memory regions defining the shared memory */ @@ -257,6 +276,7 @@ struct qcom_smem { size_t global_cacheline; struct smem_partition_header *partitions[SMEM_HOST_COUNT]; size_t cacheline[SMEM_HOST_COUNT]; + u32 item_count; unsigned num_regions; struct smem_region regions[0]; @@ -388,7 +408,7 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem, struct smem_header *header; struct smem_global_entry *entry; - if (WARN_ON(item >= SMEM_ITEM_COUNT)) + if (WARN_ON(item >= smem->item_count)) can you please add this item_count check from partition alloc/get also if avoid any unknown clients usage of smem return -EINVAL; header = smem->regions[0].virt_base; @@ -473,7 +493,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, u32 aux_base; unsigned i; - if (WARN_ON(item >= SMEM_ITEM_COUNT)) + if (WARN_ON(item >= smem->item_count)) return ERR_PTR(-EINVAL); header = smem->regions[0].virt_base; @@ -640,6 +660,19 @@ static int qcom_smem_get_sbl_version(struct qcom_smem *smem) return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]); } +static u32 qcom_smem_get_dynamic_item(struct qcom_smem *smem) +{ + struct smem_ptable *ptable; + struct smem_info *info; + + ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K; + info = (struct smem_info *)&ptable->entry[ptable->num_entries]; + if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic))) + return SMEM_ITEM_COUNT; + + return le32_to_cpu(info->num_items); +} + static int qcom_smem_set_global_partition(struct qcom_smem *smem, struct smem_ptable_entry *entry) { @@ -857,7 +890,11 @@ static int qcom_smem_probe(struct platform_device *pdev) version = qcom_smem_get_sbl_version(smem); switch (version >> 16) { case SMEM_GLOBAL_PART_VERSION: + smem->item_count = qcom_smem_get_dynamic_item(smem); + break; case SMEM_GLOBAL_HEAP_VERSION: + smem->item_count = SMEM_ITEM_COUNT; + break; default: dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version); return -EINVAL;
Re: [PATCH 04/18] rpmsg: glink: Move the common glink protocol implementation to glink_native.c
On 8/16/2017 10:48 PM, Sricharan R wrote: + +struct glink_msg { + __le16 cmd; + __le16 param1; + __le32 param2; + u8 data[]; +} __packed; why we are using extra u8 data[] member here ? + +/** + * struct glink_defer_cmd - deferred incoming control message + * @node: list node + * @msg: message header + * data: payload of the message + * + * Copy of a received control message, to be added to @rx_queue and processed + * by @rx_work of @glink_rpm. + */ +struct glink_defer_cmd { + struct list_head node; + + struct glink_msg msg; + u8 data[]; +}; + +/** + * struct glink_rpm - driver context, relates to one remote subsystem glink_rpm to qcom_glink +static int qcom_glink_tx(struct qcom_glink *glink, +const void *hdr, size_t hlen, +const void *data, size_t dlen, bool wait) +{ + unsigned int tlen = hlen + dlen; + int ret; + + /* Reject packets that are too big */ + if (tlen >= glink->tx_pipe->length) + return -EINVAL; we need to add support for split packets, in some cases packets may be greater than 16K. + + if (WARN(tlen % 8, "Unaligned TX request")) + return -EINVAL; + + ret = mutex_lock_interruptible(&glink->tx_lock); + if (ret) + return ret; + + while (qcom_glink_tx_avail(glink) < tlen) { + if (!wait) { + ret = -ENOMEM; This condition is either reader is slow or not reading data, should we return -EAGAIN here instead of -ENOMEM? + goto out; + } + + msleep(10); + } + + qcom_glink_tx_write(glink, hdr, hlen, data, dlen); + + mbox_send_message(glink->mbox_chan, NULL); + mbox_client_txdone(glink->mbox_chan, 0); + +out: + mutex_unlock(&glink->tx_lock); + + return ret; +} + +/** + * qcom_glink_send_open_req() - send a RPM_CMD_OPEN request to the remote + * @glink: + * @channel: Missed information for @ glink and @channel + * + * Allocates a local channel id and sends a RPM_CMD_OPEN message to the remote. + * Will return with refcount held, regardless of outcome. + * + * Returns 0 on success, negative errno otherwise. + */ +static int qcom_glink_send_open_req(struct qcom_glink *glink, + struct glink_channel *channel) +static irqreturn_t qcom_glink_native_intr(int irq, void *data) +{ + struct qcom_glink *glink = data; + struct glink_msg msg; + unsigned int param1; + unsigned int param2; + unsigned int avail; + unsigned int cmd; + int ret; + + for (;;) { + avail = qcom_glink_rx_avail(glink); + if (avail < sizeof(msg)) + break; + + qcom_glink_rx_peak(glink, &msg, sizeof(msg)); + + cmd = le16_to_cpu(msg.cmd); + param1 = le16_to_cpu(msg.param1); + param2 = le32_to_cpu(msg.param2); + + switch (cmd) { + case RPM_CMD_VERSION: + case RPM_CMD_VERSION_ACK: + case RPM_CMD_CLOSE: + case RPM_CMD_CLOSE_ACK: + ret = qcom_glink_rx_defer(glink, 0); + break; + case RPM_CMD_OPEN_ACK: + ret = qcom_glink_rx_open_ack(glink, param1); + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + break; + case RPM_CMD_OPEN: + ret = qcom_glink_rx_defer(glink, param2); + break; + case RPM_CMD_TX_DATA: + case RPM_CMD_TX_DATA_CONT: + ret = qcom_glink_rx_data(glink, avail); + break; + case RPM_CMD_READ_NOTIF: + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + + mbox_send_message(glink->mbox_chan, NULL); + mbox_client_txdone(glink->mbox_chan, 0); + + ret = 0; + break; + default: + dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd); please add more information in error log to find the remote peripheral also other wise after adding SMEM transport it will be difficult to find for which peripheral the error came. + ret = -EINVAL; + break; + } + + if (ret) + break; + } + + return IRQ_HANDLED; +} + +struct qcom_glink *qcom_glink_native_probe(struct device *dev, + struct qcom_glink_pipe *rx, + struct qcom_glink_pipe *tx) +{ + int irq; + int ret; + struct qcom_glink *glink; + + glink = devm_kzalloc(dev, sizeof(*glink), GFP_KERNEL); +
Re: [PATCH v2 2/5] soc: qcom: Introduce QMI helpers
On 11/7/2017 10:50 AM, Bjorn Andersson wrote: Drivers that needs to communicate with a remote QMI service all has to perform the operations of discovering the service, encoding and decoding the messages and operate the socket. This introduces an abstraction for these common operations, reducing most of the duplication in such cases. Signed-off-by: Bjorn Andersson --- Changes since v1: - Squashed notion of qmi vs qrtr in interface, to simplify client code - Lookup and service registration is cached and handled by core on ENETRESET - Introduce callbacks for BYE and DEL_CLIENT control messages - Revisited locking for socket and transaction objects, squashed a few races and made it possible to send "indication" messages from message handlers. - kerneldoc updates - Moved handling of net_reset from clients to this code, greatly simplifies typical drivers and reduces duplication - Pass transaction id of QMI message to handlers even though it's not a response, allows sending a response with the txn id of an incoming request. - Split qmi_send_message() in three, instead of passing type as parameter - to clean up handling of "indication" messages. drivers/soc/qcom/Kconfig | 1 + drivers/soc/qcom/Makefile| 1 + drivers/soc/qcom/qmi_interface.c | 849 +++ include/linux/soc/qcom/qmi.h | 157 4 files changed, 1008 insertions(+) create mode 100644 drivers/soc/qcom/qmi_interface.c diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 91b70b170a82..9718f1c41e3d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -37,6 +37,7 @@ config QCOM_PM config QCOM_QMI_HELPERS tristate + depends on ARCH_QCOM I feel It should depend on QRTR instead of ARCH_QCOM help Helper library for handling QMI encoded messages. QMI encoded messages are used in communication between the majority of QRTR diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 625750acfeef..b04b5044775f 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_PM) += spm.o obj-$(CONFIG_QCOM_QMI_HELPERS)+= qmi_helpers.o qmi_helpers-y += qmi_encdec.o +qmi_helpers-y += qmi_interface.o obj-$(CONFIG_QCOM_SMD_RPM)+= smd-rpm.o obj-$(CONFIG_QCOM_SMEM) +=smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c new file mode 100644 index ..d7885f9b072b --- /dev/null +++ b/drivers/soc/qcom/qmi_interface.c @@ -0,0 +1,849 @@ +/* + * 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 +#include + +static struct socket *qmi_sock_create(struct qmi_handle *qmi, + struct sockaddr_qrtr *sq); + +/** + * qmi_recv_new_server() - handler of NEW_SERVER control message + * @qmi: qmi handle + * @node: node of the new server + * @port: port of the new server + * + * Calls the new_server callback to inform the client about a newly registered + * server matching the currently registered service lookup. + */ +static void qmi_recv_new_server(struct qmi_handle *qmi, + unsigned int service, unsigned int instance, + unsigned int node, unsigned int port) +{ + struct qmi_ops *ops = &qmi->ops; + struct qmi_service *svc; + int ret; + + if (!ops->new_server) + return; + + /* Ignore EOF marker */ + if (!node && !port) + return; + + svc = kzalloc(sizeof(*svc), GFP_KERNEL); + if (!svc) + return; + + svc->service = service; + svc->version = instance & 0xff; + svc->instance = instance >> 8; + svc->node = node; + svc->port = port; + + ret = ops->new_server(qmi, svc); + if (ret < 0) + kfree(svc); + else + list_add(&svc->list_node, &qmi->lookup_results); +} + +/** + * qmi_recv_del_server() - handler of DEL_SERVER control message + * @qmi: qmi handle + * @node: node of the dying server, a value of -1 matches all nodes + * @port: port of the dying server, a value of -1 matches all ports + * + * Calls the del_server callback for each previously s