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