#include <linux/mutex.h>
#include <linux/of_device.h>
+#include "rpmsg_internal.h"
+
/**
* struct virtproc_info - virtual remote processor state
* @vdev: the virtio device
struct rpmsg_endpoint *ns_ept;
};
-#define to_rpmsg_device(d) container_of(d, struct rpmsg_device, dev)
-#define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv)
+/* The feature bitmap for virtio rpmsg */
+#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */
+
+/**
+ * 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_hdr {
+ u32 src;
+ u32 dst;
+ u32 reserved;
+ u16 len;
+ u16 flags;
+ u8 data[0];
+} __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
+ *
+ * @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
+ */
+struct virtio_rpmsg_channel {
+ struct rpmsg_device rpdev;
+
+ struct virtproc_info *vrp;
+};
+
+#define to_virtio_rpmsg_channel(_rpdev) \
+ container_of(_rpdev, struct virtio_rpmsg_channel, rpdev)
/*
* We're allocating buffers of 512 bytes each for communications. The
static int virtio_rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src,
u32 dst, void *data, int len);
-/* 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_device *rpdev = to_rpmsg_device(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 ssize_t modalias_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct rpmsg_device *rpdev = to_rpmsg_device(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_device *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_device *rpdev = to_rpmsg_device(dev);
- struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv);
- const struct rpmsg_device_id *ids = rpdrv->id_table;
- unsigned int i;
-
- if (ids)
- for (i = 0; ids[i].name[0]; i++)
- if (rpmsg_id_match(rpdev, &ids[i]))
- return 1;
-
- return of_driver_match_device(dev, drv);
-}
-
-static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- struct rpmsg_device *rpdev = to_rpmsg_device(dev);
-
- return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT,
- rpdev->id.name);
-}
-
static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
.destroy_ept = virtio_rpmsg_destroy_ept,
.send = virtio_rpmsg_send,
void *priv,
struct rpmsg_channel_info chinfo)
{
- return __rpmsg_create_ept(rpdev->vrp, rpdev, cb, priv, chinfo.src);
+ struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev);
+
+ return __rpmsg_create_ept(vch->vrp, rpdev, cb, priv, chinfo.src);
}
/**
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)
-{
- ept->ops->destroy_ept(ept);
-}
-EXPORT_SYMBOL(rpmsg_destroy_ept);
-
static void virtio_rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
{
- __rpmsg_destroy_ept(ept->rpdev->vrp, 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)
-{
- struct rpmsg_device *rpdev = to_rpmsg_device(dev);
- struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
- struct rpmsg_channel_info chinfo = {};
- struct rpmsg_endpoint *ept;
- int err;
-
- strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);
- chinfo.src = rpdev->src;
- chinfo.dst = RPMSG_ADDR_ANY;
-
- ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);
- 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(ept->rpdev);
- if (rpdev->ops->announce_create)
- err = rpdev->ops->announce_create(rpdev);
-out:
- return err;
+ __rpmsg_destroy_ept(vch->vrp, ept);
}
static int virtio_rpmsg_announce_create(struct rpmsg_device *rpdev)
{
- 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;
static int virtio_rpmsg_announce_destroy(struct rpmsg_device *rpdev)
{
- 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;
return err;
}
-static int rpmsg_dev_remove(struct device *dev)
-{
- struct rpmsg_device *rpdev = to_rpmsg_device(dev);
- struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
- int err = 0;
-
- if (rpdev->ops->announce_destroy)
- err = rpdev->ops->announce_destroy(rpdev);
-
- 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,
-};
-
-/**
- * __register_rpmsg_driver() - register an rpmsg driver with the rpmsg bus
- * @rpdrv: pointer to a struct rpmsg_driver
- * @owner: owning module/driver
- *
- * Returns 0 on success, and an appropriate error value on failure.
- */
-int __register_rpmsg_driver(struct rpmsg_driver *rpdrv, struct module *owner)
-{
- rpdrv->drv.bus = &rpmsg_bus;
- rpdrv->drv.owner = owner;
- 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_device *rpdev = to_rpmsg_device(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_device_match(struct device *dev, void *data)
-{
- struct rpmsg_channel_info *chinfo = data;
- struct rpmsg_device *rpdev = to_rpmsg_device(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;
-
- if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE))
- return 0;
-
- /* found a match ! */
- return 1;
-}
-
static const struct rpmsg_device_ops virtio_rpmsg_ops = {
.create_ept = virtio_rpmsg_create_ept,
.announce_create = virtio_rpmsg_announce_create,
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;
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_device_match);
+ tmp = rpmsg_find_device(dev, chinfo);
if (tmp) {
/* decrement the matched device's refcount back */
put_device(tmp);
return NULL;
}
- rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL);
- if (!rpdev)
+ vch = kzalloc(sizeof(*vch), GFP_KERNEL);
+ if (!vch)
return NULL;
- rpdev->vrp = vrp;
+ /* Link the channel to our vrp */
+ vch->vrp = vrp;
+
+ /* Assign callbacks for rpmsg_channel */
+ vch->rpdev.ops = &virtio_rpmsg_ops;
+
+ /* Assign public information to the rpmsg_device */
+ rpdev = &vch->rpdev;
rpdev->src = chinfo->src;
rpdev->dst = chinfo->dst;
rpdev->ops = &virtio_rpmsg_ops;
strncpy(rpdev->id.name, chinfo->name, RPMSG_NAME_SIZE);
- dev_set_name(&rpdev->dev, "%s:%s",
- dev_name(dev->parent), 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);
+ 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_device_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)
{
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;
}
/* invoked when a name service announcement arrives */
-static void rpmsg_ns_cb(struct rpmsg_device *rpdev, void *data, int len,
- void *priv, u32 src)
+static int rpmsg_ns_cb(struct rpmsg_device *rpdev, void *data, int len,
+ void *priv, u32 src)
{
struct rpmsg_ns_msg *msg = data;
struct rpmsg_device *newch;
if (len != sizeof(*msg)) {
dev_err(dev, "malformed ns msg (%d)\n", len);
- return;
+ return -EINVAL;
}
/*
*/
if (rpdev) {
dev_err(dev, "anomaly: ns ept has an rpdev handle\n");
- return;
+ return -EINVAL;
}
/* don't trust the remote processor for null terminating the name */
chinfo.dst = msg->addr;
if (msg->flags & RPMSG_NS_DESTROY) {
- ret = rpmsg_destroy_channel(vrp, &chinfo);
+ ret = rpmsg_unregister_device(&vrp->vdev->dev, &chinfo);
if (ret)
dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret);
} else {
if (!newch)
dev_err(dev, "rpmsg_create_channel failed\n");
}
+
+ return 0;
}
static int rpmsg_probe(struct virtio_device *vdev)
{
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;
}
static void __exit rpmsg_fini(void)
{
unregister_virtio_driver(&virtio_ipc_driver);
- bus_unregister(&rpmsg_bus);
}
module_exit(rpmsg_fini);