Merge tag 'rpmsg-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc
Pull rpmsg updates from Bjorn Andersson:
"This extracts the 'nameserver' previously used only by the virtio
rpmsg transport to work ontop of any rpmsg implementation and
clarifies the endianness of the data types used in rpmsg"
* tag 'rpmsg-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc:
rpmsg: Turn name service into a stand alone driver
rpmsg: Make rpmsg_{register|unregister}_device() public
rpmsg: virtio: Add rpmsg channel device ops
rpmsg: core: Add channel creation internal API
rpmsg: virtio: Rename rpmsg_create_channel
rpmsg: Move structure rpmsg_ns_msg to header file
rpmsg: virtio: Move from virtio to rpmsg byte conversion
rpmsg: Introduce __rpmsg{16|32|64} types
This commit is contained in:
@@ -15,6 +15,14 @@ config RPMSG_CHAR
|
||||
in /dev. They make it possible for user-space programs to send and
|
||||
receive rpmsg packets.
|
||||
|
||||
config RPMSG_NS
|
||||
tristate "RPMSG name service announcement"
|
||||
depends on RPMSG
|
||||
help
|
||||
Say Y here to enable the support of the name service announcement
|
||||
channel that probes the associated RPMsg device on remote endpoint
|
||||
service announcement.
|
||||
|
||||
config RPMSG_MTK_SCP
|
||||
tristate "MediaTek SCP"
|
||||
depends on MTK_SCP
|
||||
@@ -62,6 +70,7 @@ config RPMSG_VIRTIO
|
||||
tristate "Virtio RPMSG bus driver"
|
||||
depends on HAS_DMA
|
||||
select RPMSG
|
||||
select RPMSG_NS
|
||||
select VIRTIO
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_RPMSG) += rpmsg_core.o
|
||||
obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o
|
||||
obj-$(CONFIG_RPMSG_NS) += rpmsg_ns.o
|
||||
obj-$(CONFIG_RPMSG_MTK_SCP) += mtk_rpmsg.o
|
||||
qcom_glink-objs := qcom_glink_native.o qcom_glink_ssr.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_GLINK) += qcom_glink.o
|
||||
|
||||
@@ -20,6 +20,50 @@
|
||||
|
||||
#include "rpmsg_internal.h"
|
||||
|
||||
/**
|
||||
* rpmsg_create_channel() - create a new rpmsg channel
|
||||
* using its name and address info.
|
||||
* @rpdev: rpmsg device
|
||||
* @chinfo: channel_info to bind
|
||||
*
|
||||
* Returns a pointer to the new rpmsg device on success, or NULL on error.
|
||||
*/
|
||||
struct rpmsg_device *rpmsg_create_channel(struct rpmsg_device *rpdev,
|
||||
struct rpmsg_channel_info *chinfo)
|
||||
{
|
||||
if (WARN_ON(!rpdev))
|
||||
return NULL;
|
||||
if (!rpdev->ops || !rpdev->ops->create_channel) {
|
||||
dev_err(&rpdev->dev, "no create_channel ops found\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rpdev->ops->create_channel(rpdev, chinfo);
|
||||
}
|
||||
EXPORT_SYMBOL(rpmsg_create_channel);
|
||||
|
||||
/**
|
||||
* rpmsg_release_channel() - release a rpmsg channel
|
||||
* using its name and address info.
|
||||
* @rpdev: rpmsg device
|
||||
* @chinfo: channel_info to bind
|
||||
*
|
||||
* Returns 0 on success or an appropriate error value.
|
||||
*/
|
||||
int rpmsg_release_channel(struct rpmsg_device *rpdev,
|
||||
struct rpmsg_channel_info *chinfo)
|
||||
{
|
||||
if (WARN_ON(!rpdev))
|
||||
return -EINVAL;
|
||||
if (!rpdev->ops || !rpdev->ops->release_channel) {
|
||||
dev_err(&rpdev->dev, "no release_channel ops found\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return rpdev->ops->release_channel(rpdev, chinfo);
|
||||
}
|
||||
EXPORT_SYMBOL(rpmsg_release_channel);
|
||||
|
||||
/**
|
||||
* rpmsg_create_ept() - create a new rpmsg_endpoint
|
||||
* @rpdev: rpmsg channel device
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
/**
|
||||
* struct rpmsg_device_ops - indirection table for the rpmsg_device operations
|
||||
* @create_channel: create backend-specific channel, optional
|
||||
* @release_channel: release backend-specific channel, optional
|
||||
* @create_ept: create backend-specific endpoint, required
|
||||
* @announce_create: announce presence of new channel, optional
|
||||
* @announce_destroy: announce destruction of channel, optional
|
||||
@@ -29,6 +31,10 @@
|
||||
* advertise new channels implicitly by creating the endpoints.
|
||||
*/
|
||||
struct rpmsg_device_ops {
|
||||
struct rpmsg_device *(*create_channel)(struct rpmsg_device *rpdev,
|
||||
struct rpmsg_channel_info *chinfo);
|
||||
int (*release_channel)(struct rpmsg_device *rpdev,
|
||||
struct rpmsg_channel_info *chinfo);
|
||||
struct rpmsg_endpoint *(*create_ept)(struct rpmsg_device *rpdev,
|
||||
rpmsg_rx_cb_t cb, void *priv,
|
||||
struct rpmsg_channel_info chinfo);
|
||||
@@ -68,13 +74,13 @@ struct rpmsg_endpoint_ops {
|
||||
poll_table *wait);
|
||||
};
|
||||
|
||||
int rpmsg_register_device(struct rpmsg_device *rpdev);
|
||||
int rpmsg_unregister_device(struct device *parent,
|
||||
struct rpmsg_channel_info *chinfo);
|
||||
|
||||
struct device *rpmsg_find_device(struct device *parent,
|
||||
struct rpmsg_channel_info *chinfo);
|
||||
|
||||
struct rpmsg_device *rpmsg_create_channel(struct rpmsg_device *rpdev,
|
||||
struct rpmsg_channel_info *chinfo);
|
||||
int rpmsg_release_channel(struct rpmsg_device *rpdev,
|
||||
struct rpmsg_channel_info *chinfo);
|
||||
/**
|
||||
* rpmsg_chrdev_register_device() - register chrdev device based on rpdev
|
||||
* @rpdev: prepared rpdev to be used for creating endpoints
|
||||
|
||||
126
drivers/rpmsg/rpmsg_ns.c
Normal file
126
drivers/rpmsg/rpmsg_ns.c
Normal file
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2020 - All Rights Reserved
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/rpmsg/ns.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "rpmsg_internal.h"
|
||||
|
||||
/**
|
||||
* rpmsg_ns_register_device() - register name service device based on rpdev
|
||||
* @rpdev: prepared rpdev to be used for creating endpoints
|
||||
*
|
||||
* This function wraps rpmsg_register_device() preparing the rpdev for use as
|
||||
* basis for the rpmsg name service device.
|
||||
*/
|
||||
int rpmsg_ns_register_device(struct rpmsg_device *rpdev)
|
||||
{
|
||||
strcpy(rpdev->id.name, "rpmsg_ns");
|
||||
rpdev->driver_override = "rpmsg_ns";
|
||||
rpdev->src = RPMSG_NS_ADDR;
|
||||
rpdev->dst = RPMSG_NS_ADDR;
|
||||
|
||||
return rpmsg_register_device(rpdev);
|
||||
}
|
||||
EXPORT_SYMBOL(rpmsg_ns_register_device);
|
||||
|
||||
/* invoked when a name service announcement arrives */
|
||||
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;
|
||||
struct rpmsg_channel_info chinfo;
|
||||
struct device *dev = rpdev->dev.parent;
|
||||
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;
|
||||
}
|
||||
|
||||
/* don't trust the remote processor for null terminating the name */
|
||||
msg->name[RPMSG_NAME_SIZE - 1] = '\0';
|
||||
|
||||
strncpy(chinfo.name, msg->name, sizeof(chinfo.name));
|
||||
chinfo.src = RPMSG_ADDR_ANY;
|
||||
chinfo.dst = rpmsg32_to_cpu(rpdev, msg->addr);
|
||||
|
||||
dev_info(dev, "%sing channel %s addr 0x%x\n",
|
||||
rpmsg32_to_cpu(rpdev, msg->flags) & RPMSG_NS_DESTROY ?
|
||||
"destroy" : "creat", msg->name, chinfo.dst);
|
||||
|
||||
if (rpmsg32_to_cpu(rpdev, msg->flags) & RPMSG_NS_DESTROY) {
|
||||
ret = rpmsg_release_channel(rpdev, &chinfo);
|
||||
if (ret)
|
||||
dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret);
|
||||
} else {
|
||||
newch = rpmsg_create_channel(rpdev, &chinfo);
|
||||
if (!newch)
|
||||
dev_err(dev, "rpmsg_create_channel failed\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rpmsg_ns_probe(struct rpmsg_device *rpdev)
|
||||
{
|
||||
struct rpmsg_endpoint *ns_ept;
|
||||
struct rpmsg_channel_info ns_chinfo = {
|
||||
.src = RPMSG_NS_ADDR,
|
||||
.dst = RPMSG_NS_ADDR,
|
||||
.name = "name_service",
|
||||
};
|
||||
|
||||
/*
|
||||
* Create the NS announcement service endpoint associated to the RPMsg
|
||||
* device. The endpoint will be automatically destroyed when the RPMsg
|
||||
* device will be deleted.
|
||||
*/
|
||||
ns_ept = rpmsg_create_ept(rpdev, rpmsg_ns_cb, NULL, ns_chinfo);
|
||||
if (!ns_ept) {
|
||||
dev_err(&rpdev->dev, "failed to create the ns ept\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
rpdev->ept = ns_ept;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rpmsg_driver rpmsg_ns_driver = {
|
||||
.drv.name = KBUILD_MODNAME,
|
||||
.probe = rpmsg_ns_probe,
|
||||
};
|
||||
|
||||
static int rpmsg_ns_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_rpmsg_driver(&rpmsg_ns_driver);
|
||||
if (ret < 0)
|
||||
pr_err("%s: Failed to register rpmsg driver\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
postcore_initcall(rpmsg_ns_init);
|
||||
|
||||
static void rpmsg_ns_exit(void)
|
||||
{
|
||||
unregister_rpmsg_driver(&rpmsg_ns_driver);
|
||||
}
|
||||
module_exit(rpmsg_ns_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Name service announcement rpmsg driver");
|
||||
MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
|
||||
MODULE_ALIAS("rpmsg:" KBUILD_MODNAME);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -19,11 +19,12 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/rpmsg/byteorder.h>
|
||||
#include <linux/rpmsg/ns.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/virtio.h>
|
||||
#include <linux/virtio_byteorder.h>
|
||||
#include <linux/virtio_ids.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/wait.h>
|
||||
@@ -48,7 +49,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
|
||||
@@ -67,7 +67,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 */
|
||||
@@ -85,42 +84,14 @@ struct virtproc_info {
|
||||
* Every message sent(/received) on the rpmsg bus begins with this header.
|
||||
*/
|
||||
struct rpmsg_hdr {
|
||||
__virtio32 src;
|
||||
__virtio32 dst;
|
||||
__virtio32 reserved;
|
||||
__virtio16 len;
|
||||
__virtio16 flags;
|
||||
__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];
|
||||
__virtio32 addr;
|
||||
__virtio32 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,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct virtio_rpmsg_channel - rpmsg channel descriptor
|
||||
@@ -167,9 +138,6 @@ 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,
|
||||
@@ -181,6 +149,8 @@ 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,
|
||||
@@ -285,6 +255,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,
|
||||
@@ -341,8 +329,8 @@ static int virtio_rpmsg_announce_create(struct rpmsg_device *rpdev)
|
||||
struct rpmsg_ns_msg nsm;
|
||||
|
||||
strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
|
||||
nsm.addr = cpu_to_virtio32(vrp->vdev, rpdev->ept->addr);
|
||||
nsm.flags = cpu_to_virtio32(vrp->vdev, RPMSG_NS_CREATE);
|
||||
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)
|
||||
@@ -365,8 +353,8 @@ static int virtio_rpmsg_announce_destroy(struct rpmsg_device *rpdev)
|
||||
struct rpmsg_ns_msg nsm;
|
||||
|
||||
strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
|
||||
nsm.addr = cpu_to_virtio32(vrp->vdev, rpdev->ept->addr);
|
||||
nsm.flags = cpu_to_virtio32(vrp->vdev, RPMSG_NS_DESTROY);
|
||||
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)
|
||||
@@ -377,6 +365,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,
|
||||
@@ -395,7 +385,7 @@ 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,
|
||||
static struct rpmsg_device *__rpmsg_create_channel(struct virtproc_info *vrp,
|
||||
struct rpmsg_channel_info *chinfo)
|
||||
{
|
||||
struct virtio_rpmsg_channel *vch;
|
||||
@@ -425,6 +415,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),
|
||||
@@ -618,10 +609,10 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
|
||||
}
|
||||
}
|
||||
|
||||
msg->len = cpu_to_virtio16(vrp->vdev, len);
|
||||
msg->len = cpu_to_rpmsg16(rpdev, len);
|
||||
msg->flags = 0;
|
||||
msg->src = cpu_to_virtio32(vrp->vdev, src);
|
||||
msg->dst = cpu_to_virtio32(vrp->vdev, dst);
|
||||
msg->src = cpu_to_rpmsg32(rpdev, src);
|
||||
msg->dst = cpu_to_rpmsg32(rpdev, dst);
|
||||
msg->reserved = 0;
|
||||
memcpy(msg->data, data, len);
|
||||
|
||||
@@ -710,14 +701,15 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
|
||||
{
|
||||
struct rpmsg_endpoint *ept;
|
||||
struct scatterlist sg;
|
||||
unsigned int msg_len = virtio16_to_cpu(vrp->vdev, msg->len);
|
||||
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",
|
||||
virtio32_to_cpu(vrp->vdev, msg->src),
|
||||
virtio32_to_cpu(vrp->vdev, msg->dst), msg_len,
|
||||
virtio16_to_cpu(vrp->vdev, msg->flags),
|
||||
virtio32_to_cpu(vrp->vdev, 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);
|
||||
@@ -736,7 +728,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
|
||||
/* use the dst addr to fetch the callback of the appropriate user */
|
||||
mutex_lock(&vrp->endpoints_lock);
|
||||
|
||||
ept = idr_find(&vrp->endpoints, virtio32_to_cpu(vrp->vdev, 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)
|
||||
@@ -750,7 +742,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
|
||||
|
||||
if (ept->cb)
|
||||
ept->cb(ept->rpdev, msg->data, msg_len, ept->priv,
|
||||
virtio32_to_cpu(vrp->vdev, msg->src));
|
||||
__rpmsg32_to_cpu(little_endian, msg->src));
|
||||
|
||||
mutex_unlock(&ept->cb_lock);
|
||||
|
||||
@@ -821,68 +813,14 @@ 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* don't trust the remote processor for null terminating the name */
|
||||
msg->name[RPMSG_NAME_SIZE - 1] = '\0';
|
||||
|
||||
strncpy(chinfo.name, msg->name, sizeof(chinfo.name));
|
||||
chinfo.src = RPMSG_ADDR_ANY;
|
||||
chinfo.dst = virtio32_to_cpu(vrp->vdev, msg->addr);
|
||||
|
||||
dev_info(dev, "%sing channel %s addr 0x%x\n",
|
||||
virtio32_to_cpu(vrp->vdev, msg->flags) & RPMSG_NS_DESTROY ?
|
||||
"destroy" : "creat", msg->name, chinfo.dst);
|
||||
|
||||
if (virtio32_to_cpu(vrp->vdev, 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");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 *vqs[2];
|
||||
struct virtproc_info *vrp;
|
||||
struct virtio_rpmsg_channel *vch;
|
||||
struct rpmsg_device *rpdev_ns;
|
||||
void *bufs_va;
|
||||
int err = 0, i;
|
||||
size_t total_buf_space;
|
||||
@@ -958,14 +896,26 @@ static int rpmsg_probe(struct virtio_device *vdev)
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
goto free_coherent;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -990,6 +940,7 @@ static int rpmsg_probe(struct virtio_device *vdev)
|
||||
return 0;
|
||||
|
||||
free_coherent:
|
||||
kfree(vch);
|
||||
dma_free_coherent(vdev->dev.parent, total_buf_space,
|
||||
bufs_va, vrp->bufs_dma);
|
||||
vqs_del:
|
||||
@@ -1018,9 +969,6 @@ static void rpmsg_remove(struct virtio_device *vdev)
|
||||
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);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/kref.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/rpmsg/byteorder.h>
|
||||
|
||||
#define RPMSG_ADDR_ANY 0xFFFFFFFF
|
||||
|
||||
@@ -46,6 +47,7 @@ struct rpmsg_channel_info {
|
||||
* @dst: destination address
|
||||
* @ept: the rpmsg endpoint of this channel
|
||||
* @announce: if set, rpmsg will announce the creation/removal of this channel
|
||||
* @little_endian: True if transport is using little endian byte representation
|
||||
*/
|
||||
struct rpmsg_device {
|
||||
struct device dev;
|
||||
@@ -55,6 +57,7 @@ struct rpmsg_device {
|
||||
u32 dst;
|
||||
struct rpmsg_endpoint *ept;
|
||||
bool announce;
|
||||
bool little_endian;
|
||||
|
||||
const struct rpmsg_device_ops *ops;
|
||||
};
|
||||
@@ -111,10 +114,59 @@ struct rpmsg_driver {
|
||||
int (*callback)(struct rpmsg_device *, void *, int, void *, u32);
|
||||
};
|
||||
|
||||
static inline u16 rpmsg16_to_cpu(struct rpmsg_device *rpdev, __rpmsg16 val)
|
||||
{
|
||||
if (!rpdev)
|
||||
return __rpmsg16_to_cpu(rpmsg_is_little_endian(), val);
|
||||
else
|
||||
return __rpmsg16_to_cpu(rpdev->little_endian, val);
|
||||
}
|
||||
|
||||
static inline __rpmsg16 cpu_to_rpmsg16(struct rpmsg_device *rpdev, u16 val)
|
||||
{
|
||||
if (!rpdev)
|
||||
return __cpu_to_rpmsg16(rpmsg_is_little_endian(), val);
|
||||
else
|
||||
return __cpu_to_rpmsg16(rpdev->little_endian, val);
|
||||
}
|
||||
|
||||
static inline u32 rpmsg32_to_cpu(struct rpmsg_device *rpdev, __rpmsg32 val)
|
||||
{
|
||||
if (!rpdev)
|
||||
return __rpmsg32_to_cpu(rpmsg_is_little_endian(), val);
|
||||
else
|
||||
return __rpmsg32_to_cpu(rpdev->little_endian, val);
|
||||
}
|
||||
|
||||
static inline __rpmsg32 cpu_to_rpmsg32(struct rpmsg_device *rpdev, u32 val)
|
||||
{
|
||||
if (!rpdev)
|
||||
return __cpu_to_rpmsg32(rpmsg_is_little_endian(), val);
|
||||
else
|
||||
return __cpu_to_rpmsg32(rpdev->little_endian, val);
|
||||
}
|
||||
|
||||
static inline u64 rpmsg64_to_cpu(struct rpmsg_device *rpdev, __rpmsg64 val)
|
||||
{
|
||||
if (!rpdev)
|
||||
return __rpmsg64_to_cpu(rpmsg_is_little_endian(), val);
|
||||
else
|
||||
return __rpmsg64_to_cpu(rpdev->little_endian, val);
|
||||
}
|
||||
|
||||
static inline __rpmsg64 cpu_to_rpmsg64(struct rpmsg_device *rpdev, u64 val)
|
||||
{
|
||||
if (!rpdev)
|
||||
return __cpu_to_rpmsg64(rpmsg_is_little_endian(), val);
|
||||
else
|
||||
return __cpu_to_rpmsg64(rpdev->little_endian, val);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_RPMSG)
|
||||
|
||||
int register_rpmsg_device(struct rpmsg_device *dev);
|
||||
void unregister_rpmsg_device(struct rpmsg_device *dev);
|
||||
int rpmsg_register_device(struct rpmsg_device *rpdev);
|
||||
int rpmsg_unregister_device(struct device *parent,
|
||||
struct rpmsg_channel_info *chinfo);
|
||||
int __register_rpmsg_driver(struct rpmsg_driver *drv, struct module *owner);
|
||||
void unregister_rpmsg_driver(struct rpmsg_driver *drv);
|
||||
void rpmsg_destroy_ept(struct rpmsg_endpoint *);
|
||||
@@ -137,15 +189,18 @@ __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,
|
||||
|
||||
#else
|
||||
|
||||
static inline int register_rpmsg_device(struct rpmsg_device *dev)
|
||||
static inline int rpmsg_register_device(struct rpmsg_device *rpdev)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline void unregister_rpmsg_device(struct rpmsg_device *dev)
|
||||
static inline int rpmsg_unregister_device(struct device *parent,
|
||||
struct rpmsg_channel_info *chinfo)
|
||||
{
|
||||
/* This shouldn't be possible */
|
||||
WARN_ON(1);
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline int __register_rpmsg_driver(struct rpmsg_driver *drv,
|
||||
|
||||
67
include/linux/rpmsg/byteorder.h
Normal file
67
include/linux/rpmsg/byteorder.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Follows implementation found in linux/virtio_byteorder.h
|
||||
*/
|
||||
#ifndef _LINUX_RPMSG_BYTEORDER_H
|
||||
#define _LINUX_RPMSG_BYTEORDER_H
|
||||
#include <linux/types.h>
|
||||
#include <uapi/linux/rpmsg_types.h>
|
||||
|
||||
static inline bool rpmsg_is_little_endian(void)
|
||||
{
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline u16 __rpmsg16_to_cpu(bool little_endian, __rpmsg16 val)
|
||||
{
|
||||
if (little_endian)
|
||||
return le16_to_cpu((__force __le16)val);
|
||||
else
|
||||
return be16_to_cpu((__force __be16)val);
|
||||
}
|
||||
|
||||
static inline __rpmsg16 __cpu_to_rpmsg16(bool little_endian, u16 val)
|
||||
{
|
||||
if (little_endian)
|
||||
return (__force __rpmsg16)cpu_to_le16(val);
|
||||
else
|
||||
return (__force __rpmsg16)cpu_to_be16(val);
|
||||
}
|
||||
|
||||
static inline u32 __rpmsg32_to_cpu(bool little_endian, __rpmsg32 val)
|
||||
{
|
||||
if (little_endian)
|
||||
return le32_to_cpu((__force __le32)val);
|
||||
else
|
||||
return be32_to_cpu((__force __be32)val);
|
||||
}
|
||||
|
||||
static inline __rpmsg32 __cpu_to_rpmsg32(bool little_endian, u32 val)
|
||||
{
|
||||
if (little_endian)
|
||||
return (__force __rpmsg32)cpu_to_le32(val);
|
||||
else
|
||||
return (__force __rpmsg32)cpu_to_be32(val);
|
||||
}
|
||||
|
||||
static inline u64 __rpmsg64_to_cpu(bool little_endian, __rpmsg64 val)
|
||||
{
|
||||
if (little_endian)
|
||||
return le64_to_cpu((__force __le64)val);
|
||||
else
|
||||
return be64_to_cpu((__force __be64)val);
|
||||
}
|
||||
|
||||
static inline __rpmsg64 __cpu_to_rpmsg64(bool little_endian, u64 val)
|
||||
{
|
||||
if (little_endian)
|
||||
return (__force __rpmsg64)cpu_to_le64(val);
|
||||
else
|
||||
return (__force __rpmsg64)cpu_to_be64(val);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_RPMSG_BYTEORDER_H */
|
||||
45
include/linux/rpmsg/ns.h
Normal file
45
include/linux/rpmsg/ns.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _LINUX_RPMSG_NS_H
|
||||
#define _LINUX_RPMSG_NS_H
|
||||
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/rpmsg/byteorder.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* 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];
|
||||
__rpmsg32 addr;
|
||||
__rpmsg32 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,
|
||||
};
|
||||
|
||||
/* Address 53 is reserved for advertising remote services */
|
||||
#define RPMSG_NS_ADDR (53)
|
||||
|
||||
int rpmsg_ns_register_device(struct rpmsg_device *rpdev);
|
||||
|
||||
#endif
|
||||
11
include/uapi/linux/rpmsg_types.h
Normal file
11
include/uapi/linux/rpmsg_types.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef _UAPI_LINUX_RPMSG_TYPES_H
|
||||
#define _UAPI_LINUX_RPMSG_TYPES_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
typedef __u16 __bitwise __rpmsg16;
|
||||
typedef __u32 __bitwise __rpmsg32;
|
||||
typedef __u64 __bitwise __rpmsg64;
|
||||
|
||||
#endif /* _UAPI_LINUX_RPMSG_TYPES_H */
|
||||
Reference in New Issue
Block a user