[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <f859e1c8-7532-ffef-e34a-227835c90a64@codeaurora.org>
Date: Mon, 28 Aug 2017 17:15:54 +0530
From: Arun Kumar Neelakantam <aneela@...eaurora.org>
To: Sricharan R <sricharan@...eaurora.org>, ohad@...ery.com,
bjorn.andersson@...aro.org, linux-remoteproc@...r.kernel.org,
linux-kernel@...r.kernel.org, linux-arm-msm@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org
Subject: 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 <bjorn.andersson@...aro.org>
>
> 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 <bjorn.andersson@...aro.org>
> Signed-off-by: Sricharan R <sricharan@...eaurora.org>
Acked-by: Arun Kumar Neelakantam <aneela@...eaurora.org>
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 0000000..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 <linux/idr.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/list.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/rpmsg.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include <linux/mailbox_client.h>
> +
> +#include "rpmsg_internal.h"
> +#include "qcom_glink_native.h"
> +
> +#define GLINK_NAME_SIZE 32
> +
> +#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 endpoint this channel is associated with
> + * @glink: qcom_glink context handle
> + * @refcount: refcount for the channel object
> + * @recv_lock: guard for @ept.cb
> + * @name: unique channel name/identifier
> + * @lcid: channel id, in local space
> + * @rcid: channel id, in remote space
> + * @buf: receive buffer, for gathering fragments
> + * @buf_offset: write offset in @buf
> + * @buf_size: size of current @buf
> + * @open_ack: completed once remote has acked the open-request
> + * @open_req: completed once open-request has been received
> + */
> +struct glink_channel {
> + struct rpmsg_endpoint ept;
> +
> + struct rpmsg_device *rpdev;
> + struct qcom_glink *glink;
> +
> + struct kref refcount;
> +
> + spinlock_t recv_lock;
> +
> + char *name;
> + unsigned int lcid;
> + unsigned int rcid;
> +
> + void *buf;
> + int buf_offset;
> + int buf_size;
> +
> + struct completion open_ack;
> + struct completion open_req;
> +};
> +
> +#define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept)
> +
> +static const struct rpmsg_endpoint_ops glink_endpoint_ops;
> +
> +#define RPM_CMD_VERSION 0
> +#define RPM_CMD_VERSION_ACK 1
> +#define RPM_CMD_OPEN 2
> +#define RPM_CMD_CLOSE 3
> +#define RPM_CMD_OPEN_ACK 4
> +#define RPM_CMD_TX_DATA 9
> +#define RPM_CMD_CLOSE_ACK 11
> +#define RPM_CMD_TX_DATA_CONT 12
> +#define RPM_CMD_READ_NOTIF 13
> +
> +#define GLINK_FEATURE_INTENTLESS BIT(1)
> +
> +static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,
> + const char *name)
> +{
> + struct glink_channel *channel;
> +
> + channel = kzalloc(sizeof(*channel), GFP_KERNEL);
> + if (!channel)
> + return ERR_PTR(-ENOMEM);
> +
> + /* 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);
> +
> + kref_init(&channel->refcount);
> +
> + return channel;
> +}
> +
> +static void qcom_glink_channel_release(struct kref *ref)
> +{
> + struct glink_channel *channel = container_of(ref, struct glink_channel,
> + refcount);
> +
> + kfree(channel->name);
> + kfree(channel);
> +}
> +
> +static size_t qcom_glink_rx_avail(struct qcom_glink *glink)
> +{
> + return glink->rx_pipe->avail(glink->rx_pipe);
> +}
> +
> +static void qcom_glink_rx_peak(struct qcom_glink *glink,
> + void *data, size_t count)
> +{
> + glink->rx_pipe->peak(glink->rx_pipe, data, count);
> +}
> +
> +static void qcom_glink_rx_advance(struct qcom_glink *glink, size_t count)
> +{
> + glink->rx_pipe->advance(glink->rx_pipe, count);
> +}
> +
> +static size_t qcom_glink_tx_avail(struct qcom_glink *glink)
> +{
> + return glink->tx_pipe->avail(glink->tx_pipe);
> +}
> +
> +static void qcom_glink_tx_write(struct qcom_glink *glink,
> + const void *hdr, size_t hlen,
> + const void *data, size_t dlen)
> +{
> + glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen);
> +}
> +
> +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;
> +
> + 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;
> + goto out;
> + }
> +
> + usleep_range(10000, 15000);
> + }
> +
> + 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;
> +}
> +
> +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);
> +
> + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
> +}
> +
> +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);
> +
> + qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
> +}
> +
> +static void qcom_glink_send_open_ack(struct qcom_glink *glink,
> + struct glink_channel *channel)
> +{
> + struct glink_msg msg;
> +
> + msg.cmd = cpu_to_le16(RPM_CMD_OPEN_ACK);
> + msg.param1 = cpu_to_le16(channel->rcid);
> + msg.param2 = cpu_to_le32(0);
> +
> + qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
> +}
> +
> +/**
> + * qcom_glink_send_open_req() - send a RPM_CMD_OPEN request to the remote
> + * @glink: Ptr to the glink edge
> + * @channel: Ptr to the channel that the open req is sent
> + *
> + * 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)
> +{
> + struct {
> + struct glink_msg msg;
> + u8 name[GLINK_NAME_SIZE];
> + } __packed req;
> + int name_len = strlen(channel->name) + 1;
> + int req_len = ALIGN(sizeof(req.msg) + name_len, 8);
> + int ret;
> +
> + kref_get(&channel->refcount);
> +
> + mutex_lock(&glink->idr_lock);
> + ret = idr_alloc_cyclic(&glink->lcids, channel,
> + RPM_GLINK_CID_MIN, RPM_GLINK_CID_MAX,
> + GFP_KERNEL);
> + mutex_unlock(&glink->idr_lock);
> + if (ret < 0)
> + return ret;
> +
> + channel->lcid = ret;
> +
> + req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN);
> + req.msg.param1 = cpu_to_le16(channel->lcid);
> + req.msg.param2 = cpu_to_le32(name_len);
> + strcpy(req.name, channel->name);
> +
> + ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true);
> + if (ret)
> + goto remove_idr;
> +
> + return 0;
> +
> +remove_idr:
> + mutex_lock(&glink->idr_lock);
> + idr_remove(&glink->lcids, channel->lcid);
> + channel->lcid = 0;
> + mutex_unlock(&glink->idr_lock);
> +
> + return ret;
> +}
> +
> +static void qcom_glink_send_close_req(struct qcom_glink *glink,
> + struct glink_channel *channel)
> +{
> + struct glink_msg req;
> +
> + req.cmd = cpu_to_le16(RPM_CMD_CLOSE);
> + req.param1 = cpu_to_le16(channel->lcid);
> + req.param2 = 0;
> +
> + qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
> +}
> +
> +static void qcom_glink_send_close_ack(struct qcom_glink *glink,
> + unsigned int rcid)
> +{
> + struct glink_msg req;
> +
> + req.cmd = cpu_to_le16(RPM_CMD_CLOSE_ACK);
> + req.param1 = cpu_to_le16(rcid);
> + req.param2 = 0;
> +
> + qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
> +}
> +
> +static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
> +{
> + struct glink_defer_cmd *dcmd;
> +
> + extra = ALIGN(extra, 8);
> +
> + if (qcom_glink_rx_avail(glink) < sizeof(struct glink_msg) + extra) {
> + dev_dbg(glink->dev, "Insufficient data in rx fifo");
> + return -ENXIO;
> + }
> +
> + dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_ATOMIC);
> + if (!dcmd)
> + return -ENOMEM;
> +
> + INIT_LIST_HEAD(&dcmd->node);
> +
> + qcom_glink_rx_peak(glink, &dcmd->msg, sizeof(dcmd->msg) + extra);
> +
> + spin_lock(&glink->rx_lock);
> + list_add_tail(&dcmd->node, &glink->rx_queue);
> + spin_unlock(&glink->rx_lock);
> +
> + schedule_work(&glink->rx_work);
> + qcom_glink_rx_advance(glink, sizeof(dcmd->msg) + extra);
> +
> + return 0;
> +}
> +
> +static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
> +{
> + struct glink_channel *channel;
> + struct {
> + struct glink_msg msg;
> + __le32 chunk_size;
> + __le32 left_size;
> + } __packed hdr;
> + unsigned int chunk_size;
> + unsigned int left_size;
> + unsigned int rcid;
> +
> + if (avail < sizeof(hdr)) {
> + dev_dbg(glink->dev, "Not enough data in fifo\n");
> + return -EAGAIN;
> + }
> +
> + qcom_glink_rx_peak(glink, &hdr, sizeof(hdr));
> + chunk_size = le32_to_cpu(hdr.chunk_size);
> + left_size = le32_to_cpu(hdr.left_size);
> +
> + if (avail < sizeof(hdr) + chunk_size) {
> + dev_dbg(glink->dev, "Payload not yet in fifo\n");
> + return -EAGAIN;
> + }
> +
> + if (WARN(chunk_size % 4, "Incoming data must be word aligned\n"))
> + return -EINVAL;
> +
> + rcid = le16_to_cpu(hdr.msg.param1);
> + channel = idr_find(&glink->rcids, rcid);
> + if (!channel) {
> + 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;
> + }
> +
> + /* 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;
> +
> + channel->buf_size = chunk_size + left_size;
> + channel->buf_offset = 0;
> + }
> +
> + qcom_glink_rx_advance(glink, sizeof(hdr));
> +
> + if (channel->buf_size - channel->buf_offset < chunk_size) {
> + dev_err(glink->dev, "Insufficient space in input buffer\n");
> +
> + /* The packet header lied, drop payload */
> + qcom_glink_rx_advance(glink, chunk_size);
> + return -ENOMEM;
> + }
> +
> + qcom_glink_rx_peak(glink, channel->buf + channel->buf_offset,
> + chunk_size);
> + channel->buf_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,
> + channel->ept.priv,
> + RPMSG_ADDR_ANY);
> + }
> + spin_unlock(&channel->recv_lock);
> +
> + kfree(channel->buf);
> + channel->buf = NULL;
> + channel->buf_size = 0;
> + }
> +
> + /* Each message starts at 8 byte aligned address */
> + qcom_glink_rx_advance(glink, ALIGN(chunk_size, 8));
> +
> + return 0;
> +}
> +
> +static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
> +{
> + struct glink_channel *channel;
> +
> + channel = idr_find(&glink->lcids, lcid);
> + if (!channel) {
> + dev_err(glink->dev, "Invalid open ack packet\n");
> + return -EINVAL;
> + }
> +
> + complete(&channel->open_ack);
> +
> + return 0;
> +}
> +
> +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);
> + ret = -EINVAL;
> + break;
> + }
> +
> + if (ret)
> + break;
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +/* Locally initiated rpmsg_create_ept */
> +static struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink,
> + const char *name)
> +{
> + struct glink_channel *channel;
> + int ret;
> +
> + channel = qcom_glink_alloc_channel(glink, name);
> + if (IS_ERR(channel))
> + return ERR_CAST(channel);
> +
> + ret = qcom_glink_send_open_req(glink, channel);
> + if (ret)
> + goto release_channel;
> +
> + ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ);
> + if (!ret)
> + goto err_timeout;
> +
> + ret = wait_for_completion_timeout(&channel->open_req, 5 * HZ);
> + if (!ret)
> + goto err_timeout;
> +
> + qcom_glink_send_open_ack(glink, channel);
> +
> + return channel;
> +
> +err_timeout:
> + /* qcom_glink_send_open_req() did register the channel in lcids*/
> + mutex_lock(&glink->idr_lock);
> + idr_remove(&glink->lcids, channel->lcid);
> + mutex_unlock(&glink->idr_lock);
> +
> +release_channel:
> + /* Release qcom_glink_send_open_req() reference */
> + kref_put(&channel->refcount, qcom_glink_channel_release);
> + /* Release qcom_glink_alloc_channel() reference */
> + kref_put(&channel->refcount, qcom_glink_channel_release);
> +
> + return ERR_PTR(-ETIMEDOUT);
> +}
> +
> +/* Remote initiated rpmsg_create_ept */
> +static int qcom_glink_create_remote(struct qcom_glink *glink,
> + struct glink_channel *channel)
> +{
> + int ret;
> +
> + qcom_glink_send_open_ack(glink, channel);
> +
> + ret = qcom_glink_send_open_req(glink, channel);
> + if (ret)
> + goto close_link;
> +
> + ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ);
> + if (!ret) {
> + ret = -ETIMEDOUT;
> + goto close_link;
> + }
> +
> + return 0;
> +
> +close_link:
> + /*
> + * Send a close request to "undo" our open-ack. The close-ack will
> + * release the last reference.
> + */
> + qcom_glink_send_close_req(glink, channel);
> +
> + /* Release qcom_glink_send_open_req() reference */
> + kref_put(&channel->refcount, qcom_glink_channel_release);
> +
> + return ret;
> +}
> +
> +static struct rpmsg_endpoint *qcom_glink_create_ept(struct rpmsg_device *rpdev,
> + rpmsg_rx_cb_t cb,
> + void *priv,
> + struct rpmsg_channel_info
> + chinfo)
> +{
> + struct glink_channel *parent = to_glink_channel(rpdev->ept);
> + struct glink_channel *channel;
> + struct qcom_glink *glink = parent->glink;
> + struct rpmsg_endpoint *ept;
> + const char *name = chinfo.name;
> + int cid;
> + int ret;
> +
> + idr_for_each_entry(&glink->rcids, channel, cid) {
> + if (!strcmp(channel->name, name))
> + break;
> + }
> +
> + if (!channel) {
> + channel = qcom_glink_create_local(glink, name);
> + if (IS_ERR(channel))
> + return NULL;
> + } else {
> + ret = qcom_glink_create_remote(glink, channel);
> + if (ret)
> + return NULL;
> + }
> +
> + ept = &channel->ept;
> + ept->rpdev = rpdev;
> + ept->cb = cb;
> + ept->priv = priv;
> + ept->ops = &glink_endpoint_ops;
> +
> + return ept;
> +}
> +
> +static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept)
> +{
> + struct glink_channel *channel = to_glink_channel(ept);
> + struct qcom_glink *glink = channel->glink;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&channel->recv_lock, flags);
> + channel->ept.cb = NULL;
> + spin_unlock_irqrestore(&channel->recv_lock, flags);
> +
> + /* Decouple the potential rpdev from the channel */
> + channel->rpdev = NULL;
> +
> + qcom_glink_send_close_req(glink, channel);
> +}
> +
> +static int __qcom_glink_send(struct glink_channel *channel,
> + void *data, int len, bool wait)
> +{
> + struct qcom_glink *glink = channel->glink;
> + struct {
> + struct glink_msg msg;
> + __le32 chunk_size;
> + __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);
> + req.chunk_size = cpu_to_le32(len);
> + req.left_size = cpu_to_le32(0);
> +
> + return qcom_glink_tx(glink, &req, sizeof(req), data, len, wait);
> +}
> +
> +static int qcom_glink_send(struct rpmsg_endpoint *ept, void *data, int len)
> +{
> + struct glink_channel *channel = to_glink_channel(ept);
> +
> + return __qcom_glink_send(channel, data, len, true);
> +}
> +
> +static int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len)
> +{
> + struct glink_channel *channel = to_glink_channel(ept);
> +
> + return __qcom_glink_send(channel, data, len, false);
> +}
> +
> +/*
> + * Finds the device_node for the glink child interested in this channel.
> + */
> +static struct device_node *qcom_glink_match_channel(struct device_node *node,
> + const char *channel)
> +{
> + struct device_node *child;
> + const char *name;
> + const char *key;
> + int ret;
> +
> + for_each_available_child_of_node(node, child) {
> + key = "qcom,glink-channels";
> + ret = of_property_read_string(child, key, &name);
> + if (ret)
> + continue;
> +
> + if (strcmp(name, channel) == 0)
> + return child;
> + }
> +
> + return NULL;
> +}
> +
> +static const struct rpmsg_device_ops glink_device_ops = {
> + .create_ept = qcom_glink_create_ept,
> +};
> +
> +static const struct rpmsg_endpoint_ops glink_endpoint_ops = {
> + .destroy_ept = qcom_glink_destroy_ept,
> + .send = qcom_glink_send,
> + .trysend = qcom_glink_trysend,
> +};
> +
> +static void qcom_glink_rpdev_release(struct device *dev)
> +{
> + struct rpmsg_device *rpdev = to_rpmsg_device(dev);
> + struct glink_channel *channel = to_glink_channel(rpdev->ept);
> +
> + channel->rpdev = NULL;
> + kfree(rpdev);
> +}
> +
> +static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,
> + char *name)
> +{
> + struct glink_channel *channel;
> + struct rpmsg_device *rpdev;
> + bool create_device = false;
> + struct device_node *node;
> + int lcid;
> + int ret;
> +
> + idr_for_each_entry(&glink->lcids, channel, lcid) {
> + if (!strcmp(channel->name, name))
> + break;
> + }
> +
> + if (!channel) {
> + channel = qcom_glink_alloc_channel(glink, name);
> + if (IS_ERR(channel))
> + return PTR_ERR(channel);
> +
> + /* The opening dance was initiated by the remote */
> + create_device = true;
> + }
> +
> + mutex_lock(&glink->idr_lock);
> + ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_KERNEL);
> + if (ret < 0) {
> + dev_err(glink->dev, "Unable to insert channel into rcid list\n");
> + mutex_unlock(&glink->idr_lock);
> + goto free_channel;
> + }
> + channel->rcid = ret;
> + mutex_unlock(&glink->idr_lock);
> +
> + complete(&channel->open_req);
> +
> + if (create_device) {
> + rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL);
> + if (!rpdev) {
> + ret = -ENOMEM;
> + goto rcid_remove;
> + }
> +
> + rpdev->ept = &channel->ept;
> + strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE);
> + rpdev->src = RPMSG_ADDR_ANY;
> + rpdev->dst = RPMSG_ADDR_ANY;
> + rpdev->ops = &glink_device_ops;
> +
> + node = qcom_glink_match_channel(glink->dev->of_node, name);
> + rpdev->dev.of_node = node;
> + rpdev->dev.parent = glink->dev;
> + rpdev->dev.release = qcom_glink_rpdev_release;
> +
> + ret = rpmsg_register_device(rpdev);
> + if (ret)
> + goto free_rpdev;
> +
> + channel->rpdev = rpdev;
> + }
> +
> + return 0;
> +
> +free_rpdev:
> + kfree(rpdev);
> +rcid_remove:
> + mutex_lock(&glink->idr_lock);
> + idr_remove(&glink->rcids, channel->rcid);
> + channel->rcid = 0;
> + mutex_unlock(&glink->idr_lock);
> +free_channel:
> + /* Release the reference, iff we took it */
> + if (create_device)
> + kref_put(&channel->refcount, qcom_glink_channel_release);
> +
> + return ret;
> +}
> +
> +static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
> +{
> + struct rpmsg_channel_info chinfo;
> + struct glink_channel *channel;
> +
> + channel = idr_find(&glink->rcids, rcid);
> + if (WARN(!channel, "close request on unknown channel\n"))
> + return;
> +
> + 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);
> + }
> +
> + qcom_glink_send_close_ack(glink, channel->rcid);
> +
> + mutex_lock(&glink->idr_lock);
> + idr_remove(&glink->rcids, channel->rcid);
> + channel->rcid = 0;
> + mutex_unlock(&glink->idr_lock);
> +
> + kref_put(&channel->refcount, qcom_glink_channel_release);
> +}
> +
> +static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)
> +{
> + struct glink_channel *channel;
> +
> + channel = idr_find(&glink->lcids, lcid);
> + if (WARN(!channel, "close ack on unknown channel\n"))
> + return;
> +
> + mutex_lock(&glink->idr_lock);
> + idr_remove(&glink->lcids, channel->lcid);
> + channel->lcid = 0;
> + mutex_unlock(&glink->idr_lock);
> +
> + kref_put(&channel->refcount, qcom_glink_channel_release);
> +}
> +
> +static void qcom_glink_work(struct work_struct *work)
> +{
> + struct qcom_glink *glink = container_of(work, struct qcom_glink,
> + rx_work);
> + struct glink_defer_cmd *dcmd;
> + struct glink_msg *msg;
> + unsigned long flags;
> + unsigned int param1;
> + unsigned int param2;
> + unsigned int cmd;
> +
> + for (;;) {
> + spin_lock_irqsave(&glink->rx_lock, flags);
> + if (list_empty(&glink->rx_queue)) {
> + spin_unlock_irqrestore(&glink->rx_lock, flags);
> + break;
> + }
> + dcmd = list_first_entry(&glink->rx_queue,
> + struct glink_defer_cmd, node);
> + list_del(&dcmd->node);
> + spin_unlock_irqrestore(&glink->rx_lock, flags);
> +
> + msg = &dcmd->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:
> + qcom_glink_send_version_ack(glink);
> + break;
> + case RPM_CMD_VERSION_ACK:
> + break;
> + case RPM_CMD_OPEN:
> + qcom_glink_rx_open(glink, param1, msg->data);
> + break;
> + case RPM_CMD_CLOSE:
> + qcom_glink_rx_close(glink, param1);
> + break;
> + case RPM_CMD_CLOSE_ACK:
> + qcom_glink_rx_close_ack(glink, param1);
> + break;
> + default:
> + WARN(1, "Unknown defer object %d\n", cmd);
> + break;
> + }
> +
> + kfree(dcmd);
> + }
> +}
> +
> +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);
> + if (!glink)
> + return ERR_PTR(-ENOMEM);
> +
> + glink->dev = dev;
> + glink->tx_pipe = tx;
> + glink->rx_pipe = rx;
> +
> + mutex_init(&glink->tx_lock);
> + spin_lock_init(&glink->rx_lock);
> + INIT_LIST_HEAD(&glink->rx_queue);
> + INIT_WORK(&glink->rx_work, qcom_glink_work);
> +
> + mutex_init(&glink->idr_lock);
> + idr_init(&glink->lcids);
> + idr_init(&glink->rcids);
> +
> + 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(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_native_intr,
> + IRQF_NO_SUSPEND | IRQF_SHARED,
> + "glink-native", glink);
> + if (ret) {
> + dev_err(dev, "failed to request IRQ\n");
> + return ERR_PTR(ret);
> + }
> +
> + glink->irq = irq;
> +
> + ret = qcom_glink_send_version(glink);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + return glink;
> +}
> +
> +static int qcom_glink_remove_device(struct device *dev, void *data)
> +{
> + device_unregister(dev);
> +
> + return 0;
> +}
> +
> +void qcom_glink_native_remove(struct qcom_glink *glink)
> +{
> + struct glink_channel *channel;
> + int cid;
> + int ret;
> +
> + disable_irq(glink->irq);
> + cancel_work_sync(&glink->rx_work);
> +
> + ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device);
> + if (ret)
> + dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret);
> +
> + /* Release any defunct local channels, waiting for close-ack */
> + idr_for_each_entry(&glink->lcids, channel, cid)
> + kref_put(&channel->refcount, qcom_glink_channel_release);
> +
> + idr_destroy(&glink->lcids);
> + idr_destroy(&glink->rcids);
> +}
> diff --git a/drivers/rpmsg/qcom_glink_native.h b/drivers/rpmsg/qcom_glink_native.h
> new file mode 100644
> index 0000000..d5627a4
> --- /dev/null
> +++ b/drivers/rpmsg/qcom_glink_native.h
> @@ -0,0 +1,38 @@
> +/*
> + * 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.
> + */
> +
> +#ifndef __QCOM_GLINK_NATIVE_H__
> +#define __QCOM_GLINK_NATIVE_H__
> +
> +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 qcom_glink;
> +
> +struct qcom_glink *qcom_glink_native_probe(struct device *dev,
> + struct qcom_glink_pipe *rx,
> + struct qcom_glink_pipe *tx);
> +void qcom_glink_native_remove(struct qcom_glink *glink);
> +
> +#endif
> diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c
> index 5f0fa0d..33daa32 100644
> --- a/drivers/rpmsg/qcom_glink_rpm.c
> +++ b/drivers/rpmsg/qcom_glink_rpm.c
> @@ -19,7 +19,6 @@
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/of_address.h>
> -#include <linux/of_irq.h>
> #include <linux/platform_device.h>
> #include <linux/regmap.h>
> #include <linux/rpmsg.h>
> @@ -28,6 +27,7 @@
> #include <linux/mailbox_client.h>
>
> #include "rpmsg_internal.h"
> +#include "qcom_glink_native.h"
>
> #define RPM_TOC_SIZE 256
> #define RPM_TOC_MAGIC 0x67727430 /* grt0 */
> @@ -37,12 +37,7 @@
> #define RPM_TX_FIFO_ID 0x61703272 /* ap2r */
> #define RPM_RX_FIFO_ID 0x72326170 /* r2ap */
>
> -#define GLINK_NAME_SIZE 32
> -
> -#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)
> +#define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native)
>
> struct rpm_toc_entry {
> __le32 id;
> @@ -50,20 +45,6 @@ struct rpm_toc_entry {
> __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;
> @@ -71,13 +52,6 @@ struct rpm_toc {
> struct rpm_toc_entry entries[];
> } __packed;
>
> -struct glink_msg {
> - __le16 cmd;
> - __le16 param1;
> - __le32 param2;
> - u8 data[];
> -} __packed;
> -
> struct glink_rpm_pipe {
> struct qcom_glink_pipe native;
>
> @@ -87,151 +61,6 @@ struct glink_rpm_pipe {
> void __iomem *fifo;
> };
>
> -/**
> - * 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
> - * @dev: reference to the associated struct device
> - * @doorbell: "rpm_hlos" ipc doorbell
> - * @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 endpoint this channel is associated with
> - * @glink: qcom_glink context handle
> - * @refcount: refcount for the channel object
> - * @recv_lock: guard for @ept.cb
> - * @name: unique channel name/identifier
> - * @lcid: channel id, in local space
> - * @rcid: channel id, in remote space
> - * @buf: receive buffer, for gathering fragments
> - * @buf_offset: write offset in @buf
> - * @buf_size: size of current @buf
> - * @open_ack: completed once remote has acked the open-request
> - * @open_req: completed once open-request has been received
> - */
> -struct glink_channel {
> - struct rpmsg_endpoint ept;
> -
> - struct rpmsg_device *rpdev;
> - struct qcom_glink *glink;
> -
> - struct kref refcount;
> -
> - spinlock_t recv_lock;
> -
> - char *name;
> - unsigned int lcid;
> - unsigned int rcid;
> -
> - void *buf;
> - int buf_offset;
> - int buf_size;
> -
> - struct completion open_ack;
> - struct completion open_req;
> -};
> -
> -#define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept)
> -
> -static const struct rpmsg_endpoint_ops glink_endpoint_ops;
> -
> -#define RPM_CMD_VERSION 0
> -#define RPM_CMD_VERSION_ACK 1
> -#define RPM_CMD_OPEN 2
> -#define RPM_CMD_CLOSE 3
> -#define RPM_CMD_OPEN_ACK 4
> -#define RPM_CMD_TX_DATA 9
> -#define RPM_CMD_CLOSE_ACK 11
> -#define RPM_CMD_TX_DATA_CONT 12
> -#define RPM_CMD_READ_NOTIF 13
> -
> -#define GLINK_FEATURE_INTENTLESS BIT(1)
> -
> -static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,
> - const char *name)
> -{
> - struct glink_channel *channel;
> -
> - channel = kzalloc(sizeof(*channel), GFP_KERNEL);
> - if (!channel)
> - return ERR_PTR(-ENOMEM);
> -
> - /* 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);
> -
> - kref_init(&channel->refcount);
> -
> - return channel;
> -}
> -
> -static void qcom_glink_channel_release(struct kref *ref)
> -{
> - struct glink_channel *channel = container_of(ref, struct glink_channel,
> - refcount);
> -
> - kfree(channel->name);
> - kfree(channel);
> -}
> -
> static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe)
> {
> struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
> @@ -247,11 +76,6 @@ static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe)
> return head - tail;
> }
>
> -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)
> {
> @@ -273,12 +97,6 @@ static void glink_rpm_rx_peak(struct qcom_glink_pipe *glink_pipe,
> }
> }
>
> -static void qcom_glink_rx_peak(struct qcom_glink *glink,
> - void *data, size_t count)
> -{
> - glink->rx_pipe->peak(glink->rx_pipe, data, count);
> -}
> -
> static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe,
> size_t count)
> {
> @@ -294,11 +112,6 @@ static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe,
> writel(tail, pipe->tail);
> }
>
> -static void qcom_glink_rx_advance(struct qcom_glink *glink, size_t count)
> -{
> - glink->rx_pipe->advance(glink->rx_pipe, count);
> -}
> -
> static size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe)
> {
> struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
> @@ -314,11 +127,6 @@ static size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe)
> return tail - head;
> }
>
> -static size_t qcom_glink_tx_avail(struct qcom_glink *glink)
> -{
> - return glink->tx_pipe->avail(glink->tx_pipe);
> -}
> -
> static unsigned int glink_rpm_tx_write_one(struct glink_rpm_pipe *pipe,
> unsigned int head,
> const void *data, size_t count)
> @@ -356,731 +164,6 @@ static void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe,
> writel(head, pipe->head);
> }
>
> -static void qcom_glink_tx_write(struct qcom_glink *glink,
> - const void *hdr, size_t hlen,
> - const void *data, size_t dlen)
> -{
> - glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen);
> -}
> -
> -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;
> -
> - 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;
> - 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;
> -}
> -
> -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);
> -
> - return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
> -}
> -
> -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);
> -
> - qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
> -}
> -
> -static void qcom_glink_send_open_ack(struct qcom_glink *glink,
> - struct glink_channel *channel)
> -{
> - struct glink_msg msg;
> -
> - msg.cmd = cpu_to_le16(RPM_CMD_OPEN_ACK);
> - msg.param1 = cpu_to_le16(channel->rcid);
> - msg.param2 = cpu_to_le32(0);
> -
> - qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
> -}
> -
> -/**
> - * qcom_glink_send_open_req() - send a RPM_CMD_OPEN request to the remote
> - * @glink:
> - * @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)
> -{
> - struct {
> - struct glink_msg msg;
> - u8 name[GLINK_NAME_SIZE];
> - } __packed req;
> - int name_len = strlen(channel->name) + 1;
> - int req_len = ALIGN(sizeof(req.msg) + name_len, 8);
> - int ret;
> -
> - kref_get(&channel->refcount);
> -
> - mutex_lock(&glink->idr_lock);
> - ret = idr_alloc_cyclic(&glink->lcids, channel,
> - RPM_GLINK_CID_MIN, RPM_GLINK_CID_MAX, GFP_KERNEL);
> - mutex_unlock(&glink->idr_lock);
> - if (ret < 0)
> - return ret;
> -
> - channel->lcid = ret;
> -
> - req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN);
> - req.msg.param1 = cpu_to_le16(channel->lcid);
> - req.msg.param2 = cpu_to_le32(name_len);
> - strcpy(req.name, channel->name);
> -
> - ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true);
> - if (ret)
> - goto remove_idr;
> -
> - return 0;
> -
> -remove_idr:
> - mutex_lock(&glink->idr_lock);
> - idr_remove(&glink->lcids, channel->lcid);
> - channel->lcid = 0;
> - mutex_unlock(&glink->idr_lock);
> -
> - return ret;
> -}
> -
> -static void qcom_glink_send_close_req(struct qcom_glink *glink,
> - struct glink_channel *channel)
> -{
> - struct glink_msg req;
> -
> - req.cmd = cpu_to_le16(RPM_CMD_CLOSE);
> - req.param1 = cpu_to_le16(channel->lcid);
> - req.param2 = 0;
> -
> - qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
> -}
> -
> -static void qcom_glink_send_close_ack(struct qcom_glink *glink,
> - unsigned int rcid)
> -{
> - struct glink_msg req;
> -
> - req.cmd = cpu_to_le16(RPM_CMD_CLOSE_ACK);
> - req.param1 = cpu_to_le16(rcid);
> - req.param2 = 0;
> -
> - qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true);
> -}
> -
> -static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
> -{
> - struct glink_defer_cmd *dcmd;
> -
> - extra = ALIGN(extra, 8);
> -
> - if (qcom_glink_rx_avail(glink) < sizeof(struct glink_msg) + extra) {
> - dev_dbg(glink->dev, "Insufficient data in rx fifo");
> - return -ENXIO;
> - }
> -
> - dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_ATOMIC);
> - if (!dcmd)
> - return -ENOMEM;
> -
> - INIT_LIST_HEAD(&dcmd->node);
> -
> - qcom_glink_rx_peak(glink, &dcmd->msg, sizeof(dcmd->msg) + extra);
> -
> - spin_lock(&glink->rx_lock);
> - list_add_tail(&dcmd->node, &glink->rx_queue);
> - spin_unlock(&glink->rx_lock);
> -
> - schedule_work(&glink->rx_work);
> - qcom_glink_rx_advance(glink, sizeof(dcmd->msg) + extra);
> -
> - return 0;
> -}
> -
> -static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
> -{
> - struct glink_channel *channel;
> - struct {
> - struct glink_msg msg;
> - __le32 chunk_size;
> - __le32 left_size;
> - } __packed hdr;
> - unsigned int chunk_size;
> - unsigned int left_size;
> - unsigned int rcid;
> -
> - if (avail < sizeof(hdr)) {
> - dev_dbg(glink->dev, "Not enough data in fifo\n");
> - return -EAGAIN;
> - }
> -
> - qcom_glink_rx_peak(glink, &hdr, sizeof(hdr));
> - chunk_size = le32_to_cpu(hdr.chunk_size);
> - left_size = le32_to_cpu(hdr.left_size);
> -
> - if (avail < sizeof(hdr) + chunk_size) {
> - dev_dbg(glink->dev, "Payload not yet in fifo\n");
> - return -EAGAIN;
> - }
> -
> - if (WARN(chunk_size % 4, "Incoming data must be word aligned\n"))
> - return -EINVAL;
> -
> - rcid = le16_to_cpu(hdr.msg.param1);
> - channel = idr_find(&glink->rcids, rcid);
> - if (!channel) {
> - 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;
> - }
> -
> - /* 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;
> -
> - channel->buf_size = chunk_size + left_size;
> - channel->buf_offset = 0;
> - }
> -
> - qcom_glink_rx_advance(glink, sizeof(hdr));
> -
> - if (channel->buf_size - channel->buf_offset < chunk_size) {
> - dev_err(glink->dev, "Insufficient space in input buffer\n");
> -
> - /* The packet header lied, drop payload */
> - qcom_glink_rx_advance(glink, chunk_size);
> - return -ENOMEM;
> - }
> -
> - qcom_glink_rx_peak(glink, channel->buf + channel->buf_offset,
> - chunk_size);
> - channel->buf_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,
> - channel->ept.priv,
> - RPMSG_ADDR_ANY);
> - }
> - spin_unlock(&channel->recv_lock);
> -
> - kfree(channel->buf);
> - channel->buf = NULL;
> - channel->buf_size = 0;
> - }
> -
> - /* Each message starts at 8 byte aligned address */
> - qcom_glink_rx_advance(glink, ALIGN(chunk_size, 8));
> -
> - return 0;
> -}
> -
> -static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
> -{
> - struct glink_channel *channel;
> -
> - channel = idr_find(&glink->lcids, lcid);
> - if (!channel) {
> - dev_err(glink->dev, "Invalid open ack packet\n");
> - return -EINVAL;
> - }
> -
> - complete(&channel->open_ack);
> -
> - return 0;
> -}
> -
> -static irqreturn_t qcom_glink_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);
> - ret = -EINVAL;
> - break;
> - }
> -
> - if (ret)
> - break;
> - }
> -
> - return IRQ_HANDLED;
> -}
> -
> -/* Locally initiated rpmsg_create_ept */
> -static struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink,
> - const char *name)
> -{
> - struct glink_channel *channel;
> - int ret;
> -
> - channel = qcom_glink_alloc_channel(glink, name);
> - if (IS_ERR(channel))
> - return ERR_CAST(channel);
> -
> - ret = qcom_glink_send_open_req(glink, channel);
> - if (ret)
> - goto release_channel;
> -
> - ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ);
> - if (!ret)
> - goto err_timeout;
> -
> - ret = wait_for_completion_timeout(&channel->open_req, 5 * HZ);
> - if (!ret)
> - goto err_timeout;
> -
> - qcom_glink_send_open_ack(glink, channel);
> -
> - return channel;
> -
> -err_timeout:
> - /* qcom_glink_send_open_req() did register the channel in lcids*/
> - mutex_lock(&glink->idr_lock);
> - idr_remove(&glink->lcids, channel->lcid);
> - mutex_unlock(&glink->idr_lock);
> -
> -release_channel:
> - /* Release qcom_glink_send_open_req() reference */
> - kref_put(&channel->refcount, qcom_glink_channel_release);
> - /* Release qcom_glink_alloc_channel() reference */
> - kref_put(&channel->refcount, qcom_glink_channel_release);
> -
> - return ERR_PTR(-ETIMEDOUT);
> -}
> -
> -/* Remote initiated rpmsg_create_ept */
> -static int qcom_glink_create_remote(struct qcom_glink *glink,
> - struct glink_channel *channel)
> -{
> - int ret;
> -
> - qcom_glink_send_open_ack(glink, channel);
> -
> - ret = qcom_glink_send_open_req(glink, channel);
> - if (ret)
> - goto close_link;
> -
> - ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ);
> - if (!ret) {
> - ret = -ETIMEDOUT;
> - goto close_link;
> - }
> -
> - return 0;
> -
> -close_link:
> - /*
> - * Send a close request to "undo" our open-ack. The close-ack will
> - * release the last reference.
> - */
> - qcom_glink_send_close_req(glink, channel);
> -
> - /* Release qcom_glink_send_open_req() reference */
> - kref_put(&channel->refcount, qcom_glink_channel_release);
> -
> - return ret;
> -}
> -
> -static struct rpmsg_endpoint *qcom_glink_create_ept(struct rpmsg_device *rpdev,
> - rpmsg_rx_cb_t cb,
> - void *priv,
> - struct rpmsg_channel_info
> - chinfo)
> -{
> - struct glink_channel *parent = to_glink_channel(rpdev->ept);
> - struct glink_channel *channel;
> - struct qcom_glink *glink = parent->glink;
> - struct rpmsg_endpoint *ept;
> - const char *name = chinfo.name;
> - int cid;
> - int ret;
> -
> - idr_for_each_entry(&glink->rcids, channel, cid) {
> - if (!strcmp(channel->name, name))
> - break;
> - }
> -
> - if (!channel) {
> - channel = qcom_glink_create_local(glink, name);
> - if (IS_ERR(channel))
> - return NULL;
> - } else {
> - ret = qcom_glink_create_remote(glink, channel);
> - if (ret)
> - return NULL;
> - }
> -
> - ept = &channel->ept;
> - ept->rpdev = rpdev;
> - ept->cb = cb;
> - ept->priv = priv;
> - ept->ops = &glink_endpoint_ops;
> -
> - return ept;
> -}
> -
> -static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept)
> -{
> - struct glink_channel *channel = to_glink_channel(ept);
> - struct qcom_glink *glink = channel->glink;
> - unsigned long flags;
> -
> - spin_lock_irqsave(&channel->recv_lock, flags);
> - channel->ept.cb = NULL;
> - spin_unlock_irqrestore(&channel->recv_lock, flags);
> -
> - /* Decouple the potential rpdev from the channel */
> - channel->rpdev = NULL;
> -
> - qcom_glink_send_close_req(glink, channel);
> -}
> -
> -static int __qcom_glink_send(struct glink_channel *channel,
> - void *data, int len, bool wait)
> -{
> - struct qcom_glink *glink = channel->glink;
> - struct {
> - struct glink_msg msg;
> - __le32 chunk_size;
> - __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);
> - req.chunk_size = cpu_to_le32(len);
> - req.left_size = cpu_to_le32(0);
> -
> - return qcom_glink_tx(glink, &req, sizeof(req), data, len, wait);
> -}
> -
> -static int qcom_glink_send(struct rpmsg_endpoint *ept, void *data, int len)
> -{
> - struct glink_channel *channel = to_glink_channel(ept);
> -
> - return __qcom_glink_send(channel, data, len, true);
> -}
> -
> -static int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len)
> -{
> - struct glink_channel *channel = to_glink_channel(ept);
> -
> - return __qcom_glink_send(channel, data, len, false);
> -}
> -
> -/*
> - * Finds the device_node for the glink child interested in this channel.
> - */
> -static struct device_node *qcom_glink_match_channel(struct device_node *node,
> - const char *channel)
> -{
> - struct device_node *child;
> - const char *name;
> - const char *key;
> - int ret;
> -
> - for_each_available_child_of_node(node, child) {
> - key = "qcom,glink-channels";
> - ret = of_property_read_string(child, key, &name);
> - if (ret)
> - continue;
> -
> - if (strcmp(name, channel) == 0)
> - return child;
> - }
> -
> - return NULL;
> -}
> -
> -static const struct rpmsg_device_ops glink_device_ops = {
> - .create_ept = qcom_glink_create_ept,
> -};
> -
> -static const struct rpmsg_endpoint_ops glink_endpoint_ops = {
> - .destroy_ept = qcom_glink_destroy_ept,
> - .send = qcom_glink_send,
> - .trysend = qcom_glink_trysend,
> -};
> -
> -static void qcom_glink_rpdev_release(struct device *dev)
> -{
> - struct rpmsg_device *rpdev = to_rpmsg_device(dev);
> - struct glink_channel *channel = to_glink_channel(rpdev->ept);
> -
> - channel->rpdev = NULL;
> - kfree(rpdev);
> -}
> -
> -static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,
> - char *name)
> -{
> - struct glink_channel *channel;
> - struct rpmsg_device *rpdev;
> - bool create_device = false;
> - int lcid;
> - int ret;
> - struct device_node *node;
> -
> - idr_for_each_entry(&glink->lcids, channel, lcid) {
> - if (!strcmp(channel->name, name))
> - break;
> - }
> -
> - if (!channel) {
> - channel = qcom_glink_alloc_channel(glink, name);
> - if (IS_ERR(channel))
> - return PTR_ERR(channel);
> -
> - /* The opening dance was initiated by the remote */
> - create_device = true;
> - }
> -
> - mutex_lock(&glink->idr_lock);
> - ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_KERNEL);
> - if (ret < 0) {
> - dev_err(glink->dev, "Unable to insert channel into rcid list\n");
> - mutex_unlock(&glink->idr_lock);
> - goto free_channel;
> - }
> - channel->rcid = ret;
> - mutex_unlock(&glink->idr_lock);
> -
> - complete(&channel->open_req);
> -
> - if (create_device) {
> - rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL);
> - if (!rpdev) {
> - ret = -ENOMEM;
> - goto rcid_remove;
> - }
> -
> - rpdev->ept = &channel->ept;
> - strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE);
> - rpdev->src = RPMSG_ADDR_ANY;
> - rpdev->dst = RPMSG_ADDR_ANY;
> - rpdev->ops = &glink_device_ops;
> -
> - node = qcom_glink_match_channel(glink->dev->of_node, name);
> - rpdev->dev.of_node = node;
> - rpdev->dev.parent = glink->dev;
> - rpdev->dev.release = qcom_glink_rpdev_release;
> -
> - ret = rpmsg_register_device(rpdev);
> - if (ret)
> - goto free_rpdev;
> -
> - channel->rpdev = rpdev;
> - }
> -
> - return 0;
> -
> -free_rpdev:
> - kfree(rpdev);
> -rcid_remove:
> - mutex_lock(&glink->idr_lock);
> - idr_remove(&glink->rcids, channel->rcid);
> - channel->rcid = 0;
> - mutex_unlock(&glink->idr_lock);
> -free_channel:
> - /* Release the reference, iff we took it */
> - if (create_device)
> - kref_put(&channel->refcount, qcom_glink_channel_release);
> -
> - return ret;
> -}
> -
> -static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
> -{
> - struct rpmsg_channel_info chinfo;
> - struct glink_channel *channel;
> -
> - channel = idr_find(&glink->rcids, rcid);
> - if (WARN(!channel, "close request on unknown channel\n"))
> - return;
> -
> - 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);
> - }
> -
> - qcom_glink_send_close_ack(glink, channel->rcid);
> -
> - mutex_lock(&glink->idr_lock);
> - idr_remove(&glink->rcids, channel->rcid);
> - channel->rcid = 0;
> - mutex_unlock(&glink->idr_lock);
> -
> - kref_put(&channel->refcount, qcom_glink_channel_release);
> -}
> -
> -static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)
> -{
> - struct glink_channel *channel;
> -
> - channel = idr_find(&glink->lcids, lcid);
> - if (WARN(!channel, "close ack on unknown channel\n"))
> - return;
> -
> - mutex_lock(&glink->idr_lock);
> - idr_remove(&glink->lcids, channel->lcid);
> - channel->lcid = 0;
> - mutex_unlock(&glink->idr_lock);
> -
> - kref_put(&channel->refcount, qcom_glink_channel_release);
> -}
> -
> -static void qcom_glink_work(struct work_struct *work)
> -{
> - struct qcom_glink *glink = container_of(work, struct qcom_glink,
> - rx_work);
> - struct glink_defer_cmd *dcmd;
> - struct glink_msg *msg;
> - unsigned long flags;
> - unsigned int param1;
> - unsigned int param2;
> - unsigned int cmd;
> -
> - for (;;) {
> - spin_lock_irqsave(&glink->rx_lock, flags);
> - if (list_empty(&glink->rx_queue)) {
> - spin_unlock_irqrestore(&glink->rx_lock, flags);
> - break;
> - }
> - dcmd = list_first_entry(&glink->rx_queue, struct glink_defer_cmd, node);
> - list_del(&dcmd->node);
> - spin_unlock_irqrestore(&glink->rx_lock, flags);
> -
> - msg = &dcmd->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:
> - qcom_glink_send_version_ack(glink);
> - break;
> - case RPM_CMD_VERSION_ACK:
> - break;
> - case RPM_CMD_OPEN:
> - qcom_glink_rx_open(glink, param1, msg->data);
> - break;
> - case RPM_CMD_CLOSE:
> - qcom_glink_rx_close(glink, param1);
> - break;
> - case RPM_CMD_CLOSE_ACK:
> - qcom_glink_rx_close_ack(glink, param1);
> - break;
> - default:
> - WARN(1, "Unknown defer object %d\n", cmd);
> - break;
> - }
> -
> - kfree(dcmd);
> - }
> -}
> -
> static int glink_rpm_parse_toc(struct device *dev,
> void __iomem *msg_ram,
> size_t msg_ram_size,
> @@ -1156,56 +239,6 @@ static int glink_rpm_parse_toc(struct device *dev,
> return -EINVAL;
> }
>
> -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);
> - if (!glink)
> - return ERR_PTR(-ENOMEM);
> -
> - glink->dev = dev;
> - glink->tx_pipe = tx;
> - glink->rx_pipe = rx;
> -
> - mutex_init(&glink->tx_lock);
> - spin_lock_init(&glink->rx_lock);
> - INIT_LIST_HEAD(&glink->rx_queue);
> - INIT_WORK(&glink->rx_work, qcom_glink_work);
> -
> - mutex_init(&glink->idr_lock);
> - idr_init(&glink->lcids);
> - idr_init(&glink->rcids);
> -
> - 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(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;
> @@ -1259,33 +292,11 @@ static int glink_rpm_probe(struct platform_device *pdev)
> return 0;
> }
>
> -static int glink_rpm_remove_device(struct device *dev, void *data)
> -{
> - device_unregister(dev);
> -
> - return 0;
> -}
> -
> static int glink_rpm_remove(struct platform_device *pdev)
> {
> struct qcom_glink *glink = platform_get_drvdata(pdev);
> - struct glink_channel *channel;
> - int cid;
> - int ret;
> -
> - disable_irq(glink->irq);
> - cancel_work_sync(&glink->rx_work);
> -
> - ret = device_for_each_child(glink->dev, NULL, glink_rpm_remove_device);
> - if (ret)
> - dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret);
> -
> - /* Release any defunct local channels, waiting for close-ack */
> - idr_for_each_entry(&glink->lcids, channel, cid)
> - kref_put(&channel->refcount, qcom_glink_channel_release);
>
> - idr_destroy(&glink->lcids);
> - idr_destroy(&glink->rcids);
> + qcom_glink_native_remove(glink);
>
> return 0;
> }
Powered by blists - more mailing lists