diff options
Diffstat (limited to 'drivers/rpmsg/virtio_rpmsg_bus.c')
| -rw-r--r-- | drivers/rpmsg/virtio_rpmsg_bus.c | 801 |
1 files changed, 374 insertions, 427 deletions
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index b6135d4d54eb..484890b4a6a7 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Virtio-based remote processor messaging bus * @@ -6,33 +7,28 @@ * * Ohad Ben-Cohen <ohad@wizery.com> * Brian Swetland <swetland@google.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #define pr_fmt(fmt) "%s: " fmt, __func__ +#include <linux/dma-mapping.h> +#include <linux/idr.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/virtio.h> -#include <linux/virtio_ids.h> -#include <linux/virtio_config.h> +#include <linux/mutex.h> +#include <linux/rpmsg.h> +#include <linux/rpmsg/byteorder.h> +#include <linux/rpmsg/ns.h> #include <linux/scatterlist.h> -#include <linux/dma-mapping.h> #include <linux/slab.h> -#include <linux/idr.h> -#include <linux/jiffies.h> #include <linux/sched.h> +#include <linux/virtio.h> +#include <linux/virtio_ids.h> +#include <linux/virtio_config.h> #include <linux/wait.h> -#include <linux/rpmsg.h> -#include <linux/mutex.h> + +#include "rpmsg_internal.h" /** * struct virtproc_info - virtual remote processor state @@ -41,6 +37,8 @@ * @svq: tx virtqueue * @rbufs: kernel address of rx buffers * @sbufs: kernel address of tx buffers + * @num_bufs: total number of buffers for rx and tx + * @buf_size: size of one rx or tx buffer * @last_sbuf: index of last tx buffer used * @bufs_dma: dma base addr of the buffers * @tx_lock: protects svq, sbufs and sleepers, to allow concurrent senders. @@ -50,7 +48,6 @@ * @endpoints_lock: lock of the endpoints set * @sendq: wait queue of sending contexts waiting for a tx buffers * @sleepers: number of senders that are waiting for a tx buffer - * @ns_ept: the bus's name service endpoint * * This structure stores the rpmsg state of a given virtio remote processor * device (there might be several virtio proc devices for each physical @@ -60,6 +57,8 @@ struct virtproc_info { struct virtio_device *vdev; struct virtqueue *rvq, *svq; void *rbufs, *sbufs; + unsigned int num_bufs; + unsigned int buf_size; int last_sbuf; dma_addr_t bufs_dma; struct mutex tx_lock; @@ -67,32 +66,58 @@ struct virtproc_info { struct mutex endpoints_lock; wait_queue_head_t sendq; atomic_t sleepers; - struct rpmsg_endpoint *ns_ept; }; +/* The feature bitmap for virtio rpmsg */ +#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ + /** - * struct rpmsg_channel_info - internal channel info representation - * @name: name of service - * @src: local address + * struct rpmsg_hdr - common header for all rpmsg messages + * @src: source address * @dst: destination address + * @reserved: reserved for future use + * @len: length of payload (in bytes) + * @flags: message flags + * @data: @len bytes of message payload data + * + * Every message sent(/received) on the rpmsg bus begins with this header. */ -struct rpmsg_channel_info { - char name[RPMSG_NAME_SIZE]; - u32 src; - u32 dst; +struct rpmsg_hdr { + __rpmsg32 src; + __rpmsg32 dst; + __rpmsg32 reserved; + __rpmsg16 len; + __rpmsg16 flags; + u8 data[]; +} __packed; + + +/** + * struct virtio_rpmsg_channel - rpmsg channel descriptor + * @rpdev: the rpmsg channel device + * @vrp: the virtio remote processor device this channel belongs to + * + * This structure stores the channel that links the rpmsg device to the virtio + * remote processor device. + */ +struct virtio_rpmsg_channel { + struct rpmsg_device rpdev; + + struct virtproc_info *vrp; }; -#define to_rpmsg_channel(d) container_of(d, struct rpmsg_channel, dev) -#define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) +#define to_virtio_rpmsg_channel(_rpdev) \ + container_of(_rpdev, struct virtio_rpmsg_channel, rpdev) /* - * We're allocating 512 buffers of 512 bytes for communications, and then - * using the first 256 buffers for RX, and the last 256 buffers for TX. + * We're allocating buffers of 512 bytes each for communications. The + * number of buffers will be computed from the number of buffers supported + * by the vring, upto a maximum of 512 buffers (256 in each direction). * * Each buffer will have 16 bytes for the msg header and 496 bytes for * the payload. * - * This will require a total space of 256KB for the buffers. + * This will utilize a maximum total space of 256KB for the buffers. * * We might also want to add support for user-provided buffers in time. * This will allow bigger buffer size flexibility, and can also be used @@ -102,9 +127,8 @@ struct rpmsg_channel_info { * can change this without changing anything in the firmware of the remote * processor. */ -#define RPMSG_NUM_BUFS (512) -#define RPMSG_BUF_SIZE (512) -#define RPMSG_TOTAL_BUF_SPACE (RPMSG_NUM_BUFS * RPMSG_BUF_SIZE) +#define MAX_RPMSG_NUM_BUFS (512) +#define MAX_RPMSG_BUF_SIZE (512) /* * Local addresses are dynamically allocated on-demand. @@ -113,79 +137,46 @@ struct rpmsg_channel_info { */ #define RPMSG_RESERVED_ADDRESSES (1024) -/* Address 53 is reserved for advertising remote services */ -#define RPMSG_NS_ADDR (53) - -/* sysfs show configuration fields */ -#define rpmsg_show_attr(field, path, format_string) \ -static ssize_t \ -field##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); \ - \ - return sprintf(buf, format_string, rpdev->path); \ -} - -/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */ -rpmsg_show_attr(name, id.name, "%s\n"); -rpmsg_show_attr(src, src, "0x%x\n"); -rpmsg_show_attr(dst, dst, "0x%x\n"); -rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n"); +static void virtio_rpmsg_destroy_ept(struct rpmsg_endpoint *ept); +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_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 ssize_t virtio_rpmsg_get_mtu(struct rpmsg_endpoint *ept); +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, + .send = virtio_rpmsg_send, + .sendto = virtio_rpmsg_sendto, + .trysend = virtio_rpmsg_trysend, + .trysendto = virtio_rpmsg_trysendto, + .get_mtu = virtio_rpmsg_get_mtu, +}; -/* - * Unique (and free running) index for rpmsg devices. +/** + * rpmsg_sg_init - initialize scatterlist according to cpu address location + * @sg: scatterlist to fill + * @cpu_addr: virtual address of the buffer + * @len: buffer length * - * Yeah, we're not recycling those numbers (yet?). will be easy - * to change if/when we want to. + * An internal function filling scatterlist according to virtual address + * location (in vmalloc or in kernel). */ -static unsigned int rpmsg_dev_index; - -static ssize_t modalias_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); - - return sprintf(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name); -} - -static struct device_attribute rpmsg_dev_attrs[] = { - __ATTR_RO(name), - __ATTR_RO(modalias), - __ATTR_RO(dst), - __ATTR_RO(src), - __ATTR_RO(announce), - __ATTR_NULL -}; - -/* rpmsg devices and drivers are matched using the service name */ -static inline int rpmsg_id_match(const struct rpmsg_channel *rpdev, - const struct rpmsg_device_id *id) -{ - return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0; -} - -/* match rpmsg channel and rpmsg driver */ -static int rpmsg_dev_match(struct device *dev, struct device_driver *drv) -{ - struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); - struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv); - const struct rpmsg_device_id *ids = rpdrv->id_table; - unsigned int i; - - for (i = 0; ids[i].name[0]; i++) - if (rpmsg_id_match(rpdev, &ids[i])) - return 1; - - return 0; -} - -static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env) +static void +rpmsg_sg_init(struct scatterlist *sg, void *cpu_addr, unsigned int len) { - struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); - - return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT, - rpdev->id.name); + if (is_vmalloc_addr(cpu_addr)) { + sg_init_table(sg, 1); + sg_set_page(sg, vmalloc_to_page(cpu_addr), len, + offset_in_page(cpu_addr)); + } else { + WARN_ON(!virt_addr_valid(cpu_addr)); + sg_init_one(sg, cpu_addr, len); + } } /** @@ -210,18 +201,17 @@ static void __ept_release(struct kref *kref) /* for more info, see below documentation of rpmsg_create_ept() */ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, - struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb, - void *priv, u32 addr) + struct rpmsg_device *rpdev, + rpmsg_rx_cb_t cb, + void *priv, u32 addr) { int id_min, id_max, id; struct rpmsg_endpoint *ept; struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev; ept = kzalloc(sizeof(*ept), GFP_KERNEL); - if (!ept) { - dev_err(dev, "failed to kzalloc a new ept\n"); + if (!ept) return NULL; - } kref_init(&ept->refcount); mutex_init(&ept->cb_lock); @@ -229,6 +219,7 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, ept->rpdev = rpdev; ept->cb = cb; ept->priv = priv; + ept->ops = &virtio_endpoint_ops; /* do we need to allocate a local address ? */ if (addr == RPMSG_ADDR_ANY) { @@ -259,52 +250,33 @@ free_ept: return NULL; } -/** - * rpmsg_create_ept() - create a new rpmsg_endpoint - * @rpdev: rpmsg channel device - * @cb: rx callback handler - * @priv: private data for the driver's use - * @addr: local rpmsg address to bind with @cb - * - * Every rpmsg address in the system is bound to an rx callback (so when - * inbound messages arrive, they are dispatched by the rpmsg bus using the - * appropriate callback handler) by means of an rpmsg_endpoint struct. - * - * This function allows drivers to create such an endpoint, and by that, - * bind a callback, and possibly some private data too, to an rpmsg address - * (either one that is known in advance, or one that will be dynamically - * assigned for them). - * - * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint - * is already created for them when they are probed by the rpmsg bus - * (using the rx callback provided when they registered to the rpmsg bus). - * - * So things should just work for simple drivers: they already have an - * endpoint, their rx callback is bound to their rpmsg address, and when - * relevant inbound messages arrive (i.e. messages which their dst address - * equals to the src address of their rpmsg channel), the driver's handler - * is invoked to process it. - * - * That said, more complicated drivers might do need to allocate - * additional rpmsg addresses, and bind them to different rx callbacks. - * To accomplish that, those drivers need to call this function. - * - * Drivers should provide their @rpdev channel (so the new endpoint would belong - * to the same remote processor their channel belongs to), an rx callback - * function, an optional private data (which is provided back when the - * rx callback is invoked), and an address they want to bind with the - * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will - * dynamically assign them an available rpmsg address (drivers should have - * a very good reason why not to always use RPMSG_ADDR_ANY here). - * - * Returns a pointer to the endpoint on success, or NULL on error. - */ -struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev, - rpmsg_rx_cb_t cb, void *priv, u32 addr) +static struct rpmsg_device *virtio_rpmsg_create_channel(struct rpmsg_device *rpdev, + struct rpmsg_channel_info *chinfo) { - return __rpmsg_create_ept(rpdev->vrp, rpdev, cb, priv, addr); + struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); + struct virtproc_info *vrp = vch->vrp; + + return __rpmsg_create_channel(vrp, chinfo); +} + +static int virtio_rpmsg_release_channel(struct rpmsg_device *rpdev, + struct rpmsg_channel_info *chinfo) +{ + struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); + struct virtproc_info *vrp = vch->vrp; + + return rpmsg_unregister_device(&vrp->vdev->dev, chinfo); +} + +static struct rpmsg_endpoint *virtio_rpmsg_create_ept(struct rpmsg_device *rpdev, + rpmsg_rx_cb_t cb, + void *priv, + struct rpmsg_channel_info chinfo) +{ + struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); + + return __rpmsg_create_ept(vch->vrp, rpdev, cb, priv, chinfo.src); } -EXPORT_SYMBOL(rpmsg_create_ept); /** * __rpmsg_destroy_ept() - destroy an existing rpmsg endpoint @@ -332,160 +304,76 @@ __rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept) kref_put(&ept->refcount, __ept_release); } -/** - * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint - * @ept: endpoing to destroy - * - * Should be used by drivers to destroy an rpmsg endpoint previously - * created with rpmsg_create_ept(). - */ -void rpmsg_destroy_ept(struct rpmsg_endpoint *ept) +static void virtio_rpmsg_destroy_ept(struct rpmsg_endpoint *ept) { - __rpmsg_destroy_ept(ept->rpdev->vrp, ept); + struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(ept->rpdev); + + __rpmsg_destroy_ept(vch->vrp, ept); } -EXPORT_SYMBOL(rpmsg_destroy_ept); -/* - * when an rpmsg driver is probed with a channel, we seamlessly create - * it an endpoint, binding its rx callback to a unique local rpmsg - * address. - * - * if we need to, we also announce about this channel to the remote - * processor (needed in case the driver is exposing an rpmsg service). - */ -static int rpmsg_dev_probe(struct device *dev) +static int virtio_rpmsg_announce_create(struct rpmsg_device *rpdev) { - struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); - struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); - struct virtproc_info *vrp = rpdev->vrp; - struct rpmsg_endpoint *ept; - int err; - - ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, rpdev->src); - if (!ept) { - dev_err(dev, "failed to create endpoint\n"); - err = -ENOMEM; - goto out; - } - - rpdev->ept = ept; - rpdev->src = ept->addr; - - err = rpdrv->probe(rpdev); - if (err) { - dev_err(dev, "%s: failed: %d\n", __func__, err); - rpmsg_destroy_ept(ept); - goto out; - } + struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); + struct virtproc_info *vrp = vch->vrp; + struct device *dev = &rpdev->dev; + int err = 0; /* need to tell remote processor's name service about this channel ? */ - if (rpdev->announce && - virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { + if (rpdev->announce && rpdev->ept && + virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { struct rpmsg_ns_msg nsm; - strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE); - nsm.addr = rpdev->src; - nsm.flags = RPMSG_NS_CREATE; + strscpy_pad(nsm.name, rpdev->id.name, sizeof(nsm.name)); + nsm.addr = cpu_to_rpmsg32(rpdev, rpdev->ept->addr); + nsm.flags = cpu_to_rpmsg32(rpdev, RPMSG_NS_CREATE); - err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR); + err = rpmsg_sendto(rpdev->ept, &nsm, sizeof(nsm), RPMSG_NS_ADDR); if (err) dev_err(dev, "failed to announce service %d\n", err); } -out: return err; } -static int rpmsg_dev_remove(struct device *dev) +static int virtio_rpmsg_announce_destroy(struct rpmsg_device *rpdev) { - struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); - struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); - struct virtproc_info *vrp = rpdev->vrp; + struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); + struct virtproc_info *vrp = vch->vrp; + struct device *dev = &rpdev->dev; int err = 0; /* tell remote processor's name service we're removing this channel */ - if (rpdev->announce && - virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { + if (rpdev->announce && rpdev->ept && + virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { struct rpmsg_ns_msg nsm; - strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE); - nsm.addr = rpdev->src; - nsm.flags = RPMSG_NS_DESTROY; + strscpy_pad(nsm.name, rpdev->id.name, sizeof(nsm.name)); + nsm.addr = cpu_to_rpmsg32(rpdev, rpdev->ept->addr); + nsm.flags = cpu_to_rpmsg32(rpdev, RPMSG_NS_DESTROY); - err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR); + err = rpmsg_sendto(rpdev->ept, &nsm, sizeof(nsm), RPMSG_NS_ADDR); if (err) dev_err(dev, "failed to announce service %d\n", err); } - rpdrv->remove(rpdev); - - rpmsg_destroy_ept(rpdev->ept); - return err; } -static struct bus_type rpmsg_bus = { - .name = "rpmsg", - .match = rpmsg_dev_match, - .dev_attrs = rpmsg_dev_attrs, - .uevent = rpmsg_uevent, - .probe = rpmsg_dev_probe, - .remove = rpmsg_dev_remove, +static const struct rpmsg_device_ops virtio_rpmsg_ops = { + .create_channel = virtio_rpmsg_create_channel, + .release_channel = virtio_rpmsg_release_channel, + .create_ept = virtio_rpmsg_create_ept, + .announce_create = virtio_rpmsg_announce_create, + .announce_destroy = virtio_rpmsg_announce_destroy, }; -/** - * register_rpmsg_driver() - register an rpmsg driver with the rpmsg bus - * @rpdrv: pointer to a struct rpmsg_driver - * - * Returns 0 on success, and an appropriate error value on failure. - */ -int register_rpmsg_driver(struct rpmsg_driver *rpdrv) +static void virtio_rpmsg_release_device(struct device *dev) { - rpdrv->drv.bus = &rpmsg_bus; - return driver_register(&rpdrv->drv); -} -EXPORT_SYMBOL(register_rpmsg_driver); - -/** - * unregister_rpmsg_driver() - unregister an rpmsg driver from the rpmsg bus - * @rpdrv: pointer to a struct rpmsg_driver - * - * Returns 0 on success, and an appropriate error value on failure. - */ -void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv) -{ - driver_unregister(&rpdrv->drv); -} -EXPORT_SYMBOL(unregister_rpmsg_driver); - -static void rpmsg_release_device(struct device *dev) -{ - struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); - - kfree(rpdev); -} - -/* - * match an rpmsg channel with a channel info struct. - * this is used to make sure we're not creating rpmsg devices for channels - * that already exist. - */ -static int rpmsg_channel_match(struct device *dev, void *data) -{ - struct rpmsg_channel_info *chinfo = data; - struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); - - if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src) - return 0; - - if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst) - return 0; + struct rpmsg_device *rpdev = to_rpmsg_device(dev); + struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); - if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE)) - return 0; - - /* found a match ! */ - return 1; + kfree(rpdev->driver_override); + kfree(vch); } /* @@ -493,15 +381,16 @@ static int rpmsg_channel_match(struct device *dev, void *data) * this function will be used to create both static and dynamic * channels. */ -static struct rpmsg_channel *rpmsg_create_channel(struct virtproc_info *vrp, - struct rpmsg_channel_info *chinfo) +static struct rpmsg_device *__rpmsg_create_channel(struct virtproc_info *vrp, + struct rpmsg_channel_info *chinfo) { - struct rpmsg_channel *rpdev; + struct virtio_rpmsg_channel *vch; + struct rpmsg_device *rpdev; struct device *tmp, *dev = &vrp->vdev->dev; int ret; /* make sure a similar channel doesn't already exist */ - tmp = device_find_child(dev, chinfo, rpmsg_channel_match); + tmp = rpmsg_find_device(dev, chinfo); if (tmp) { /* decrement the matched device's refcount back */ put_device(tmp); @@ -510,62 +399,37 @@ static struct rpmsg_channel *rpmsg_create_channel(struct virtproc_info *vrp, return NULL; } - rpdev = kzalloc(sizeof(struct rpmsg_channel), GFP_KERNEL); - if (!rpdev) { - pr_err("kzalloc failed\n"); + vch = kzalloc(sizeof(*vch), GFP_KERNEL); + if (!vch) return NULL; - } - rpdev->vrp = vrp; + /* Link the channel to our vrp */ + vch->vrp = vrp; + + /* Assign public information to the rpmsg_device */ + rpdev = &vch->rpdev; rpdev->src = chinfo->src; rpdev->dst = chinfo->dst; + rpdev->ops = &virtio_rpmsg_ops; + rpdev->little_endian = virtio_is_little_endian(vrp->vdev); /* * rpmsg server channels has predefined local address (for now), * and their existence needs to be announced remotely */ - rpdev->announce = rpdev->src != RPMSG_ADDR_ANY ? true : false; + rpdev->announce = rpdev->src != RPMSG_ADDR_ANY; - strncpy(rpdev->id.name, chinfo->name, RPMSG_NAME_SIZE); - - /* very simple device indexing plumbing which is enough for now */ - dev_set_name(&rpdev->dev, "rpmsg%d", rpmsg_dev_index++); + strscpy(rpdev->id.name, chinfo->name, sizeof(rpdev->id.name)); rpdev->dev.parent = &vrp->vdev->dev; - rpdev->dev.bus = &rpmsg_bus; - rpdev->dev.release = rpmsg_release_device; - - ret = device_register(&rpdev->dev); - if (ret) { - dev_err(dev, "device_register failed: %d\n", ret); - put_device(&rpdev->dev); + rpdev->dev.release = virtio_rpmsg_release_device; + ret = rpmsg_register_device(rpdev); + if (ret) return NULL; - } return rpdev; } -/* - * find an existing channel using its name + address properties, - * and destroy it - */ -static int rpmsg_destroy_channel(struct virtproc_info *vrp, - struct rpmsg_channel_info *chinfo) -{ - struct virtio_device *vdev = vrp->vdev; - struct device *dev; - - dev = device_find_child(&vdev->dev, chinfo, rpmsg_channel_match); - if (!dev) - return -EINVAL; - - device_unregister(dev); - - put_device(dev); - - return 0; -} - /* super simple buffer "allocator" that is just enough for now */ static void *get_a_tx_buf(struct virtproc_info *vrp) { @@ -579,8 +443,8 @@ static void *get_a_tx_buf(struct virtproc_info *vrp) * either pick the next unused tx buffer * (half of our buffers are used for sending messages) */ - if (vrp->last_sbuf < RPMSG_NUM_BUFS / 2) - ret = vrp->sbufs + RPMSG_BUF_SIZE * vrp->last_sbuf++; + if (vrp->last_sbuf < vrp->num_bufs / 2) + ret = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++; /* or recycle a used one */ else ret = virtqueue_get_buf(vrp->svq, &len); @@ -675,15 +539,17 @@ static void rpmsg_downref_sleepers(struct virtproc_info *vrp) * 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 + * should use the appropriate rpmsg_{try}send{to} API * (see include/linux/rpmsg.h). * - * Returns 0 on success and an appropriate error value on failure. + * Return: 0 on success and an appropriate error value on failure. */ -int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst, - void *data, int len, bool wait) +static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev, + u32 src, u32 dst, + void *data, int len, bool wait) { - struct virtproc_info *vrp = rpdev->vrp; + 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; @@ -704,7 +570,7 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst, * messaging), or to improve the buffer allocator, to support * variable-length buffer sizes. */ - if (len > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) { + if (len > vrp->buf_size - sizeof(struct rpmsg_hdr)) { dev_err(dev, "message is too big (%d)\n", len); return -EMSGSIZE; } @@ -739,20 +605,21 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst, } } - msg->len = len; + msg->len = cpu_to_rpmsg16(rpdev, len); msg->flags = 0; - msg->src = src; - msg->dst = dst; + 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", - msg->src, msg->dst, msg->len, - msg->flags, msg->reserved); - print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1, - msg, sizeof(*msg) + msg->len, true); + src, dst, len, msg->flags, msg->reserved); +#if defined(CONFIG_DYNAMIC_DEBUG) + dynamic_hex_dump("rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1, + msg, sizeof(*msg) + len, true); +#endif - sg_init_one(&sg, msg, sizeof(*msg) + len); + rpmsg_sg_init(&sg, msg, sizeof(*msg) + len); mutex_lock(&vrp->tx_lock); @@ -774,35 +641,82 @@ out: mutex_unlock(&vrp->tx_lock); return err; } -EXPORT_SYMBOL(rpmsg_send_offchannel_raw); + +static int virtio_rpmsg_send(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); +} + +static int virtio_rpmsg_sendto(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); +} + +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; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); +} + +static int virtio_rpmsg_trysendto(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, false); +} + +static ssize_t virtio_rpmsg_get_mtu(struct rpmsg_endpoint *ept) +{ + struct rpmsg_device *rpdev = ept->rpdev; + struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); + + return vch->vrp->buf_size - sizeof(struct rpmsg_hdr); +} static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, struct rpmsg_hdr *msg, unsigned int len) { struct rpmsg_endpoint *ept; struct scatterlist sg; + bool little_endian = virtio_is_little_endian(vrp->vdev); + unsigned int msg_len = __rpmsg16_to_cpu(little_endian, msg->len); int err; dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n", - msg->src, msg->dst, msg->len, - msg->flags, msg->reserved); - print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1, - msg, sizeof(*msg) + msg->len, true); + __rpmsg32_to_cpu(little_endian, msg->src), + __rpmsg32_to_cpu(little_endian, msg->dst), msg_len, + __rpmsg16_to_cpu(little_endian, msg->flags), + __rpmsg32_to_cpu(little_endian, msg->reserved)); +#if defined(CONFIG_DYNAMIC_DEBUG) + dynamic_hex_dump("rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1, + msg, sizeof(*msg) + msg_len, true); +#endif /* * We currently use fixed-sized buffers, so trivially sanitize * the reported payload length. */ - if (len > RPMSG_BUF_SIZE || - msg->len > (len - sizeof(struct rpmsg_hdr))) { - dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len); + if (len > vrp->buf_size || + msg_len > (len - sizeof(struct rpmsg_hdr))) { + dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg_len); return -EINVAL; } /* use the dst addr to fetch the callback of the appropriate user */ mutex_lock(&vrp->endpoints_lock); - ept = idr_find(&vrp->endpoints, msg->dst); + ept = idr_find(&vrp->endpoints, __rpmsg32_to_cpu(little_endian, msg->dst)); /* let's make sure no one deallocates ept while we use it */ if (ept) @@ -815,18 +729,18 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, mutex_lock(&ept->cb_lock); if (ept->cb) - ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, - msg->src); + ept->cb(ept->rpdev, msg->data, msg_len, ept->priv, + __rpmsg32_to_cpu(little_endian, msg->src)); mutex_unlock(&ept->cb_lock); /* farewell, ept, we don't need you anymore */ kref_put(&ept->refcount, __ept_release); } else - dev_warn(dev, "msg received with no recipient\n"); + dev_warn_ratelimited(dev, "msg received with no recipient\n"); /* publish the real size of the buffer */ - sg_init_one(&sg, msg, RPMSG_BUF_SIZE); + rpmsg_sg_init(&sg, msg, vrp->buf_size); /* add the buffer back to the remote processor's virtqueue */ err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL); @@ -861,7 +775,7 @@ static void rpmsg_recv_done(struct virtqueue *rvq) msgs_received++; msg = virtqueue_get_buf(rvq, &len); - }; + } dev_dbg(dev, "Received %u messages\n", msgs_received); @@ -887,67 +801,63 @@ static void rpmsg_xmit_done(struct virtqueue *svq) wake_up_interruptible(&vrp->sendq); } -/* invoked when a name service announcement arrives */ -static void rpmsg_ns_cb(struct rpmsg_channel *rpdev, void *data, int len, - void *priv, u32 src) +/* + * Called to expose to user a /dev/rpmsg_ctrlX interface allowing to + * create endpoint-to-endpoint communication without associated RPMsg channel. + * The endpoints are rattached to the ctrldev RPMsg device. + */ +static struct rpmsg_device *rpmsg_virtio_add_ctrl_dev(struct virtio_device *vdev) { - struct rpmsg_ns_msg *msg = data; - struct rpmsg_channel *newch; - struct rpmsg_channel_info chinfo; - struct virtproc_info *vrp = priv; - struct device *dev = &vrp->vdev->dev; - int ret; + struct virtproc_info *vrp = vdev->priv; + struct virtio_rpmsg_channel *vch; + struct rpmsg_device *rpdev_ctrl; + int err = 0; - print_hex_dump(KERN_DEBUG, "NS announcement: ", - DUMP_PREFIX_NONE, 16, 1, - data, len, true); + vch = kzalloc(sizeof(*vch), GFP_KERNEL); + if (!vch) + return ERR_PTR(-ENOMEM); - if (len != sizeof(*msg)) { - dev_err(dev, "malformed ns msg (%d)\n", len); - return; - } + /* Link the channel to the vrp */ + vch->vrp = vrp; - /* - * the name service ept does _not_ belong to a real rpmsg channel, - * and is handled by the rpmsg bus itself. - * for sanity reasons, make sure a valid rpdev has _not_ sneaked - * in somehow. - */ - if (rpdev) { - dev_err(dev, "anomaly: ns ept has an rpdev handle\n"); - return; - } + /* Assign public information to the rpmsg_device */ + rpdev_ctrl = &vch->rpdev; + rpdev_ctrl->ops = &virtio_rpmsg_ops; - /* don't trust the remote processor for null terminating the name */ - msg->name[RPMSG_NAME_SIZE - 1] = '\0'; + rpdev_ctrl->dev.parent = &vrp->vdev->dev; + rpdev_ctrl->dev.release = virtio_rpmsg_release_device; + rpdev_ctrl->little_endian = virtio_is_little_endian(vrp->vdev); - dev_info(dev, "%sing channel %s addr 0x%x\n", - msg->flags & RPMSG_NS_DESTROY ? "destroy" : "creat", - msg->name, msg->addr); + err = rpmsg_ctrldev_register_device(rpdev_ctrl); + if (err) { + /* vch will be free in virtio_rpmsg_release_device() */ + return ERR_PTR(err); + } - strncpy(chinfo.name, msg->name, sizeof(chinfo.name)); - chinfo.src = RPMSG_ADDR_ANY; - chinfo.dst = msg->addr; + return rpdev_ctrl; +} - if (msg->flags & RPMSG_NS_DESTROY) { - ret = rpmsg_destroy_channel(vrp, &chinfo); - if (ret) - dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret); - } else { - newch = rpmsg_create_channel(vrp, &chinfo); - if (!newch) - dev_err(dev, "rpmsg_create_channel failed\n"); - } +static void rpmsg_virtio_del_ctrl_dev(struct rpmsg_device *rpdev_ctrl) +{ + if (!rpdev_ctrl) + return; + device_unregister(&rpdev_ctrl->dev); } static int rpmsg_probe(struct virtio_device *vdev) { - vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done }; - const char *names[] = { "input", "output" }; + struct virtqueue_info vqs_info[] = { + { "input", rpmsg_recv_done }, + { "output", rpmsg_xmit_done }, + }; struct virtqueue *vqs[2]; struct virtproc_info *vrp; + struct virtio_rpmsg_channel *vch = NULL; + struct rpmsg_device *rpdev_ns, *rpdev_ctrl; void *bufs_va; int err = 0, i; + size_t total_buf_space; + bool notify; vrp = kzalloc(sizeof(*vrp), GFP_KERNEL); if (!vrp) @@ -961,40 +871,54 @@ static int rpmsg_probe(struct virtio_device *vdev) init_waitqueue_head(&vrp->sendq); /* We expect two virtqueues, rx and tx (and in this order) */ - err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, names); + err = virtio_find_vqs(vdev, 2, vqs, vqs_info, NULL); if (err) goto free_vrp; vrp->rvq = vqs[0]; vrp->svq = vqs[1]; + /* we expect symmetric tx/rx vrings */ + WARN_ON(virtqueue_get_vring_size(vrp->rvq) != + virtqueue_get_vring_size(vrp->svq)); + + /* we need less buffers if vrings are small */ + if (virtqueue_get_vring_size(vrp->rvq) < MAX_RPMSG_NUM_BUFS / 2) + vrp->num_bufs = virtqueue_get_vring_size(vrp->rvq) * 2; + else + vrp->num_bufs = MAX_RPMSG_NUM_BUFS; + + vrp->buf_size = MAX_RPMSG_BUF_SIZE; + + total_buf_space = vrp->num_bufs * vrp->buf_size; + /* allocate coherent memory for the buffers */ - bufs_va = dma_alloc_coherent(vdev->dev.parent->parent, - RPMSG_TOTAL_BUF_SPACE, - &vrp->bufs_dma, GFP_KERNEL); + bufs_va = dma_alloc_coherent(vdev->dev.parent, + total_buf_space, &vrp->bufs_dma, + GFP_KERNEL); if (!bufs_va) { err = -ENOMEM; goto vqs_del; } - dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%llx\n", bufs_va, - (unsigned long long)vrp->bufs_dma); + dev_dbg(&vdev->dev, "buffers: va %p, dma %pad\n", + bufs_va, &vrp->bufs_dma); /* half of the buffers is dedicated for RX */ vrp->rbufs = bufs_va; /* and half is dedicated for TX */ - vrp->sbufs = bufs_va + RPMSG_TOTAL_BUF_SPACE / 2; + vrp->sbufs = bufs_va + total_buf_space / 2; /* set up the receive buffers */ - for (i = 0; i < RPMSG_NUM_BUFS / 2; i++) { + for (i = 0; i < vrp->num_bufs / 2; i++) { struct scatterlist sg; - void *cpu_addr = vrp->rbufs + i * RPMSG_BUF_SIZE; + void *cpu_addr = vrp->rbufs + i * vrp->buf_size; - sg_init_one(&sg, cpu_addr, RPMSG_BUF_SIZE); + rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size); err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr, - GFP_KERNEL); + GFP_KERNEL); WARN_ON(err); /* sanity check; this can't really happen */ } @@ -1003,28 +927,63 @@ static int rpmsg_probe(struct virtio_device *vdev) vdev->priv = vrp; + rpdev_ctrl = rpmsg_virtio_add_ctrl_dev(vdev); + if (IS_ERR(rpdev_ctrl)) { + err = PTR_ERR(rpdev_ctrl); + goto free_coherent; + } + /* if supported by the remote processor, enable the name service */ if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) { - /* a dedicated endpoint handles the name service msgs */ - vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, rpmsg_ns_cb, - vrp, RPMSG_NS_ADDR); - if (!vrp->ns_ept) { - dev_err(&vdev->dev, "failed to create the ns ept\n"); + vch = kzalloc(sizeof(*vch), GFP_KERNEL); + if (!vch) { err = -ENOMEM; - goto free_coherent; + goto free_ctrldev; } + + /* Link the channel to our vrp */ + vch->vrp = vrp; + + /* Assign public information to the rpmsg_device */ + rpdev_ns = &vch->rpdev; + rpdev_ns->ops = &virtio_rpmsg_ops; + rpdev_ns->little_endian = virtio_is_little_endian(vrp->vdev); + + rpdev_ns->dev.parent = &vrp->vdev->dev; + rpdev_ns->dev.release = virtio_rpmsg_release_device; + + err = rpmsg_ns_register_device(rpdev_ns); + if (err) + /* vch will be free in virtio_rpmsg_release_device() */ + goto free_ctrldev; } + /* + * Prepare to kick but don't notify yet - we can't do this before + * device is ready. + */ + notify = virtqueue_kick_prepare(vrp->rvq); + + /* From this point on, we can notify and get callbacks. */ + virtio_device_ready(vdev); + /* tell the remote processor it can start sending messages */ - virtqueue_kick(vrp->rvq); + /* + * this might be concurrent with callbacks, but we are only + * doing notify, not a full kick here, so that's ok. + */ + if (notify) + virtqueue_notify(vrp->rvq); dev_info(&vdev->dev, "rpmsg host is online\n"); return 0; +free_ctrldev: + rpmsg_virtio_del_ctrl_dev(rpdev_ctrl); free_coherent: - dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE, - bufs_va, vrp->bufs_dma); + dma_free_coherent(vdev->dev.parent, total_buf_space, + bufs_va, vrp->bufs_dma); vqs_del: vdev->config->del_vqs(vrp->vdev); free_vrp: @@ -1042,23 +1001,21 @@ static int rpmsg_remove_device(struct device *dev, void *data) static void rpmsg_remove(struct virtio_device *vdev) { struct virtproc_info *vrp = vdev->priv; + size_t total_buf_space = vrp->num_bufs * vrp->buf_size; int ret; - vdev->config->reset(vdev); + virtio_reset_device(vdev); ret = device_for_each_child(&vdev->dev, NULL, rpmsg_remove_device); if (ret) dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret); - if (vrp->ns_ept) - __rpmsg_destroy_ept(vrp, vrp->ns_ept); - idr_destroy(&vrp->endpoints); vdev->config->del_vqs(vrp->vdev); - dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE, - vrp->rbufs, vrp->bufs_dma); + dma_free_coherent(vdev->dev.parent, total_buf_space, + vrp->rbufs, vrp->bufs_dma); kfree(vrp); } @@ -1076,7 +1033,6 @@ static struct virtio_driver virtio_ipc_driver = { .feature_table = features, .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, .id_table = id_table, .probe = rpmsg_probe, .remove = rpmsg_remove, @@ -1086,17 +1042,9 @@ static int __init rpmsg_init(void) { int ret; - ret = bus_register(&rpmsg_bus); - if (ret) { - pr_err("failed to register rpmsg bus: %d\n", ret); - return ret; - } - ret = register_virtio_driver(&virtio_ipc_driver); - if (ret) { + if (ret) pr_err("failed to register virtio driver: %d\n", ret); - bus_unregister(&rpmsg_bus); - } return ret; } @@ -1105,7 +1053,6 @@ subsys_initcall(rpmsg_init); static void __exit rpmsg_fini(void) { unregister_virtio_driver(&virtio_ipc_driver); - bus_unregister(&rpmsg_bus); } module_exit(rpmsg_fini); |
