diff options
Diffstat (limited to 'drivers/rpmsg/virtio_rpmsg_bus.c')
| -rw-r--r-- | drivers/rpmsg/virtio_rpmsg_bus.c | 302 |
1 files changed, 150 insertions, 152 deletions
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 664f957012cd..484890b4a6a7 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -11,21 +11,22 @@ #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 <linux/of_device.h> #include "rpmsg_internal.h" @@ -47,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 @@ -66,7 +66,6 @@ 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 */ @@ -84,45 +83,22 @@ struct virtproc_info { * Every message sent(/received) on the rpmsg bus begins with this header. */ struct rpmsg_hdr { - u32 src; - u32 dst; - u32 reserved; - u16 len; - u16 flags; - u8 data[0]; + __rpmsg32 src; + __rpmsg32 dst; + __rpmsg32 reserved; + __rpmsg16 len; + __rpmsg16 flags; + u8 data[]; } __packed; -/** - * struct rpmsg_ns_msg - dynamic name service announcement message - * @name: name of remote service that is published - * @addr: address of remote service that is published - * @flags: indicates whether service is created or destroyed - * - * This message is sent across to publish a new service, or announce - * about its removal. When we receive these messages, an appropriate - * rpmsg channel (i.e device) is created/destroyed. In turn, the ->probe() - * or ->remove() handler of the appropriate rpmsg driver will be invoked - * (if/as-soon-as one is registered). - */ -struct rpmsg_ns_msg { - char name[RPMSG_NAME_SIZE]; - u32 addr; - u32 flags; -} __packed; /** - * enum rpmsg_ns_flags - dynamic name service announcement flags + * struct virtio_rpmsg_channel - rpmsg channel descriptor + * @rpdev: the rpmsg channel device + * @vrp: the virtio remote processor device this channel belongs to * - * @RPMSG_NS_CREATE: a new remote service was just created - * @RPMSG_NS_DESTROY: a known remote service was just destroyed - */ -enum rpmsg_ns_flags { - RPMSG_NS_CREATE = 0, - RPMSG_NS_DESTROY = 1, -}; - -/** - * @vrp: the remote processor 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; @@ -161,29 +137,24 @@ struct virtio_rpmsg_channel { */ #define RPMSG_RESERVED_ADDRESSES (1024) -/* Address 53 is reserved for advertising remote services */ -#define RPMSG_NS_ADDR (53) - 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_send_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 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, - .send_offchannel = virtio_rpmsg_send_offchannel, .trysend = virtio_rpmsg_trysend, .trysendto = virtio_rpmsg_trysendto, - .trysend_offchannel = virtio_rpmsg_trysend_offchannel, + .get_mtu = virtio_rpmsg_get_mtu, }; /** @@ -279,6 +250,24 @@ free_ept: return NULL; } +static struct rpmsg_device *virtio_rpmsg_create_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_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, @@ -334,9 +323,9 @@ static int virtio_rpmsg_announce_create(struct rpmsg_device *rpdev) 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->ept->addr; - 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->ept, &nsm, sizeof(nsm), RPMSG_NS_ADDR); if (err) @@ -358,9 +347,9 @@ static int virtio_rpmsg_announce_destroy(struct rpmsg_device *rpdev) 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->ept->addr; - 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->ept, &nsm, sizeof(nsm), RPMSG_NS_ADDR); if (err) @@ -371,6 +360,8 @@ static int virtio_rpmsg_announce_destroy(struct rpmsg_device *rpdev) } 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, @@ -381,6 +372,7 @@ static void virtio_rpmsg_release_device(struct device *dev) struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); + kfree(rpdev->driver_override); kfree(vch); } @@ -389,8 +381,8 @@ static void virtio_rpmsg_release_device(struct device *dev) * this function will be used to create both static and dynamic * channels. */ -static struct rpmsg_device *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 virtio_rpmsg_channel *vch; struct rpmsg_device *rpdev; @@ -419,6 +411,7 @@ static struct rpmsg_device *rpmsg_create_channel(struct virtproc_info *vrp, 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), @@ -426,7 +419,7 @@ static struct rpmsg_device *rpmsg_create_channel(struct virtproc_info *vrp, */ rpdev->announce = rpdev->src != RPMSG_ADDR_ANY; - strncpy(rpdev->id.name, chinfo->name, RPMSG_NAME_SIZE); + strscpy(rpdev->id.name, chinfo->name, sizeof(rpdev->id.name)); rpdev->dev.parent = &vrp->vdev->dev; rpdev->dev.release = virtio_rpmsg_release_device; @@ -546,10 +539,10 @@ 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. */ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev, u32 src, u32 dst, @@ -612,18 +605,18 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev, } } - 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); + 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) + msg->len, true); + msg, sizeof(*msg) + len, true); #endif rpmsg_sg_init(&sg, msg, sizeof(*msg) + len); @@ -666,14 +659,6 @@ static int virtio_rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true); } -static int virtio_rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src, - u32 dst, void *data, int len) -{ - struct rpmsg_device *rpdev = ept->rpdev; - - 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; @@ -691,12 +676,12 @@ static int virtio_rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); } -static int virtio_rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, - u32 dst, void *data, int len) +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 rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); + return vch->vrp->buf_size - sizeof(struct rpmsg_hdr); } static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, @@ -704,13 +689,18 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, { 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); + __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); + msg, sizeof(*msg) + msg_len, true); #endif /* @@ -718,15 +708,15 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, * the reported payload length. */ 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); + 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) @@ -739,15 +729,15 @@ 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 */ rpmsg_sg_init(&sg, msg, vrp->buf_size); @@ -811,68 +801,59 @@ static void rpmsg_xmit_done(struct virtqueue *svq) wake_up_interruptible(&vrp->sendq); } -/* invoked when a name service announcement arrives */ -static int rpmsg_ns_cb(struct rpmsg_device *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_device *newch; - struct rpmsg_channel_info chinfo; - struct virtproc_info *vrp = priv; - struct device *dev = &vrp->vdev->dev; - int ret; - -#if defined(CONFIG_DYNAMIC_DEBUG) - dynamic_hex_dump("NS announcement: ", DUMP_PREFIX_NONE, 16, 1, - data, len, true); -#endif - - if (len != sizeof(*msg)) { - dev_err(dev, "malformed ns msg (%d)\n", len); - return -EINVAL; - } + struct virtproc_info *vrp = vdev->priv; + struct virtio_rpmsg_channel *vch; + struct rpmsg_device *rpdev_ctrl; + int err = 0; - /* - * 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 -EINVAL; - } + vch = kzalloc(sizeof(*vch), GFP_KERNEL); + if (!vch) + return ERR_PTR(-ENOMEM); - /* don't trust the remote processor for null terminating the name */ - msg->name[RPMSG_NAME_SIZE - 1] = '\0'; + /* Link the channel to the vrp */ + vch->vrp = vrp; - dev_info(dev, "%sing channel %s addr 0x%x\n", - msg->flags & RPMSG_NS_DESTROY ? "destroy" : "creat", - msg->name, msg->addr); + /* Assign public information to the rpmsg_device */ + rpdev_ctrl = &vch->rpdev; + rpdev_ctrl->ops = &virtio_rpmsg_ops; - strncpy(chinfo.name, msg->name, sizeof(chinfo.name)); - chinfo.src = RPMSG_ADDR_ANY; - chinfo.dst = msg->addr; + 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); - if (msg->flags & RPMSG_NS_DESTROY) { - ret = rpmsg_unregister_device(&vrp->vdev->dev, &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"); + err = rpmsg_ctrldev_register_device(rpdev_ctrl); + if (err) { + /* vch will be free in virtio_rpmsg_release_device() */ + return ERR_PTR(err); } - return 0; + return rpdev_ctrl; +} + +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 }; - static const char * const 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; @@ -890,7 +871,7 @@ 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 = virtio_find_vqs(vdev, 2, vqs, vq_cbs, names, NULL); + err = virtio_find_vqs(vdev, 2, vqs, vqs_info, NULL); if (err) goto free_vrp; @@ -912,7 +893,7 @@ static int rpmsg_probe(struct virtio_device *vdev) total_buf_space = vrp->num_bufs * vrp->buf_size; /* allocate coherent memory for the buffers */ - bufs_va = dma_alloc_coherent(vdev->dev.parent->parent, + bufs_va = dma_alloc_coherent(vdev->dev.parent, total_buf_space, &vrp->bufs_dma, GFP_KERNEL); if (!bufs_va) { @@ -946,16 +927,35 @@ 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; } /* @@ -979,8 +979,10 @@ static int rpmsg_probe(struct virtio_device *vdev) return 0; +free_ctrldev: + rpmsg_virtio_del_ctrl_dev(rpdev_ctrl); free_coherent: - dma_free_coherent(vdev->dev.parent->parent, total_buf_space, + dma_free_coherent(vdev->dev.parent, total_buf_space, bufs_va, vrp->bufs_dma); vqs_del: vdev->config->del_vqs(vrp->vdev); @@ -1002,20 +1004,17 @@ static void rpmsg_remove(struct virtio_device *vdev) 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, total_buf_space, + dma_free_coherent(vdev->dev.parent, total_buf_space, vrp->rbufs, vrp->bufs_dma); kfree(vrp); @@ -1034,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, |
