Hi,

The attachments are the rpmsg-tty patches based on linux LTS 5.15.
If you have any problems, feel free to contact me.

Regards,
Bowen Wang

Xiang Xiao <xiaoxiang781...@gmail.com> 于2024年3月11日周一 23:43写道:

> On Mon, Mar 11, 2024 at 11:13 PM Andre Heinemans <andre.heinem...@nxp.com>
> wrote:
>
> > Hi,
> >
> > Does the NuttX uart_rpmsg.c driver have a Linux counterpart to interact
> > with?
>
>
> Yes, the old version is here:
>
> https://lore.kernel.org/lkml/CAH2Cfb87Wacgsh=xz9h9kgwygbkxnbdbcdj4w3ups2likbt...@mail.gmail.com/
>
>
> > I want to achieve a virtual uart connection through rpmsg on an imx8mp
> > between NuttX (m7) and Linux (a53).
> > The tty_rpmsg.c driver in mainline linux does not seem compatible as it
> > read and writes the raw data directly from the rpmsg buffers.
>
>
> The mainline version comes from ST developer, which lacks of the flow
> control and very easy to lose the data with the fast transaction.
>
>
> > Whereas the NuttX driver uses a struct ‘uart_rpmsg_write_s’ which
> contains
> > the raw data in one of its fields.
> >
> >
> We renewed the rpmsg tty driver on top of Linux 5.14 recently, which works
> perfectly with the NuttX mainline rpmsg_uart driver.
> Bowen could share the implementation tomorrow.
>
>
>
> > Kind regards,
> > Andre
> >
>
From 32118f9e7f00bd874cec91f8f23d8647a9576e7c Mon Sep 17 00:00:00 2001
From: Bowen Wang <wangbowen6@xiaomi.com>
Date: Fri, 15 Dec 2023 19:29:34 +0800
Subject: [PATCH 2/4] rpmsg: support the zero copy transimit

VELAPLATFO-18516

by rpmsg_get_tx_payload_buffer and rpmsg_sendxxx_nocopy

Change-Id: I0b9ae403783232e33d736eee9be888c33317d3e0
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
---
 drivers/rpmsg/rpmsg_core.c       | 110 +++++++++++++++++
 drivers/rpmsg/rpmsg_internal.h   |  31 +++--
 drivers/rpmsg/virtio_rpmsg_bus.c | 203 ++++++++++++++++---------------
 include/linux/rpmsg.h            |  42 +++++++
 4 files changed, 280 insertions(+), 106 deletions(-)

diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index 43f40d3713a9..65be0d411403 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -133,6 +133,116 @@ void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
 }
 EXPORT_SYMBOL(rpmsg_destroy_ept);
 
+/**
+ * rpmsg_get_tx_payload_buffer() - get the payload buffer from the pool
+ * @ept: the rpmsg endpoint
+ * @len: length of payload
+ * @wait: wait if the pool is empty
+ *
+ * Returns the buffer on success and an appropriate error value on failure.
+ */
+void *rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
+				  unsigned int *len, bool wait)
+{
+	if (WARN_ON(!ept))
+		return ERR_PTR(-EINVAL);
+	if (!ept->ops->get_tx_payload_buffer)
+		return ERR_PTR(-ENXIO);
+
+	return ept->ops->get_tx_payload_buffer(ept, len, wait);
+}
+EXPORT_SYMBOL(rpmsg_get_tx_payload_buffer);
+
+/**
+ * rpmsg_send_nocopy() - send a message across to the remote processor
+ * @ept: the rpmsg endpoint
+ * @data: payload of message
+ * @len: length of payload
+ *
+ * This function sends @data of length @len on the @ept endpoint.
+ * The message will be sent to the remote processor which the @ept
+ * endpoint belongs to, using @ept's address and its associated rpmsg
+ * device destination addresses.
+ * In case there are no TX buffers available, the function will block until
+ * one becomes available, or a timeout of 15 seconds elapses. When the latter
+ * happens, -ERESTARTSYS is returned.
+ *
+ * Can only be called from process context (for now).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+int rpmsg_send_nocopy(struct rpmsg_endpoint *ept, void *data, int len)
+{
+	if (WARN_ON(!ept))
+		return -EINVAL;
+	if (!ept->ops->send_nocopy)
+		return -ENXIO;
+
+	return ept->ops->send_nocopy(ept, data, len);
+}
+EXPORT_SYMBOL(rpmsg_send_nocopy);
+
+/**
+ * rpmsg_sendto_nocopy() - send a message across to the remote processor, specify dst
+ * @ept: the rpmsg endpoint
+ * @data: payload of message
+ * @len: length of payload
+ * @dst: destination address
+ *
+ * This function sends @data of length @len to the remote @dst address.
+ * The message will be sent to the remote processor which the @ept
+ * endpoint belongs to, using @ept's address as source.
+ * In case there are no TX buffers available, the function will block until
+ * one becomes available, or a timeout of 15 seconds elapses. When the latter
+ * happens, -ERESTARTSYS is returned.
+ *
+ * Can only be called from process context (for now).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+int rpmsg_sendto_nocopy(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
+{
+	if (WARN_ON(!ept))
+		return -EINVAL;
+	if (!ept->ops->sendto_nocopy)
+		return -ENXIO;
+
+	return ept->ops->sendto_nocopy(ept, data, len, dst);
+}
+EXPORT_SYMBOL(rpmsg_sendto_nocopy);
+
+/**
+ * rpmsg_send_offchannel_nocopy() - send a message using explicit src/dst addresses
+ * @ept: the rpmsg endpoint
+ * @src: source address
+ * @dst: destination address
+ * @data: payload of message
+ * @len: length of payload
+ *
+ * This function sends @data of length @len to the remote @dst address,
+ * and uses @src as the source address.
+ * The message will be sent to the remote processor which the @ept
+ * endpoint belongs to.
+ * In case there are no TX buffers available, the function will block until
+ * one becomes available, or a timeout of 15 seconds elapses. When the latter
+ * happens, -ERESTARTSYS is returned.
+ *
+ * Can only be called from process context (for now).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+int rpmsg_send_offchannel_nocopy(struct rpmsg_endpoint *ept, u32 src, u32 dst,
+				 void *data, int len)
+{
+	if (WARN_ON(!ept))
+		return -EINVAL;
+	if (!ept->ops->send_offchannel_nocopy)
+		return -ENXIO;
+
+	return ept->ops->send_offchannel_nocopy(ept, src, dst, data, len);
+}
+EXPORT_SYMBOL(rpmsg_send_offchannel_nocopy);
+
 /**
  * rpmsg_send() - send a message across to the remote processor
  * @ept: the rpmsg endpoint
diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h
index bf24c7494b9f..9caaeb2dcd9a 100644
--- a/drivers/rpmsg/rpmsg_internal.h
+++ b/drivers/rpmsg/rpmsg_internal.h
@@ -48,16 +48,20 @@ struct rpmsg_device_ops {
 
 /**
  * struct rpmsg_endpoint_ops - indirection table for rpmsg_endpoint operations
- * @destroy_ept:	see @rpmsg_destroy_ept(), required
- * @send:		see @rpmsg_send(), required
- * @sendto:		see @rpmsg_sendto(), optional
- * @send_offchannel:	see @rpmsg_send_offchannel(), optional
- * @trysend:		see @rpmsg_trysend(), required
- * @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
+ * @destroy_ept:		see @rpmsg_destroy_ept(), required
+ * @get_tx_payload_buffer:	see @rpmsg_get_tx_payload_buffer(), optional
+ * @send_nocopy:		see @rpmsg_send_nocopy(), optional
+ * @sendto_nocopy:		see @rpmsg_sendto_nocopy(), optional
+ * @send_offchannel_nocopy:	see @rpmsg_send_offchannel_nocopy(), optional
+ * @send:			see @rpmsg_send(), required
+ * @sendto:			see @rpmsg_sendto(), optional
+ * @send_offchannel:		see @rpmsg_send_offchannel(), optional
+ * @trysend:			see @rpmsg_trysend(), required
+ * @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 +70,13 @@ struct rpmsg_device_ops {
 struct rpmsg_endpoint_ops {
 	void (*destroy_ept)(struct rpmsg_endpoint *ept);
 
+	void *(*get_tx_payload_buffer)(struct rpmsg_endpoint *ept,
+				       unsigned int *len, bool wait);
+	int (*send_nocopy)(struct rpmsg_endpoint *ept, void *data, int len);
+	int (*sendto_nocopy)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst);
+	int (*send_offchannel_nocopy)(struct rpmsg_endpoint *ept, u32 src, u32 dst,
+				      void *data, int len);
+
 	int (*send)(struct rpmsg_endpoint *ept, void *data, int len);
 	int (*sendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst);
 	int (*send_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst,
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 18d485f1b1a2..783810e670a0 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -141,21 +141,33 @@ struct virtio_rpmsg_channel {
 #define RPMSG_RESERVED_ADDRESSES	(1024)
 
 static void virtio_rpmsg_destroy_ept(struct rpmsg_endpoint *ept);
+static void *virtio_rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
+						unsigned int *len, bool wait);
+static int virtio_rpmsg_send_offchannel_nocopy(struct rpmsg_endpoint *ept, u32 src,
+					       u32 dst, void *data, int len);
+static int virtio_rpmsg_send_nocopy(struct rpmsg_endpoint *ept, void *data, int len);
+static int virtio_rpmsg_sendto_nocopy(struct rpmsg_endpoint *ept, void *data, int len,
+				      u32 dst);
+static int virtio_rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src,
+					u32 dst, void *data, int len);
 static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len);
 static int virtio_rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len,
 			       u32 dst);
-static int virtio_rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src,
-					u32 dst, void *data, int len);
+static int virtio_rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src,
+					   u32 dst, void *data, int len);
 static int virtio_rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len);
 static int virtio_rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data,
 				  int len, u32 dst);
-static int virtio_rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src,
-					   u32 dst, void *data, int len);
+
 static struct rpmsg_device *__rpmsg_create_channel(struct virtproc_info *vrp,
 						   struct rpmsg_channel_info *chinfo);
 
 static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
 	.destroy_ept = virtio_rpmsg_destroy_ept,
+	.get_tx_payload_buffer = virtio_rpmsg_get_tx_payload_buffer,
+	.send_nocopy = virtio_rpmsg_send_nocopy,
+	.sendto_nocopy = virtio_rpmsg_sendto_nocopy,
+	.send_offchannel_nocopy = virtio_rpmsg_send_offchannel_nocopy,
 	.send = virtio_rpmsg_send,
 	.sendto = virtio_rpmsg_sendto,
 	.send_offchannel = virtio_rpmsg_send_offchannel,
@@ -441,9 +453,8 @@ static struct rpmsg_device *__rpmsg_create_channel(struct virtproc_info *vrp,
 }
 
 /* super simple buffer "allocator" that is just enough for now */
-static void *get_a_tx_buf(struct virtproc_info *vrp)
+static void *get_a_tx_buf(struct virtproc_info *vrp, unsigned int *len)
 {
-	unsigned int len;
 	void *ret;
 
 	/* support multiple concurrent senders */
@@ -454,8 +465,9 @@ static void *get_a_tx_buf(struct virtproc_info *vrp)
 		ret = vrp->sbufs + vrp->sbuf_size * vrp->last_sbuf++;
 	/* or recycle a used one */
 	else
-		ret = virtqueue_get_buf(vrp->svq, &len);
+		ret = virtqueue_get_buf(vrp->svq, len);
 
+	*len = vrp->sbuf_size;
 	mutex_unlock(&vrp->tx_lock);
 
 	return ret;
@@ -517,75 +529,20 @@ static void rpmsg_downref_sleepers(struct virtproc_info *vrp)
 	mutex_unlock(&vrp->tx_lock);
 }
 
-/**
- * rpmsg_send_offchannel_raw() - send a message across to the remote processor
- * @rpdev: the rpmsg channel
- * @src: source address
- * @dst: destination address
- * @data: payload of message
- * @len: length of payload
- * @wait: indicates whether caller should block in case no TX buffers available
- *
- * This function is the base implementation for all of the rpmsg sending API.
- *
- * It will send @data of length @len to @dst, and say it's from @src. The
- * message will be sent to the remote processor which the @rpdev channel
- * belongs to.
- *
- * The message is sent using one of the TX buffers that are available for
- * communication with this remote processor.
- *
- * If @wait is true, the caller will be blocked until either a TX buffer is
- * available, or 15 seconds elapses (we don't want callers to
- * sleep indefinitely due to misbehaving remote processors), and in that
- * case -ERESTARTSYS is returned. The number '15' itself was picked
- * arbitrarily; there's little point in asking drivers to provide a timeout
- * value themselves.
- *
- * Otherwise, if @wait is false, and there are no TX buffers available,
- * the function will immediately fail, and -ENOMEM will be returned.
- *
- * Normally drivers shouldn't use this function directly; instead, drivers
- * should use the appropriate rpmsg_{try}send{to, _offchannel} API
- * (see include/linux/rpmsg.h).
- *
- * Returns 0 on success and an appropriate error value on failure.
- */
-static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
-				     u32 src, u32 dst,
-				     void *data, int len, bool wait)
+static void *virtio_rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
+						unsigned int *len, bool wait)
 {
+	struct rpmsg_device *rpdev = ept->rpdev;
 	struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev);
 	struct virtproc_info *vrp = vch->vrp;
 	struct device *dev = &rpdev->dev;
-	struct scatterlist sg;
 	struct rpmsg_hdr *msg;
 	int err;
 
-	/* bcasting isn't allowed */
-	if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) {
-		dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst);
-		return -EINVAL;
-	}
-
-	/*
-	 * We currently use fixed-sized buffers, and therefore the payload
-	 * length is limited.
-	 *
-	 * One of the possible improvements here is either to support
-	 * user-provided buffers (and then we can also support zero-copy
-	 * messaging), or to improve the buffer allocator, to support
-	 * variable-length buffer sizes.
-	 */
-	if (len > vrp->sbuf_size - sizeof(struct rpmsg_hdr)) {
-		dev_err(dev, "message is too big (%d)\n", len);
-		return -EMSGSIZE;
-	}
-
 	/* grab a buffer */
-	msg = get_a_tx_buf(vrp);
+	msg = get_a_tx_buf(vrp, len);
 	if (!msg && !wait)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	/* no free buffer ? wait for one (but bail after 15 seconds) */
 	while (!msg) {
@@ -599,7 +556,7 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
 		 * if later this happens to be required, it'd be easy to add.
 		 */
 		err = wait_event_interruptible_timeout(vrp->sendq,
-					(msg = get_a_tx_buf(vrp)),
+					(msg = get_a_tx_buf(vrp, len)),
 					msecs_to_jiffies(15000));
 
 		/* disable "tx-complete" interrupts if we're the last sleeper */
@@ -608,16 +565,52 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
 		/* timeout ? */
 		if (!err) {
 			dev_err(dev, "timeout waiting for a tx buffer\n");
-			return -ERESTARTSYS;
+			return ERR_PTR(-ERESTARTSYS);
 		}
 	}
 
+	*len -= sizeof(*msg);
+	return msg + 1;
+}
+
+static int virtio_rpmsg_send_offchannel_nocopy(struct rpmsg_endpoint *ept, u32 src,
+					       u32 dst, void *data, int len)
+{
+	struct rpmsg_device *rpdev = ept->rpdev;
+	struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev);
+	struct virtproc_info *vrp = vch->vrp;
+	struct device *dev = &rpdev->dev;
+	struct scatterlist sg;
+	struct rpmsg_hdr *msg;
+	int err;
+
+	/* bcasting isn't allowed */
+	if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) {
+		dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst);
+		return -EINVAL;
+	}
+
+	/*
+	 * We currently use fixed-sized buffers, and therefore the payload
+	 * length is limited.
+	 *
+	 * One of the possible improvements here is either to support
+	 * user-provided buffers (and then we can also support zero-copy
+	 * messaging), or to improve the buffer allocator, to support
+	 * variable-length buffer sizes.
+	 */
+	if (len > vrp->sbuf_size - sizeof(struct rpmsg_hdr)) {
+		dev_err(dev, "message is too big (%d)\n", len);
+		return -EMSGSIZE;
+	}
+
+	msg = data - sizeof(*msg);
+
 	msg->len = cpu_to_rpmsg16(rpdev, len);
 	msg->flags = 0;
 	msg->src = cpu_to_rpmsg32(rpdev, src);
 	msg->dst = cpu_to_rpmsg32(rpdev, dst);
 	msg->reserved = 0;
-	memcpy(msg->data, data, len);
 
 	dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n",
 		src, dst, len, msg->flags, msg->reserved);
@@ -649,54 +642,72 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
 	return err;
 }
 
-static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len)
+static int virtio_rpmsg_send_nocopy(struct rpmsg_endpoint *ept, void *data, int len)
 {
-	struct rpmsg_device *rpdev = ept->rpdev;
-	u32 src = ept->addr, dst = rpdev->dst;
-
-	return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true);
+	return virtio_rpmsg_send_offchannel_nocopy(ept, ept->addr, ept->rpdev->dst,
+						   data, len);
 }
 
-static int virtio_rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len,
-			       u32 dst)
+static int virtio_rpmsg_sendto_nocopy(struct rpmsg_endpoint *ept, void *data, int len,
+				      u32 dst)
 {
-	struct rpmsg_device *rpdev = ept->rpdev;
-	u32 src = ept->addr;
-
-	return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true);
+	return virtio_rpmsg_send_offchannel_nocopy(ept, ept->addr, dst, data, len);
 }
 
 static int virtio_rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src,
 					u32 dst, void *data, int len)
 {
-	struct rpmsg_device *rpdev = ept->rpdev;
+	unsigned int max;
+	void *buf;
 
-	return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true);
-}
+	buf = virtio_rpmsg_get_tx_payload_buffer(ept, &max, true);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
 
-static int virtio_rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len)
-{
-	struct rpmsg_device *rpdev = ept->rpdev;
-	u32 src = ept->addr, dst = rpdev->dst;
+	if (len > max)
+		len = max;
+	memcpy(buf, data, len);
 
-	return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false);
+	return virtio_rpmsg_send_offchannel_nocopy(ept, src, dst, buf, len);
 }
 
-static int virtio_rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data,
-				  int len, u32 dst)
+static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len)
 {
-	struct rpmsg_device *rpdev = ept->rpdev;
-	u32 src = ept->addr;
+	return virtio_rpmsg_send_offchannel(ept, ept->addr, ept->rpdev->dst, data, len);
+}
 
-	return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false);
+static int virtio_rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len,
+			       u32 dst)
+{
+	return virtio_rpmsg_send_offchannel(ept, ept->addr, dst, data, len);
 }
 
 static int virtio_rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src,
 					   u32 dst, void *data, int len)
 {
-	struct rpmsg_device *rpdev = ept->rpdev;
+	unsigned int max;
+	void *buf;
+
+	buf = virtio_rpmsg_get_tx_payload_buffer(ept, &max, false);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	if (len > max)
+		len = max;
+	memcpy(buf, data, len);
 
-	return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false);
+	return virtio_rpmsg_send_offchannel_nocopy(ept, src, dst, buf, len);
+}
+
+static int virtio_rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len)
+{
+	return virtio_rpmsg_trysend_offchannel(ept, ept->addr, ept->rpdev->dst, data, len);
+}
+
+static int virtio_rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data,
+				  int len, u32 dst)
+{
+	return virtio_rpmsg_trysend_offchannel(ept, ept->addr, dst, data, len);
 }
 
 static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
index 3579b670336a..991cb8a25653 100644
--- a/include/linux/rpmsg.h
+++ b/include/linux/rpmsg.h
@@ -199,6 +199,14 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *,
 					rpmsg_rx_cb_t cb, void *priv,
 					struct rpmsg_channel_info chinfo);
 
+void *rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
+				  unsigned int *len, bool wait);
+
+int rpmsg_send_nocopy(struct rpmsg_endpoint *ept, void *data, int len);
+int rpmsg_sendto_nocopy(struct rpmsg_endpoint *ept, void *data, int len, u32 dst);
+int rpmsg_send_offchannel_nocopy(struct rpmsg_endpoint *ept, u32 src, u32 dst,
+				 void *data, int len);
+
 int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len);
 int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst);
 int rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,
@@ -271,6 +279,40 @@ static inline struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev
 	return NULL;
 }
 
+static inline void *rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
+						unsigned int *len, bool wait)
+{
+	/* This shouldn't be possible */
+	WARN_ON(1);
+
+	return ERR_PTR(-ENXIO);
+}
+
+static inline int rpmsg_send_nocopy(struct rpmsg_endpoint *ept, void *data, int len)
+{
+	/* This shouldn't be possible */
+	WARN_ON(1);
+
+	return -ENXIO;
+}
+
+static inline int rpmsg_sendto_nocopy(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
+{
+	/* This shouldn't be possible */
+	WARN_ON(1);
+
+	return -ENXIO;
+}
+
+static inline int rpmsg_send_offchannel_nocopy(struct rpmsg_endpoint *ept, u32 src, u32 dst,
+					       void *data, int len)
+{
+	/* This shouldn't be possible */
+	WARN_ON(1);
+
+	return -ENXIO;
+}
+
 static inline int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len)
 {
 	/* This shouldn't be possible */
-- 
2.34.1

From f6a68fbdd8c4c15c23c3d8fe75135e9859cbf63b Mon Sep 17 00:00:00 2001
From: Bowen Wang <wangbowen6@xiaomi.com>
Date: Mon, 22 Jan 2024 17:14:44 +0800
Subject: [PATCH 4/4] drivers/tty/rpmsg_tty.c: add rpmsg tty support

VELAPLATFO-18516

With this rpmsg driver, Linux can communicate with Vela through
rpmsg tty.

Change-Id: I52d9d7c2a5bb2b1ff95b1d916c6c4cb965b08a55
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
---
 drivers/tty/Kconfig     |  13 ++
 drivers/tty/Makefile    |   1 +
 drivers/tty/rpmsg_tty.c | 362 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 376 insertions(+)
 create mode 100644 drivers/tty/rpmsg_tty.c

diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 23cc988c68a4..dfe2e0f652b6 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -368,6 +368,19 @@ config VCC
 
 source "drivers/tty/hvc/Kconfig"
 
+config RPMSG_TTY
+	tristate "RPMSG based TTY Driver"
+	depends on RPMSG
+	help
+	  Say Y here to export rpmsg endpoints as tty device usually found in
+	  /dev/ttyRx. They make it possible for user-space programs to send
+	  and receive rpmsg packets through tty interface.
+
+	  Here is some possible use case:
+	  1.Connect to the embedded processor RTOS shell
+	  2.Communicate with the embedded GPS subsystem through NMEA
+	  3.Communicate with the embedded modem subsystem through ATCMD
+
 endif # TTY
 
 source "drivers/tty/serdev/Kconfig"
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index a2bd75fbaaa4..07aca5184a55 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -26,5 +26,6 @@ obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
 obj-$(CONFIG_GOLDFISH_TTY)	+= goldfish.o
 obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o
 obj-$(CONFIG_VCC)		+= vcc.o
+obj-$(CONFIG_RPMSG_TTY)		+= rpmsg_tty.o
 
 obj-y += ipwireless/
diff --git a/drivers/tty/rpmsg_tty.c b/drivers/tty/rpmsg_tty.c
new file mode 100644
index 000000000000..7f07039241b4
--- /dev/null
+++ b/drivers/tty/rpmsg_tty.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017 Pinecone Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/rpmsg.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/workqueue.h>
+
+#define RPMSG_TTY_WRITE		0
+#define RPMSG_TTY_WAKEUP	1
+
+struct rpmsg_tty_header {
+	u32			command:31;
+	u32			response:1;
+	s32			result;
+	u64			cookie;
+} __packed;
+
+struct rpmsg_tty_write {
+	struct rpmsg_tty_header	header;
+	u32			count;
+	u32			resolved;
+	char			data[0];
+} __packed;
+
+#define rpmsg_tty_wakeup	rpmsg_tty_header
+
+struct rpmsg_tty_cookie {
+	struct completion	done;
+	s32			result;
+};
+
+struct tty_rpmsg_port {
+	struct tty_port		port;
+	struct work_struct	work;
+	struct tty_driver	*driver;
+	int			xmit_size;
+};
+
+static struct tty_rpmsg_port *to_tty_rpmsg_port(struct tty_port *p)
+{
+	return container_of(p, struct tty_rpmsg_port, port);
+}
+
+static int tty_rpmsg_port_activate(struct tty_port *p, struct tty_struct *tty)
+{
+	struct tty_rpmsg_port *port = to_tty_rpmsg_port(p);
+
+	port->xmit_size = 0;
+	return tty_port_alloc_xmit_buf(p);
+}
+
+static void tty_rpmsg_port_shutdown(struct tty_port *p)
+{
+	tty_port_free_xmit_buf(p);
+}
+
+static void tty_rpmsg_port_destruct(struct tty_port *p)
+{
+	/* don't need free manually since it come from devm_kzalloc */
+}
+
+static const struct tty_port_operations tty_rpmsg_port_ops = {
+	.activate = tty_rpmsg_port_activate,
+	.shutdown = tty_rpmsg_port_shutdown,
+	.destruct = tty_rpmsg_port_destruct,
+};
+
+static int tty_rpmsg_open(struct tty_struct *tty, struct file *filp)
+{
+	tty_flip_buffer_push(tty->port);
+	return tty_port_open(tty->port, tty, filp);
+}
+
+static void tty_rpmsg_close(struct tty_struct *tty, struct file *filp)
+{
+	tty_port_close(tty->port, tty, filp);
+}
+
+static unsigned int tty_rpmsg_write_room(struct tty_struct *tty)
+{
+	struct tty_port *p = tty->port;
+	struct tty_rpmsg_port *port = to_tty_rpmsg_port(p);
+	struct rpmsg_device *rpdev = dev_get_drvdata(tty->dev);
+	int space = rpmsg_get_tx_buffer_size(rpdev->ept);
+
+	space -= sizeof(struct rpmsg_tty_write);
+	if (space > PAGE_SIZE)
+		space = PAGE_SIZE;
+
+	mutex_lock(&p->buf_mutex);
+	space -= port->xmit_size;
+	mutex_unlock(&p->buf_mutex);
+
+	return (unsigned int)(space < 0 ? 0 : space);
+}
+
+static int tty_rpmsg_do_write(struct tty_struct *tty,
+			      const unsigned char *buf, int count)
+{
+	struct rpmsg_device *rpdev = dev_get_drvdata(tty->dev);
+	struct rpmsg_tty_cookie cookie;
+	struct rpmsg_tty_write *msg;
+	unsigned int max;
+	int ret;
+
+	msg = rpmsg_get_tx_payload_buffer(rpdev->ept, &max, true);
+	if (IS_ERR(msg))
+		return PTR_ERR(msg);
+
+	if (sizeof(*msg) + count > max)
+		count = max - sizeof(*msg);
+
+	memset(msg, 0, sizeof(*msg));
+
+	msg->header.command = RPMSG_TTY_WRITE;
+	msg->header.result  = -ENXIO;
+	msg->header.cookie  = (uintptr_t)&cookie;
+
+	msg->count = count;
+	memcpy(msg->data, buf, count);
+
+	memset(&cookie, 0, sizeof(cookie));
+	init_completion(&cookie.done);
+
+	ret = rpmsg_send_nocopy(rpdev->ept, msg, sizeof(*msg) + count);
+	if (ret < 0)
+		return ret;
+
+	wait_for_completion(&cookie.done);
+	return cookie.result;
+}
+
+static int tty_rpmsg_write(struct tty_struct *tty,
+			   const unsigned char *buf, int count)
+{
+	struct tty_port *p = tty->port;
+	struct tty_rpmsg_port *port = to_tty_rpmsg_port(p);
+	int space, written;
+
+	space = tty_rpmsg_write_room(tty);
+	if (count > space)
+		count = space;
+
+	written = count;
+
+	mutex_lock(&p->buf_mutex);
+	if (port->xmit_size) {
+		memcpy(p->xmit_buf + port->xmit_size, buf, count);
+		port->xmit_size += count;
+
+		buf = p->xmit_buf;
+		count = port->xmit_size;
+	}
+
+	if (count) {
+		int ret = tty_rpmsg_do_write(tty, buf, count);
+
+		if (ret > 0) {
+			memmove(p->xmit_buf, buf + ret, count - ret);
+			port->xmit_size = count - ret;
+		} else if (p->xmit_buf != buf) {
+			memcpy(p->xmit_buf, buf, count);
+			port->xmit_size = count;
+		}
+	}
+	mutex_unlock(&p->buf_mutex);
+
+	return written;
+}
+
+static void tty_rpmsg_write_work(struct work_struct *work)
+{
+	struct tty_rpmsg_port *port =
+		container_of(work, struct tty_rpmsg_port, work);
+	struct tty_struct *tty = tty_port_tty_get(&port->port);
+
+	if (tty) {
+		tty_rpmsg_write(tty, NULL, 0);
+		tty_kref_put(tty);
+	}
+
+	tty_port_tty_wakeup(&port->port);
+}
+
+static void tty_rpmsg_hangup(struct tty_struct *tty)
+{
+	tty_port_hangup(tty->port);
+}
+
+static const struct tty_operations tty_rpmsg_ops = {
+	.open = tty_rpmsg_open,
+	.close = tty_rpmsg_close,
+	.write = tty_rpmsg_write,
+	.write_room = tty_rpmsg_write_room,
+	.hangup = tty_rpmsg_hangup,
+};
+
+static int rpmsg_tty_probe(struct rpmsg_device *rpdev)
+{
+	struct tty_rpmsg_port *port;
+	struct device *dev;
+	u32 max_size;
+	int ret;
+
+	port = devm_kzalloc(&rpdev->dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+	dev_set_drvdata(&rpdev->dev, &port->port);
+
+	tty_port_init(&port->port);
+	port->port.ops = &tty_rpmsg_port_ops;
+	INIT_WORK(&port->work, tty_rpmsg_write_work);
+
+	/*
+	 * don't limit receive buffer size by default since:
+	 * 1.tty core don't notify us when somebody read the data from buffer
+	 * 2.it's hard to send RPMSG_TTY_WAKEUP once some space is available
+	 */
+	if (of_property_read_u32(rpdev->dev.of_node,
+				 "max-size", &max_size) < 0) {
+		max_size = INT_MAX;
+	}
+	tty_buffer_set_limit(&port->port, max_size);
+
+	port->driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW |
+		TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_UNNUMBERED_NODE);
+	if (IS_ERR(port->driver)) {
+		ret = PTR_ERR(port->driver);
+		goto put_port;
+	}
+
+	port->driver->driver_name  = rpdev->id.name;
+	port->driver->name         = rpdev->id.name + 6; /* skip "rpmsg-" */
+	port->driver->type         = TTY_DRIVER_TYPE_SERIAL |
+				     TTY_DRIVER_REAL_RAW;
+	port->driver->subtype      = SERIAL_TYPE_NORMAL;
+
+	port->driver->init_termios = tty_std_termios;
+	port->driver->init_termios.c_iflag = 0;
+	port->driver->init_termios.c_oflag = 0;
+	port->driver->init_termios.c_lflag = 0;
+
+	tty_set_operations(port->driver, &tty_rpmsg_ops);
+	ret = tty_register_driver(port->driver);
+	if (ret < 0)
+		goto put_tty;
+
+	dev = tty_port_register_device_attr(&port->port, port->driver,
+					    0, &rpdev->dev, rpdev, NULL);
+	if (IS_ERR(dev)) {
+		ret = PTR_ERR(dev);
+		goto tty_unregister;
+	}
+
+	return 0;
+
+tty_unregister:
+	tty_unregister_driver(port->driver);
+put_tty:
+	tty_driver_kref_put(port->driver);
+put_port:
+	tty_port_put(&port->port);
+	return ret;
+}
+
+static void rpmsg_tty_remove(struct rpmsg_device *rpdev)
+{
+	struct tty_port *p = dev_get_drvdata(&rpdev->dev);
+	struct tty_rpmsg_port *port = to_tty_rpmsg_port(p);
+
+	cancel_work_sync(&port->work);
+	tty_unregister_device(port->driver, 0);
+	tty_unregister_driver(port->driver);
+	tty_driver_kref_put(port->driver);
+	tty_port_put(p);
+}
+
+static int rpmsg_tty_write_handler(struct rpmsg_device *rpdev,
+				   void *data, int len, void *priv, u32 src)
+{
+	struct tty_port *p = dev_get_drvdata(&rpdev->dev);
+	struct rpmsg_tty_write *msg = data;
+	int ret;
+
+	ret = tty_insert_flip_string(p, msg->data, msg->count);
+	if (ret > 0)
+		tty_flip_buffer_push(p);
+
+	msg->header.response = 1;
+	msg->header.result = ret;
+
+	return rpmsg_send(rpdev->ept, msg, sizeof(*msg));
+}
+
+static int rpmsg_tty_wakeup_handler(struct rpmsg_device *rpdev,
+				    void *data, int len, void *priv, u32 src)
+{
+	struct tty_port *p = dev_get_drvdata(&rpdev->dev);
+	struct tty_rpmsg_port *port = to_tty_rpmsg_port(p);
+
+	schedule_work(&port->work);
+	return 0;
+}
+
+static const rpmsg_rx_cb_t rpmsg_tty_handler[] = {
+	[RPMSG_TTY_WRITE]  = rpmsg_tty_write_handler,
+	[RPMSG_TTY_WAKEUP] = rpmsg_tty_wakeup_handler,
+};
+
+static int rpmsg_tty_callback(struct rpmsg_device *rpdev,
+			      void *data, int len, void *priv, u32 src)
+{
+	struct rpmsg_tty_header *hdr = data;
+	u32 cmd = hdr->command;
+	int ret = -EINVAL;
+
+	if (hdr->response) {
+		struct rpmsg_tty_cookie *cookie =
+			(struct rpmsg_tty_cookie *)hdr->cookie;
+
+		cookie->result = hdr->result;
+		complete(&cookie->done);
+	} else if (cmd < ARRAY_SIZE(rpmsg_tty_handler)) {
+		ret = rpmsg_tty_handler[cmd](rpdev, data, len, priv, src);
+	} else {
+		dev_err(&rpdev->dev, "invalid command %u\n", cmd);
+	}
+
+	return ret;
+}
+
+static int rpmsg_tty_match(struct rpmsg_device *dev, struct rpmsg_driver *drv)
+{
+	const char *devname = dev->id.name;
+	const char *drvname = "rpmsg-tty";
+
+	/* match all device start with rpmsg-tty */
+	return strncmp(devname, drvname, strlen(drvname)) == 0;
+}
+
+static struct rpmsg_driver rpmsg_tty_driver = {
+	.drv = {
+		.name	= "rpmsg_tty",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= rpmsg_tty_probe,
+	.remove		= rpmsg_tty_remove,
+	.callback	= rpmsg_tty_callback,
+	.match		= rpmsg_tty_match,
+};
+
+module_rpmsg_driver(rpmsg_tty_driver);
+
+MODULE_ALIAS("rpmsg:rpmsg_tty");
+MODULE_AUTHOR("Xiang Xiao <xiaoxiang@pinecone.net>");
+MODULE_DESCRIPTION("TTY over the rpmsg channel");
+MODULE_LICENSE("GPL v2");
-- 
2.34.1

From 74972386eb40cd1f3238f8edb6d7e9b9689f5e13 Mon Sep 17 00:00:00 2001
From: Bowen Wang <wangbowen6@xiaomi.com>
Date: Fri, 15 Dec 2023 19:07:32 +0800
Subject: [PATCH 1/4] rpmsg: rpmsg_driver add match callback

VELAPLATFO-18516

so the driver could decide whether support a particular device at
runtime.

Change-Id: Idda3f8e9ce2238eb0cf7609db73e443d47ee547b
Signed-off-by: Bowen Wang <wangbowen6@xiaomi.com>
---
 drivers/rpmsg/rpmsg_core.c | 3 +++
 include/linux/rpmsg.h      | 1 +
 2 files changed, 4 insertions(+)

diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index fe7417f1da5f..43f40d3713a9 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -517,6 +517,9 @@ static int rpmsg_dev_match(struct device *dev, struct device_driver *drv)
 	if (rpdev->driver_override)
 		return !strcmp(rpdev->driver_override, drv->name);
 
+	if (rpdrv->match)
+		return rpdrv->match(rpdev, rpdrv);
+
 	if (ids)
 		for (i = 0; ids[i].name[0]; i++)
 			if (rpmsg_id_match(rpdev, &ids[i])) {
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
index 032d63573b85..3579b670336a 100644
--- a/include/linux/rpmsg.h
+++ b/include/linux/rpmsg.h
@@ -134,6 +134,7 @@ struct rpmsg_driver {
 	int (*callback)(struct rpmsg_device *, void *, int, void *, u32);
 	int (*signals)(struct rpmsg_device *rpdev,
 		       void *priv, u32 old, u32 new);
+	int (*match)(struct rpmsg_device *dev, struct rpmsg_driver *drv);
 };
 
 static inline u16 rpmsg16_to_cpu(struct rpmsg_device *rpdev, __rpmsg16 val)
-- 
2.34.1

From c340588e1b46c15704eeaea72027b314f5ffb308 Mon Sep 17 00:00:00 2001
From: Bowen Wang <wangbowen6@xiaomi.com>
Date: Mon, 25 Dec 2023 11:26:12 +0800
Subject: [PATCH 3/4] rpmsg: add rpmsg_get_tx/rx_buffer_size_ api

VELAPLATFO-18516

expose the maximum tx and rx packet size to the rpmsg driver

Change-Id: I30ee2d6cccf052cd11aa9af6f18c588bdc1b801f
Signed-off-by: Bowen Wang <wangbowen6@xiaomi.com>
---
 drivers/rpmsg/rpmsg_core.c       | 34 ++++++++++++++++++++++++++++++++
 drivers/rpmsg/rpmsg_internal.h   |  5 +++++
 drivers/rpmsg/virtio_rpmsg_bus.c | 18 +++++++++++++++++
 include/linux/rpmsg.h            | 19 ++++++++++++++++++
 4 files changed, 76 insertions(+)

diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index 65be0d411403..dbf392c38f61 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -133,6 +133,40 @@ void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
 }
 EXPORT_SYMBOL(rpmsg_destroy_ept);
 
+/**
+ * rpmsg_get_tx_buffer_size() - get the endpoint's tx buffer size
+ * @ept:	the rpmsg endpoint
+ *
+ * Returns tx buffer size on success and an appropriate error value on failure.
+ */
+int rpmsg_get_tx_buffer_size(struct rpmsg_endpoint *ept)
+{
+	if (WARN_ON(!ept))
+		return -EINVAL;
+	if (!ept->ops->get_tx_buffer_size)
+		return -ENXIO;
+
+	return ept->ops->get_tx_buffer_size(ept);
+}
+EXPORT_SYMBOL(rpmsg_get_tx_buffer_size);
+
+/**
+ * rpmsg_get_rx_buffer_size() - get the endpoint's rx buffer size
+ * @ept:	the rpmsg endpoint
+ *
+ * Returns rx buffer size on success and an appropriate error value on failure.
+ */
+int rpmsg_get_rx_buffer_size(struct rpmsg_endpoint *ept)
+{
+	if (WARN_ON(!ept))
+		return -EINVAL;
+	if (!ept->ops->get_rx_buffer_size)
+		return -ENXIO;
+
+	return ept->ops->get_rx_buffer_size(ept);
+}
+EXPORT_SYMBOL(rpmsg_get_rx_buffer_size);
+
 /**
  * rpmsg_get_tx_payload_buffer() - get the payload buffer from the pool
  * @ept: the rpmsg endpoint
diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h
index 9caaeb2dcd9a..a103c9a255ac 100644
--- a/drivers/rpmsg/rpmsg_internal.h
+++ b/drivers/rpmsg/rpmsg_internal.h
@@ -49,6 +49,8 @@ struct rpmsg_device_ops {
 /**
  * struct rpmsg_endpoint_ops - indirection table for rpmsg_endpoint operations
  * @destroy_ept:		see @rpmsg_destroy_ept(), required
+ * @get_tx_buffer_size:		see @rpmsg_get_tx_buffer_size(), optional
+ * @get_rx_buffer_size:		see @rpmsg_get_rx_buffer_size(), optional
  * @get_tx_payload_buffer:	see @rpmsg_get_tx_payload_buffer(), optional
  * @send_nocopy:		see @rpmsg_send_nocopy(), optional
  * @sendto_nocopy:		see @rpmsg_sendto_nocopy(), optional
@@ -70,6 +72,9 @@ struct rpmsg_device_ops {
 struct rpmsg_endpoint_ops {
 	void (*destroy_ept)(struct rpmsg_endpoint *ept);
 
+	int (*get_tx_buffer_size)(struct rpmsg_endpoint *ept);
+	int (*get_rx_buffer_size)(struct rpmsg_endpoint *ept);
+
 	void *(*get_tx_payload_buffer)(struct rpmsg_endpoint *ept,
 				       unsigned int *len, bool wait);
 	int (*send_nocopy)(struct rpmsg_endpoint *ept, void *data, int len);
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 783810e670a0..14cd01b3ba6f 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -141,6 +141,8 @@ struct virtio_rpmsg_channel {
 #define RPMSG_RESERVED_ADDRESSES	(1024)
 
 static void virtio_rpmsg_destroy_ept(struct rpmsg_endpoint *ept);
+static int virtio_rpmsg_get_tx_buffer_size(struct rpmsg_endpoint *ept);
+static int virtio_rpmsg_get_rx_buffer_size(struct rpmsg_endpoint *ept);
 static void *virtio_rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
 						unsigned int *len, bool wait);
 static int virtio_rpmsg_send_offchannel_nocopy(struct rpmsg_endpoint *ept, u32 src,
@@ -164,6 +166,8 @@ static struct rpmsg_device *__rpmsg_create_channel(struct virtproc_info *vrp,
 
 static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
 	.destroy_ept = virtio_rpmsg_destroy_ept,
+	.get_tx_buffer_size = virtio_rpmsg_get_tx_buffer_size,
+	.get_rx_buffer_size = virtio_rpmsg_get_rx_buffer_size,
 	.get_tx_payload_buffer = virtio_rpmsg_get_tx_payload_buffer,
 	.send_nocopy = virtio_rpmsg_send_nocopy,
 	.sendto_nocopy = virtio_rpmsg_sendto_nocopy,
@@ -529,6 +533,20 @@ static void rpmsg_downref_sleepers(struct virtproc_info *vrp)
 	mutex_unlock(&vrp->tx_lock);
 }
 
+static int virtio_rpmsg_get_tx_buffer_size(struct rpmsg_endpoint *ept)
+{
+	struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(ept->rpdev);
+
+	return vch->vrp->sbuf_size - sizeof(struct rpmsg_hdr);
+}
+
+static int virtio_rpmsg_get_rx_buffer_size(struct rpmsg_endpoint *ept)
+{
+	struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(ept->rpdev);
+
+	return vch->vrp->rbuf_size - sizeof(struct rpmsg_hdr);
+}
+
 static void *virtio_rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
 						unsigned int *len, bool wait)
 {
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
index 991cb8a25653..bfc9fc64ee29 100644
--- a/include/linux/rpmsg.h
+++ b/include/linux/rpmsg.h
@@ -199,6 +199,9 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *,
 					rpmsg_rx_cb_t cb, void *priv,
 					struct rpmsg_channel_info chinfo);
 
+int rpmsg_get_tx_buffer_size(struct rpmsg_endpoint *ept);
+int rpmsg_get_rx_buffer_size(struct rpmsg_endpoint *ept);
+
 void *rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
 				  unsigned int *len, bool wait);
 
@@ -279,6 +282,22 @@ static inline struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev
 	return NULL;
 }
 
+int rpmsg_get_tx_buffer_size(struct rpmsg_endpoint *ept)
+{
+	/* This shouldn't be possible */
+	WARN_ON(1);
+
+	return -ENXIO;
+}
+
+int rpmsg_get_rx_buffer_size(struct rpmsg_endpoint *ept)
+{
+	/* This shouldn't be possible */
+	WARN_ON(1);
+
+	return -ENXIO;
+}
+
 static inline void *rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
 						unsigned int *len, bool wait)
 {
-- 
2.34.1

Reply via email to