diff options
Diffstat (limited to 'drivers/rpmsg')
| -rw-r--r-- | drivers/rpmsg/Kconfig | 23 | ||||
| -rw-r--r-- | drivers/rpmsg/Makefile | 5 | ||||
| -rw-r--r-- | drivers/rpmsg/mtk_rpmsg.c | 14 | ||||
| -rw-r--r-- | drivers/rpmsg/qcom_glink_native.c | 191 | ||||
| -rw-r--r-- | drivers/rpmsg/qcom_glink_ssr.c | 167 | ||||
| -rw-r--r-- | drivers/rpmsg/qcom_smd.c | 74 | ||||
| -rw-r--r-- | drivers/rpmsg/rpmsg_char.c | 278 | ||||
| -rw-r--r-- | drivers/rpmsg/rpmsg_char.h | 46 | ||||
| -rw-r--r-- | drivers/rpmsg/rpmsg_core.c | 178 | ||||
| -rw-r--r-- | drivers/rpmsg/rpmsg_ctrl.c | 243 | ||||
| -rw-r--r-- | drivers/rpmsg/rpmsg_internal.h | 31 | ||||
| -rw-r--r-- | drivers/rpmsg/rpmsg_ns.c | 124 | ||||
| -rw-r--r-- | drivers/rpmsg/virtio_rpmsg_bus.c | 250 | 
13 files changed, 1239 insertions, 385 deletions
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index a9108ff563dc..d3795860f5c0 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -15,6 +15,22 @@ config RPMSG_CHAR  	  in /dev. They make it possible for user-space programs to send and  	  receive rpmsg packets. +config RPMSG_CTRL +	tristate "RPMSG control interface" +	depends on RPMSG && ( RPMSG_CHAR || RPMSG_CHAR=n ) +	help +	  Say Y here to enable the support of the /dev/rpmsg_ctrlX API. This API +	  allows user-space programs to create endpoints with specific service name, +	  source and destination addresses. + +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 @@ -24,13 +40,13 @@ config RPMSG_MTK_SCP  	  remote processors in MediaTek platforms.  	  This use IPI and IPC to communicate with remote processors. -config RPMSG_QCOM_GLINK_NATIVE +config RPMSG_QCOM_GLINK  	tristate  	select RPMSG  config RPMSG_QCOM_GLINK_RPM  	tristate "Qualcomm RPM Glink driver" -	select RPMSG_QCOM_GLINK_NATIVE +	select RPMSG_QCOM_GLINK  	depends on HAS_IOMEM  	depends on MAILBOX  	help @@ -40,7 +56,7 @@ config RPMSG_QCOM_GLINK_RPM  config RPMSG_QCOM_GLINK_SMEM  	tristate "Qualcomm SMEM Glink driver" -	select RPMSG_QCOM_GLINK_NATIVE +	select RPMSG_QCOM_GLINK  	depends on MAILBOX  	depends on QCOM_SMEM  	help @@ -62,6 +78,7 @@ config RPMSG_VIRTIO  	tristate "Virtio RPMSG bus driver"  	depends on HAS_DMA  	select RPMSG +	select RPMSG_NS  	select VIRTIO  endmenu diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index ae92a7fb08f6..58e3b382e316 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -1,9 +1,12 @@  # SPDX-License-Identifier: GPL-2.0  obj-$(CONFIG_RPMSG)		+= rpmsg_core.o  obj-$(CONFIG_RPMSG_CHAR)	+= rpmsg_char.o +obj-$(CONFIG_RPMSG_CTRL)	+= rpmsg_ctrl.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  obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o -obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o  obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o  obj-$(CONFIG_RPMSG_QCOM_SMD)	+= qcom_smd.o  obj-$(CONFIG_RPMSG_VIRTIO)	+= virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/mtk_rpmsg.c b/drivers/rpmsg/mtk_rpmsg.c index 232aa4e40133..d1213c33da20 100644 --- a/drivers/rpmsg/mtk_rpmsg.c +++ b/drivers/rpmsg/mtk_rpmsg.c @@ -8,6 +8,7 @@  #include <linux/platform_device.h>  #include <linux/remoteproc.h>  #include <linux/rpmsg/mtk_rpmsg.h> +#include <linux/slab.h>  #include <linux/workqueue.h>  #include "rpmsg_internal.h" @@ -182,7 +183,7 @@ mtk_rpmsg_match_device_subnode(struct device_node *node, const char *channel)  	int ret;  	for_each_available_child_of_node(node, child) { -		ret = of_property_read_string(child, "mtk,rpmsg-name", &name); +		ret = of_property_read_string(child, "mediatek,rpmsg-name", &name);  		if (ret)  			continue; @@ -199,7 +200,6 @@ static int mtk_rpmsg_register_device(struct mtk_rpmsg_rproc_subdev *mtk_subdev,  	struct rpmsg_device *rpdev;  	struct mtk_rpmsg_device *mdev;  	struct platform_device *pdev = mtk_subdev->pdev; -	int ret;  	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);  	if (!mdev) @@ -218,13 +218,7 @@ static int mtk_rpmsg_register_device(struct mtk_rpmsg_rproc_subdev *mtk_subdev,  	rpdev->dev.parent = &pdev->dev;  	rpdev->dev.release = mtk_rpmsg_release_device; -	ret = rpmsg_register_device(rpdev); -	if (ret) { -		kfree(mdev); -		return ret; -	} - -	return 0; +	return rpmsg_register_device(rpdev);  }  static void mtk_register_device_work_function(struct work_struct *register_work) @@ -240,7 +234,9 @@ static void mtk_register_device_work_function(struct work_struct *register_work)  		if (info->registered)  			continue; +		mutex_unlock(&subdev->channels_lock);  		ret = mtk_rpmsg_register_device(subdev, &info->info); +		mutex_lock(&subdev->channels_lock);  		if (ret) {  			dev_err(&pdev->dev, "Can't create rpmsg_device\n");  			continue; diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 1995f5b3ea67..115c0a1eddb1 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -92,12 +92,12 @@ struct glink_core_rx_intent {   * @rcids:	idr of all channels with a known remote channel id   * @features:	remote features   * @intentless:	flag to indicate that there is no intent + * @tx_avail_notify: Waitqueue for pending tx tasks + * @sent_read_notify: flag to check cmd sent or not   */  struct qcom_glink {  	struct device *dev; -	const char *name; -  	struct mbox_client mbox_client;  	struct mbox_chan *mbox_chan; @@ -118,6 +118,8 @@ struct qcom_glink {  	unsigned long features;  	bool intentless; +	wait_queue_head_t tx_avail_notify; +	bool sent_read_notify;  };  enum { @@ -301,6 +303,20 @@ static void qcom_glink_tx_write(struct qcom_glink *glink,  	glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen);  } +static void qcom_glink_send_read_notify(struct qcom_glink *glink) +{ +	struct glink_msg msg; + +	msg.cmd = cpu_to_le16(RPM_CMD_READ_NOTIF); +	msg.param1 = 0; +	msg.param2 = 0; + +	qcom_glink_tx_write(glink, &msg, sizeof(msg), NULL, 0); + +	mbox_send_message(glink->mbox_chan, NULL); +	mbox_client_txdone(glink->mbox_chan, 0); +} +  static int qcom_glink_tx(struct qcom_glink *glink,  			 const void *hdr, size_t hlen,  			 const void *data, size_t dlen, bool wait) @@ -321,12 +337,21 @@ static int qcom_glink_tx(struct qcom_glink *glink,  			goto out;  		} +		if (!glink->sent_read_notify) { +			glink->sent_read_notify = true; +			qcom_glink_send_read_notify(glink); +		} +  		/* Wait without holding the tx_lock */  		spin_unlock_irqrestore(&glink->tx_lock, flags); -		usleep_range(10000, 15000); +		wait_event_timeout(glink->tx_avail_notify, +				   qcom_glink_tx_avail(glink) >= tlen, 10 * HZ);  		spin_lock_irqsave(&glink->tx_lock, flags); + +		if (qcom_glink_tx_avail(glink) >= tlen) +			glink->sent_read_notify = false;  	}  	qcom_glink_tx_write(glink, hdr, hlen, data, dlen); @@ -400,7 +425,7 @@ static void qcom_glink_handle_intent_req_ack(struct qcom_glink *glink,   * Allocates a local channel id and sends a RPM_CMD_OPEN message to the remote.   * Will return with refcount held, regardless of outcome.   * - * Returns 0 on success, negative errno otherwise. + * Return: 0 on success, negative errno otherwise.   */  static int qcom_glink_send_open_req(struct qcom_glink *glink,  				    struct glink_channel *channel) @@ -553,7 +578,7 @@ static void qcom_glink_receive_version(struct qcom_glink *glink,  		break;  	case GLINK_VERSION_1:  		glink->features &= features; -		/* FALLTHROUGH */ +		fallthrough;  	default:  		qcom_glink_send_version_ack(glink);  		break; @@ -584,7 +609,7 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink,  			break;  		glink->features &= features; -		/* FALLTHROUGH */ +		fallthrough;  	default:  		qcom_glink_send_version(glink);  		break; @@ -765,7 +790,7 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)  		return -ENXIO;  	} -	dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_ATOMIC); +	dcmd = kzalloc(struct_size(dcmd, data, extra), GFP_ATOMIC);  	if (!dcmd)  		return -ENOMEM; @@ -857,6 +882,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)  			dev_err(glink->dev,  				"no intent found for channel %s intent %d",  				channel->name, liid); +			ret = -ENOENT;  			goto advance_rx;  		}  	} @@ -970,7 +996,7 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)  		return -EINVAL;  	} -	complete(&channel->open_ack); +	complete_all(&channel->open_ack);  	return 0;  } @@ -985,6 +1011,9 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data)  	unsigned int cmd;  	int ret = 0; +	/* To wakeup any blocking writers */ +	wake_up_all(&glink->tx_avail_notify); +  	for (;;) {  		avail = qcom_glink_rx_avail(glink);  		if (avail < sizeof(msg)) @@ -1178,7 +1207,7 @@ static int qcom_glink_announce_create(struct rpmsg_device *rpdev)  	__be32 *val = defaults;  	int size; -	if (glink->intentless) +	if (glink->intentless || !completion_done(&channel->open_ack))  		return 0;  	prop = of_find_property(np, "qcom,intents", NULL); @@ -1270,6 +1299,8 @@ static int __qcom_glink_send(struct glink_channel *channel,  	} __packed req;  	int ret;  	unsigned long flags; +	int chunk_size = len; +	int left_size = 0;  	if (!glink->intentless) {  		while (!intent) { @@ -1303,18 +1334,46 @@ static int __qcom_glink_send(struct glink_channel *channel,  		iid = intent->id;  	} +	if (wait && chunk_size > SZ_8K) { +		chunk_size = SZ_8K; +		left_size = len - chunk_size; +	}  	req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA);  	req.msg.param1 = cpu_to_le16(channel->lcid);  	req.msg.param2 = cpu_to_le32(iid); -	req.chunk_size = cpu_to_le32(len); -	req.left_size = cpu_to_le32(0); +	req.chunk_size = cpu_to_le32(chunk_size); +	req.left_size = cpu_to_le32(left_size); -	ret = qcom_glink_tx(glink, &req, sizeof(req), data, len, wait); +	ret = qcom_glink_tx(glink, &req, sizeof(req), data, chunk_size, wait);  	/* Mark intent available if we failed */ -	if (ret && intent) +	if (ret && intent) {  		intent->in_use = false; +		return ret; +	} +	while (left_size > 0) { +		data = (void *)((char *)data + chunk_size); +		chunk_size = left_size; +		if (chunk_size > SZ_8K) +			chunk_size = SZ_8K; +		left_size -= chunk_size; + +		req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA_CONT); +		req.msg.param1 = cpu_to_le16(channel->lcid); +		req.msg.param2 = cpu_to_le32(iid); +		req.chunk_size = cpu_to_le32(chunk_size); +		req.left_size = cpu_to_le32(left_size); + +		ret = qcom_glink_tx(glink, &req, sizeof(req), data, +				    chunk_size, wait); + +		/* Mark intent available if we failed */ +		if (ret && intent) { +			intent->in_use = false; +			break; +		} +	}  	return ret;  } @@ -1332,6 +1391,20 @@ static int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len)  	return __qcom_glink_send(channel, data, len, false);  } +static int qcom_glink_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +{ +	struct glink_channel *channel = to_glink_channel(ept); + +	return __qcom_glink_send(channel, data, len, true); +} + +static int qcom_glink_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +{ +	struct glink_channel *channel = to_glink_channel(ept); + +	return __qcom_glink_send(channel, data, len, false); +} +  /*   * Finds the device_node for the glink child interested in this channel.   */ @@ -1364,15 +1437,15 @@ static const struct rpmsg_device_ops glink_device_ops = {  static const struct rpmsg_endpoint_ops glink_endpoint_ops = {  	.destroy_ept = qcom_glink_destroy_ept,  	.send = qcom_glink_send, +	.sendto = qcom_glink_sendto,  	.trysend = qcom_glink_trysend, +	.trysendto = qcom_glink_trysendto,  };  static void qcom_glink_rpdev_release(struct device *dev)  {  	struct rpmsg_device *rpdev = to_rpmsg_device(dev); -	struct glink_channel *channel = to_glink_channel(rpdev->ept); -	channel->rpdev = NULL;  	kfree(rpdev);  } @@ -1413,7 +1486,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,  	channel->rcid = ret;  	spin_unlock_irqrestore(&glink->idr_lock, flags); -	complete(&channel->open_req); +	complete_all(&channel->open_req);  	if (create_device) {  		rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); @@ -1423,7 +1496,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,  		}  		rpdev->ept = &channel->ept; -		strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE); +		strscpy_pad(rpdev->id.name, name, RPMSG_NAME_SIZE);  		rpdev->src = RPMSG_ADDR_ANY;  		rpdev->dst = RPMSG_ADDR_ANY;  		rpdev->ops = &glink_device_ops; @@ -1471,12 +1544,13 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)  	cancel_work_sync(&channel->intent_work);  	if (channel->rpdev) { -		strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); +		strscpy_pad(chinfo.name, channel->name, sizeof(chinfo.name));  		chinfo.src = RPMSG_ADDR_ANY;  		chinfo.dst = RPMSG_ADDR_ANY;  		rpmsg_unregister_device(glink->dev, &chinfo);  	} +	channel->rpdev = NULL;  	qcom_glink_send_close_ack(glink, channel->rcid); @@ -1490,9 +1564,13 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)  static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)  { +	struct rpmsg_channel_info chinfo;  	struct glink_channel *channel;  	unsigned long flags; +	/* To wakeup any blocking writers */ +	wake_up_all(&glink->tx_avail_notify); +  	spin_lock_irqsave(&glink->idr_lock, flags);  	channel = idr_find(&glink->lcids, lcid);  	if (WARN(!channel, "close ack on unknown channel\n")) { @@ -1504,6 +1582,16 @@ static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)  	channel->lcid = 0;  	spin_unlock_irqrestore(&glink->idr_lock, flags); +	/* Decouple the potential rpdev from the channel */ +	if (channel->rpdev) { +		strscpy(chinfo.name, channel->name, sizeof(chinfo.name)); +		chinfo.src = RPMSG_ADDR_ANY; +		chinfo.dst = RPMSG_ADDR_ANY; + +		rpmsg_unregister_device(glink->dev, &chinfo); +	} +	channel->rpdev = NULL; +  	kref_put(&channel->refcount, qcom_glink_channel_release);  } @@ -1574,6 +1662,60 @@ static void qcom_glink_cancel_rx_work(struct qcom_glink *glink)  		kfree(dcmd);  } +static ssize_t rpmsg_name_show(struct device *dev, +			       struct device_attribute *attr, char *buf) +{ +	int ret = 0; +	const char *name; + +	ret = of_property_read_string(dev->of_node, "label", &name); +	if (ret < 0) +		name = dev->of_node->name; + +	return sysfs_emit(buf, "%s\n", name); +} +static DEVICE_ATTR_RO(rpmsg_name); + +static struct attribute *qcom_glink_attrs[] = { +	&dev_attr_rpmsg_name.attr, +	NULL +}; +ATTRIBUTE_GROUPS(qcom_glink); + +static void qcom_glink_device_release(struct device *dev) +{ +	struct rpmsg_device *rpdev = to_rpmsg_device(dev); +	struct glink_channel *channel = to_glink_channel(rpdev->ept); + +	/* Release qcom_glink_alloc_channel() reference */ +	kref_put(&channel->refcount, qcom_glink_channel_release); +	kfree(rpdev); +} + +static int qcom_glink_create_chrdev(struct qcom_glink *glink) +{ +	struct rpmsg_device *rpdev; +	struct glink_channel *channel; + +	rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); +	if (!rpdev) +		return -ENOMEM; + +	channel = qcom_glink_alloc_channel(glink, "rpmsg_chrdev"); +	if (IS_ERR(channel)) { +		kfree(rpdev); +		return PTR_ERR(channel); +	} +	channel->rpdev = rpdev; + +	rpdev->ept = &channel->ept; +	rpdev->ops = &glink_device_ops; +	rpdev->dev.parent = glink->dev; +	rpdev->dev.release = qcom_glink_device_release; + +	return rpmsg_ctrldev_register_device(rpdev); +} +  struct qcom_glink *qcom_glink_native_probe(struct device *dev,  					   unsigned long features,  					   struct qcom_glink_pipe *rx, @@ -1599,14 +1741,17 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,  	spin_lock_init(&glink->rx_lock);  	INIT_LIST_HEAD(&glink->rx_queue);  	INIT_WORK(&glink->rx_work, qcom_glink_work); +	init_waitqueue_head(&glink->tx_avail_notify);  	spin_lock_init(&glink->idr_lock);  	idr_init(&glink->lcids);  	idr_init(&glink->rcids); -	ret = of_property_read_string(dev->of_node, "label", &glink->name); -	if (ret < 0) -		glink->name = dev->of_node->name; +	glink->dev->groups = qcom_glink_groups; + +	ret = device_add_groups(dev, qcom_glink_groups); +	if (ret) +		dev_err(dev, "failed to add groups\n");  	glink->mbox_client.dev = dev;  	glink->mbox_client.knows_txdone = true; @@ -1633,6 +1778,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,  	if (ret)  		return ERR_PTR(ret); +	ret = qcom_glink_create_chrdev(glink); +	if (ret) +		dev_err(glink->dev, "failed to register chrdev\n"); +  	return glink;  }  EXPORT_SYMBOL_GPL(qcom_glink_native_probe); diff --git a/drivers/rpmsg/qcom_glink_ssr.c b/drivers/rpmsg/qcom_glink_ssr.c new file mode 100644 index 000000000000..776d64446879 --- /dev/null +++ b/drivers/rpmsg/qcom_glink_ssr.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, Linaro Ltd. + */ + +#include <linux/completion.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/rpmsg.h> +#include <linux/rpmsg/qcom_glink.h> +#include <linux/remoteproc/qcom_rproc.h> + +/** + * struct do_cleanup_msg - The data structure for an SSR do_cleanup message + * @version:	The G-Link SSR protocol version + * @command:	The G-Link SSR command - do_cleanup + * @seq_num:	Sequence number + * @name_len:	Length of the name of the subsystem being restarted + * @name:	G-Link edge name of the subsystem being restarted + */ +struct do_cleanup_msg { +	__le32 version; +	__le32 command; +	__le32 seq_num; +	__le32 name_len; +	char name[32]; +}; + +/** + * struct cleanup_done_msg - The data structure for an SSR cleanup_done message + * @version:	The G-Link SSR protocol version + * @response:	The G-Link SSR response to a do_cleanup command, cleanup_done + * @seq_num:	Sequence number + */ +struct cleanup_done_msg { +	__le32 version; +	__le32 response; +	__le32 seq_num; +}; + +/* + * G-Link SSR protocol commands + */ +#define GLINK_SSR_DO_CLEANUP	0 +#define GLINK_SSR_CLEANUP_DONE	1 + +struct glink_ssr { +	struct device *dev; +	struct rpmsg_endpoint *ept; + +	struct notifier_block nb; + +	u32 seq_num; +	struct completion completion; +}; + +/* Notifier list for all registered glink_ssr instances */ +static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); + +/** + * qcom_glink_ssr_notify() - notify GLINK SSR about stopped remoteproc + * @ssr_name:	name of the remoteproc that has been stopped + */ +void qcom_glink_ssr_notify(const char *ssr_name) +{ +	blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr_name); +} +EXPORT_SYMBOL_GPL(qcom_glink_ssr_notify); + +static int qcom_glink_ssr_callback(struct rpmsg_device *rpdev, +				   void *data, int len, void *priv, u32 addr) +{ +	struct cleanup_done_msg *msg = data; +	struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); + +	if (len < sizeof(*msg)) { +		dev_err(ssr->dev, "message too short\n"); +		return -EINVAL; +	} + +	if (le32_to_cpu(msg->version) != 0) +		return -EINVAL; + +	if (le32_to_cpu(msg->response) != GLINK_SSR_CLEANUP_DONE) +		return 0; + +	if (le32_to_cpu(msg->seq_num) != ssr->seq_num) { +		dev_err(ssr->dev, "invalid sequence number of response\n"); +		return -EINVAL; +	} + +	complete(&ssr->completion); + +	return 0; +} + +static int qcom_glink_ssr_notifier_call(struct notifier_block *nb, +					unsigned long event, +					void *data) +{ +	struct glink_ssr *ssr = container_of(nb, struct glink_ssr, nb); +	struct do_cleanup_msg msg; +	char *ssr_name = data; +	int ret; + +	ssr->seq_num++; +	reinit_completion(&ssr->completion); + +	memset(&msg, 0, sizeof(msg)); +	msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP); +	msg.seq_num = cpu_to_le32(ssr->seq_num); +	msg.name_len = cpu_to_le32(strlen(ssr_name)); +	strlcpy(msg.name, ssr_name, sizeof(msg.name)); + +	ret = rpmsg_send(ssr->ept, &msg, sizeof(msg)); +	if (ret < 0) +		dev_err(ssr->dev, "failed to send cleanup message\n"); + +	ret = wait_for_completion_timeout(&ssr->completion, HZ); +	if (!ret) +		dev_err(ssr->dev, "timeout waiting for cleanup done message\n"); + +	return NOTIFY_DONE; +} + +static int qcom_glink_ssr_probe(struct rpmsg_device *rpdev) +{ +	struct glink_ssr *ssr; + +	ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL); +	if (!ssr) +		return -ENOMEM; + +	init_completion(&ssr->completion); + +	ssr->dev = &rpdev->dev; +	ssr->ept = rpdev->ept; +	ssr->nb.notifier_call = qcom_glink_ssr_notifier_call; + +	dev_set_drvdata(&rpdev->dev, ssr); + +	return blocking_notifier_chain_register(&ssr_notifiers, &ssr->nb); +} + +static void qcom_glink_ssr_remove(struct rpmsg_device *rpdev) +{ +	struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); + +	blocking_notifier_chain_unregister(&ssr_notifiers, &ssr->nb); +} + +static const struct rpmsg_device_id qcom_glink_ssr_match[] = { +	{ "glink_ssr" }, +	{} +}; + +static struct rpmsg_driver qcom_glink_ssr_driver = { +	.probe = qcom_glink_ssr_probe, +	.remove = qcom_glink_ssr_remove, +	.callback = qcom_glink_ssr_callback, +	.id_table = qcom_glink_ssr_match, +	.drv = { +		.name = "qcom_glink_ssr", +	}, +}; +module_rpmsg_driver(qcom_glink_ssr_driver); diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c index 4abbeea782fa..1044cf03c542 100644 --- a/drivers/rpmsg/qcom_smd.c +++ b/drivers/rpmsg/qcom_smd.c @@ -729,11 +729,11 @@ static int qcom_smd_write_fifo(struct qcom_smd_channel *channel,  }  /** - * qcom_smd_send - write data to smd channel + * __qcom_smd_send - write data to smd channel   * @channel:	channel handle   * @data:	buffer of data to write   * @len:	number of bytes to write - * @wait:	flag to indicate if write has ca wait + * @wait:	flag to indicate if write can wait   *   * This is a blocking write of len bytes into the channel's tx ring buffer and   * signal the remote end. It will sleep until there is enough space available @@ -974,6 +974,20 @@ static int qcom_smd_trysend(struct rpmsg_endpoint *ept, void *data, int len)  	return __qcom_smd_send(qsept->qsch, data, len, false);  } +static int qcom_smd_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +{ +	struct qcom_smd_endpoint *qsept = to_smd_endpoint(ept); + +	return __qcom_smd_send(qsept->qsch, data, len, true); +} + +static int qcom_smd_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +{ +	struct qcom_smd_endpoint *qsept = to_smd_endpoint(ept); + +	return __qcom_smd_send(qsept->qsch, data, len, false); +} +  static __poll_t qcom_smd_poll(struct rpmsg_endpoint *ept,  				  struct file *filp, poll_table *wait)  { @@ -1038,7 +1052,9 @@ static const struct rpmsg_device_ops qcom_smd_device_ops = {  static const struct rpmsg_endpoint_ops qcom_smd_endpoint_ops = {  	.destroy_ept = qcom_smd_destroy_ept,  	.send = qcom_smd_send, +	.sendto = qcom_smd_sendto,  	.trysend = qcom_smd_trysend, +	.trysendto = qcom_smd_trysendto,  	.poll = qcom_smd_poll,  }; @@ -1073,7 +1089,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)  	/* Assign public information to the rpmsg_device */  	rpdev = &qsdev->rpdev; -	strncpy(rpdev->id.name, channel->name, RPMSG_NAME_SIZE); +	strscpy_pad(rpdev->id.name, channel->name, RPMSG_NAME_SIZE);  	rpdev->src = RPMSG_ADDR_ANY;  	rpdev->dst = RPMSG_ADDR_ANY; @@ -1097,7 +1113,7 @@ static int qcom_smd_create_chrdev(struct qcom_smd_edge *edge)  	qsdev->rpdev.dev.parent = &edge->dev;  	qsdev->rpdev.dev.release = qcom_smd_release_device; -	return rpmsg_chrdev_register_device(&qsdev->rpdev); +	return rpmsg_ctrldev_register_device(&qsdev->rpdev);  }  /* @@ -1272,9 +1288,14 @@ static void qcom_channel_state_worker(struct work_struct *work)  		if (channel->state != SMD_CHANNEL_CLOSED)  			continue; +		/* +		 * Always open rpm_requests, even when already opened which is +		 * required on some SoCs like msm8953. +		 */  		remote_state = GET_RX_CHANNEL_INFO(channel, state);  		if (remote_state != SMD_CHANNEL_OPENING && -		    remote_state != SMD_CHANNEL_OPENED) +		    remote_state != SMD_CHANNEL_OPENED && +		    strcmp(channel->name, "rpm_requests"))  			continue;  		if (channel->registered) @@ -1282,9 +1303,7 @@ static void qcom_channel_state_worker(struct work_struct *work)  		spin_unlock_irqrestore(&edge->channels_lock, flags);  		qcom_smd_create_device(channel); -		channel->registered = true;  		spin_lock_irqsave(&edge->channels_lock, flags); -  		channel->registered = true;  	} @@ -1304,7 +1323,7 @@ static void qcom_channel_state_worker(struct work_struct *work)  		spin_unlock_irqrestore(&edge->channels_lock, flags); -		strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); +		strscpy_pad(chinfo.name, channel->name, sizeof(chinfo.name));  		chinfo.src = RPMSG_ADDR_ANY;  		chinfo.dst = RPMSG_ADDR_ANY;  		rpmsg_unregister_device(&edge->dev, &chinfo); @@ -1338,7 +1357,7 @@ static int qcom_smd_parse_edge(struct device *dev,  	ret = of_property_read_u32(node, key, &edge->edge_id);  	if (ret) {  		dev_err(dev, "edge missing %s property\n", key); -		return -EINVAL; +		goto put_node;  	}  	edge->remote_pid = QCOM_SMEM_HOST_ANY; @@ -1349,32 +1368,38 @@ static int qcom_smd_parse_edge(struct device *dev,  	edge->mbox_client.knows_txdone = true;  	edge->mbox_chan = mbox_request_channel(&edge->mbox_client, 0);  	if (IS_ERR(edge->mbox_chan)) { -		if (PTR_ERR(edge->mbox_chan) != -ENODEV) -			return PTR_ERR(edge->mbox_chan); +		if (PTR_ERR(edge->mbox_chan) != -ENODEV) { +			ret = PTR_ERR(edge->mbox_chan); +			goto put_node; +		}  		edge->mbox_chan = NULL;  		syscon_np = of_parse_phandle(node, "qcom,ipc", 0);  		if (!syscon_np) {  			dev_err(dev, "no qcom,ipc node\n"); -			return -ENODEV; +			ret = -ENODEV; +			goto put_node;  		}  		edge->ipc_regmap = syscon_node_to_regmap(syscon_np); -		if (IS_ERR(edge->ipc_regmap)) -			return PTR_ERR(edge->ipc_regmap); +		of_node_put(syscon_np); +		if (IS_ERR(edge->ipc_regmap)) { +			ret = PTR_ERR(edge->ipc_regmap); +			goto put_node; +		}  		key = "qcom,ipc";  		ret = of_property_read_u32_index(node, key, 1, &edge->ipc_offset);  		if (ret < 0) {  			dev_err(dev, "no offset in %s\n", key); -			return -EINVAL; +			goto put_node;  		}  		ret = of_property_read_u32_index(node, key, 2, &edge->ipc_bit);  		if (ret < 0) {  			dev_err(dev, "no bit in %s\n", key); -			return -EINVAL; +			goto put_node;  		}  	} @@ -1383,9 +1408,10 @@ static int qcom_smd_parse_edge(struct device *dev,  		edge->name = node->name;  	irq = irq_of_parse_and_map(node, 0); -	if (irq < 0) { +	if (!irq) {  		dev_err(dev, "required smd interrupt missing\n"); -		return -EINVAL; +		ret = -EINVAL; +		goto put_node;  	}  	ret = devm_request_irq(dev, irq, @@ -1393,12 +1419,18 @@ static int qcom_smd_parse_edge(struct device *dev,  			       node->name, edge);  	if (ret) {  		dev_err(dev, "failed to request smd irq\n"); -		return ret; +		goto put_node;  	}  	edge->irq = irq;  	return 0; + +put_node: +	of_node_put(node); +	edge->of_node = NULL; + +	return ret;  }  /* @@ -1439,7 +1471,7 @@ ATTRIBUTE_GROUPS(qcom_smd_edge);   * @parent:    parent device for the edge   * @node:      device_node describing the edge   * - * Returns an edge reference, or negative ERR_PTR() on failure. + * Return: an edge reference, or negative ERR_PTR() on failure.   */  struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,  					     struct device_node *node) @@ -1577,7 +1609,7 @@ static int __init qcom_smd_init(void)  {  	return platform_driver_register(&qcom_smd_driver);  } -subsys_initcall(qcom_smd_init); +arch_initcall(qcom_smd_init);  static void __exit qcom_smd_exit(void)  { diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 4bbbacdbf3bb..3e0b8f3496ed 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -1,5 +1,6 @@  // SPDX-License-Identifier: GPL-2.0  /* + * Copyright (C) 2022, STMicroelectronics   * Copyright (c) 2016, Linaro Ltd.   * Copyright (c) 2012, Michal Simek <monstr@monstr.eu>   * Copyright (c) 2012, PetaLogix @@ -9,6 +10,9 @@   * Based on rpmsg performance statistics driver by Michal Simek, which in turn   * was based on TI & Google OMX rpmsg driver.   */ + +#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt +  #include <linux/cdev.h>  #include <linux/device.h>  #include <linux/fs.h> @@ -22,35 +26,19 @@  #include <linux/uaccess.h>  #include <uapi/linux/rpmsg.h> +#include "rpmsg_char.h"  #include "rpmsg_internal.h"  #define RPMSG_DEV_MAX	(MINORMASK + 1)  static dev_t rpmsg_major; -static struct class *rpmsg_class; -static DEFINE_IDA(rpmsg_ctrl_ida);  static DEFINE_IDA(rpmsg_ept_ida);  static DEFINE_IDA(rpmsg_minor_ida);  #define dev_to_eptdev(dev) container_of(dev, struct rpmsg_eptdev, dev)  #define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_eptdev, cdev) -#define dev_to_ctrldev(dev) container_of(dev, struct rpmsg_ctrldev, dev) -#define cdev_to_ctrldev(i_cdev) container_of(i_cdev, struct rpmsg_ctrldev, cdev) - -/** - * struct rpmsg_ctrldev - control device for instantiating endpoint devices - * @rpdev:	underlaying rpmsg device - * @cdev:	cdev for the ctrl device - * @dev:	device for the ctrl device - */ -struct rpmsg_ctrldev { -	struct rpmsg_device *rpdev; -	struct cdev cdev; -	struct device dev; -}; -  /**   * struct rpmsg_eptdev - endpoint device context   * @dev:	endpoint device @@ -62,6 +50,8 @@ struct rpmsg_ctrldev {   * @queue_lock:	synchronization of @queue operations   * @queue:	incoming message queue   * @readq:	wait object for incoming queue + * @default_ept: set to channel default endpoint if the default endpoint should be re-used + *              on device open to prevent endpoint address update.   */  struct rpmsg_eptdev {  	struct device dev; @@ -72,19 +62,23 @@ struct rpmsg_eptdev {  	struct mutex ept_lock;  	struct rpmsg_endpoint *ept; +	struct rpmsg_endpoint *default_ept;  	spinlock_t queue_lock;  	struct sk_buff_head queue;  	wait_queue_head_t readq; +  }; -static int rpmsg_eptdev_destroy(struct device *dev, void *data) +int rpmsg_chrdev_eptdev_destroy(struct device *dev, void *data)  {  	struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev);  	mutex_lock(&eptdev->ept_lock);  	if (eptdev->ept) { -		rpmsg_destroy_ept(eptdev->ept); +		/* The default endpoint is released by the rpmsg core */ +		if (!eptdev->default_ept) +			rpmsg_destroy_ept(eptdev->ept);  		eptdev->ept = NULL;  	}  	mutex_unlock(&eptdev->ept_lock); @@ -92,11 +86,12 @@ static int rpmsg_eptdev_destroy(struct device *dev, void *data)  	/* wake up any blocked readers */  	wake_up_interruptible(&eptdev->readq); -	device_del(&eptdev->dev); +	cdev_device_del(&eptdev->cdev, &eptdev->dev);  	put_device(&eptdev->dev);  	return 0;  } +EXPORT_SYMBOL(rpmsg_chrdev_eptdev_destroy);  static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len,  			void *priv, u32 addr) @@ -127,17 +122,33 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp)  	struct rpmsg_device *rpdev = eptdev->rpdev;  	struct device *dev = &eptdev->dev; +	mutex_lock(&eptdev->ept_lock); +	if (eptdev->ept) { +		mutex_unlock(&eptdev->ept_lock); +		return -EBUSY; +	} +  	get_device(dev); -	ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo); +	/* +	 * If the default_ept is set, the rpmsg device default endpoint is used. +	 * Else a new endpoint is created on open that will be destroyed on release. +	 */ +	if (eptdev->default_ept) +		ept = eptdev->default_ept; +	else +		ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo); +  	if (!ept) {  		dev_err(dev, "failed to open %s\n", eptdev->chinfo.name);  		put_device(dev); +		mutex_unlock(&eptdev->ept_lock);  		return -EINVAL;  	}  	eptdev->ept = ept;  	filp->private_data = eptdev; +	mutex_unlock(&eptdev->ept_lock);  	return 0;  } @@ -150,7 +161,8 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp)  	/* Close the endpoint, if it's not already destroyed by the parent */  	mutex_lock(&eptdev->ept_lock);  	if (eptdev->ept) { -		rpmsg_destroy_ept(eptdev->ept); +		if (!eptdev->default_ept) +			rpmsg_destroy_ept(eptdev->ept);  		eptdev->ept = NULL;  	}  	mutex_unlock(&eptdev->ept_lock); @@ -238,10 +250,13 @@ static ssize_t rpmsg_eptdev_write_iter(struct kiocb *iocb,  		goto unlock_eptdev;  	} -	if (filp->f_flags & O_NONBLOCK) -		ret = rpmsg_trysend(eptdev->ept, kbuf, len); -	else -		ret = rpmsg_send(eptdev->ept, kbuf, len); +	if (filp->f_flags & O_NONBLOCK) { +		ret = rpmsg_trysendto(eptdev->ept, kbuf, len, eptdev->chinfo.dst); +		if (ret == -ENOMEM) +			ret = -EAGAIN; +	} else { +		ret = rpmsg_sendto(eptdev->ept, kbuf, len, eptdev->chinfo.dst); +	}  unlock_eptdev:  	mutex_unlock(&eptdev->ept_lock); @@ -277,7 +292,11 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd,  	if (cmd != RPMSG_DESTROY_EPT_IOCTL)  		return -EINVAL; -	return rpmsg_eptdev_destroy(&eptdev->dev, NULL); +	/* Don't allow to destroy a default endpoint. */ +	if (eptdev->default_ept) +		return -EINVAL; + +	return rpmsg_chrdev_eptdev_destroy(&eptdev->dev, NULL);  }  static const struct file_operations rpmsg_eptdev_fops = { @@ -332,25 +351,21 @@ static void rpmsg_eptdev_release_device(struct device *dev)  	ida_simple_remove(&rpmsg_ept_ida, dev->id);  	ida_simple_remove(&rpmsg_minor_ida, MINOR(eptdev->dev.devt)); -	cdev_del(&eptdev->cdev);  	kfree(eptdev);  } -static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev, -			       struct rpmsg_channel_info chinfo) +static struct rpmsg_eptdev *rpmsg_chrdev_eptdev_alloc(struct rpmsg_device *rpdev, +						      struct device *parent)  { -	struct rpmsg_device *rpdev = ctrldev->rpdev;  	struct rpmsg_eptdev *eptdev;  	struct device *dev; -	int ret;  	eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL);  	if (!eptdev) -		return -ENOMEM; +		return ERR_PTR(-ENOMEM);  	dev = &eptdev->dev;  	eptdev->rpdev = rpdev; -	eptdev->chinfo = chinfo;  	mutex_init(&eptdev->ept_lock);  	spin_lock_init(&eptdev->queue_lock); @@ -359,13 +374,23 @@ static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev,  	device_initialize(dev);  	dev->class = rpmsg_class; -	dev->parent = &ctrldev->dev; +	dev->parent = parent;  	dev->groups = rpmsg_eptdev_groups;  	dev_set_drvdata(dev, eptdev);  	cdev_init(&eptdev->cdev, &rpmsg_eptdev_fops);  	eptdev->cdev.owner = THIS_MODULE; +	return eptdev; +} + +static int rpmsg_chrdev_eptdev_add(struct rpmsg_eptdev *eptdev, struct rpmsg_channel_info chinfo) +{ +	struct device *dev = &eptdev->dev; +	int ret; + +	eptdev->chinfo = chinfo; +  	ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);  	if (ret < 0)  		goto free_eptdev; @@ -377,19 +402,13 @@ static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev,  	dev->id = ret;  	dev_set_name(dev, "rpmsg%d", ret); -	ret = cdev_add(&eptdev->cdev, dev->devt, 1); +	ret = cdev_device_add(&eptdev->cdev, &eptdev->dev);  	if (ret)  		goto free_ept_ida;  	/* We can now rely on the release function for cleanup */  	dev->release = rpmsg_eptdev_release_device; -	ret = device_add(dev); -	if (ret) { -		dev_err(dev, "device_add failed: %d\n", ret); -		put_device(dev); -	} -  	return ret;  free_ept_ida: @@ -403,178 +422,95 @@ free_eptdev:  	return ret;  } -static int rpmsg_ctrldev_open(struct inode *inode, struct file *filp) -{ -	struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); - -	get_device(&ctrldev->dev); -	filp->private_data = ctrldev; - -	return 0; -} - -static int rpmsg_ctrldev_release(struct inode *inode, struct file *filp) -{ -	struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); - -	put_device(&ctrldev->dev); - -	return 0; -} - -static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd, -				unsigned long arg) +int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent, +			       struct rpmsg_channel_info chinfo)  { -	struct rpmsg_ctrldev *ctrldev = fp->private_data; -	void __user *argp = (void __user *)arg; -	struct rpmsg_endpoint_info eptinfo; -	struct rpmsg_channel_info chinfo; - -	if (cmd != RPMSG_CREATE_EPT_IOCTL) -		return -EINVAL; - -	if (copy_from_user(&eptinfo, argp, sizeof(eptinfo))) -		return -EFAULT; - -	memcpy(chinfo.name, eptinfo.name, RPMSG_NAME_SIZE); -	chinfo.name[RPMSG_NAME_SIZE-1] = '\0'; -	chinfo.src = eptinfo.src; -	chinfo.dst = eptinfo.dst; - -	return rpmsg_eptdev_create(ctrldev, chinfo); -}; - -static const struct file_operations rpmsg_ctrldev_fops = { -	.owner = THIS_MODULE, -	.open = rpmsg_ctrldev_open, -	.release = rpmsg_ctrldev_release, -	.unlocked_ioctl = rpmsg_ctrldev_ioctl, -	.compat_ioctl = compat_ptr_ioctl, -}; +	struct rpmsg_eptdev *eptdev; -static void rpmsg_ctrldev_release_device(struct device *dev) -{ -	struct rpmsg_ctrldev *ctrldev = dev_to_ctrldev(dev); +	eptdev = rpmsg_chrdev_eptdev_alloc(rpdev, parent); +	if (IS_ERR(eptdev)) +		return PTR_ERR(eptdev); -	ida_simple_remove(&rpmsg_ctrl_ida, dev->id); -	ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); -	cdev_del(&ctrldev->cdev); -	kfree(ctrldev); +	return rpmsg_chrdev_eptdev_add(eptdev, chinfo);  } +EXPORT_SYMBOL(rpmsg_chrdev_eptdev_create);  static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)  { -	struct rpmsg_ctrldev *ctrldev; -	struct device *dev; -	int ret; - -	ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL); -	if (!ctrldev) -		return -ENOMEM; - -	ctrldev->rpdev = rpdev; - -	dev = &ctrldev->dev; -	device_initialize(dev); -	dev->parent = &rpdev->dev; -	dev->class = rpmsg_class; - -	cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops); -	ctrldev->cdev.owner = THIS_MODULE; - -	ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL); -	if (ret < 0) -		goto free_ctrldev; -	dev->devt = MKDEV(MAJOR(rpmsg_major), ret); - -	ret = ida_simple_get(&rpmsg_ctrl_ida, 0, 0, GFP_KERNEL); -	if (ret < 0) -		goto free_minor_ida; -	dev->id = ret; -	dev_set_name(&ctrldev->dev, "rpmsg_ctrl%d", ret); - -	ret = cdev_add(&ctrldev->cdev, dev->devt, 1); -	if (ret) -		goto free_ctrl_ida; - -	/* We can now rely on the release function for cleanup */ -	dev->release = rpmsg_ctrldev_release_device; +	struct rpmsg_channel_info chinfo; +	struct rpmsg_eptdev *eptdev; +	struct device *dev = &rpdev->dev; -	ret = device_add(dev); -	if (ret) { -		dev_err(&rpdev->dev, "device_add failed: %d\n", ret); -		put_device(dev); -	} +	memcpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); +	chinfo.src = rpdev->src; +	chinfo.dst = rpdev->dst; -	dev_set_drvdata(&rpdev->dev, ctrldev); +	eptdev = rpmsg_chrdev_eptdev_alloc(rpdev, dev); +	if (IS_ERR(eptdev)) +		return PTR_ERR(eptdev); -	return ret; +	/* Set the default_ept to the rpmsg device endpoint */ +	eptdev->default_ept = rpdev->ept; -free_ctrl_ida: -	ida_simple_remove(&rpmsg_ctrl_ida, dev->id); -free_minor_ida: -	ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); -free_ctrldev: -	put_device(dev); -	kfree(ctrldev); +	/* +	 * The rpmsg_ept_cb uses *priv parameter to get its rpmsg_eptdev context. +	 * Storedit in default_ept *priv field. +	 */ +	eptdev->default_ept->priv = eptdev; -	return ret; +	return rpmsg_chrdev_eptdev_add(eptdev, chinfo);  }  static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev)  { -	struct rpmsg_ctrldev *ctrldev = dev_get_drvdata(&rpdev->dev);  	int ret; -	/* Destroy all endpoints */ -	ret = device_for_each_child(&ctrldev->dev, NULL, rpmsg_eptdev_destroy); +	ret = device_for_each_child(&rpdev->dev, NULL, rpmsg_chrdev_eptdev_destroy);  	if (ret) -		dev_warn(&rpdev->dev, "failed to nuke endpoints: %d\n", ret); - -	device_del(&ctrldev->dev); -	put_device(&ctrldev->dev); +		dev_warn(&rpdev->dev, "failed to destroy endpoints: %d\n", ret);  } +static struct rpmsg_device_id rpmsg_chrdev_id_table[] = { +	{ .name	= "rpmsg-raw" }, +	{ }, +}; +  static struct rpmsg_driver rpmsg_chrdev_driver = {  	.probe = rpmsg_chrdev_probe,  	.remove = rpmsg_chrdev_remove, -	.drv = { -		.name = "rpmsg_chrdev", -	}, +	.callback = rpmsg_ept_cb, +	.id_table = rpmsg_chrdev_id_table, +	.drv.name = "rpmsg_chrdev",  }; -static int rpmsg_char_init(void) +static int rpmsg_chrdev_init(void)  {  	int ret; -	ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg"); +	ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg_char");  	if (ret < 0) { -		pr_err("rpmsg: failed to allocate char dev region\n"); +		pr_err("failed to allocate char dev region\n");  		return ret;  	} -	rpmsg_class = class_create(THIS_MODULE, "rpmsg"); -	if (IS_ERR(rpmsg_class)) { -		pr_err("failed to create rpmsg class\n"); -		unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); -		return PTR_ERR(rpmsg_class); -	} -  	ret = register_rpmsg_driver(&rpmsg_chrdev_driver);  	if (ret < 0) { -		pr_err("rpmsgchr: failed to register rpmsg driver\n"); -		class_destroy(rpmsg_class); -		unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); +		pr_err("rpmsg: failed to register rpmsg raw driver\n"); +		goto free_region;  	} +	return 0; + +free_region: +	unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); +  	return ret;  } -postcore_initcall(rpmsg_char_init); +postcore_initcall(rpmsg_chrdev_init);  static void rpmsg_chrdev_exit(void)  {  	unregister_rpmsg_driver(&rpmsg_chrdev_driver); -	class_destroy(rpmsg_class);  	unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);  }  module_exit(rpmsg_chrdev_exit); diff --git a/drivers/rpmsg/rpmsg_char.h b/drivers/rpmsg/rpmsg_char.h new file mode 100644 index 000000000000..117d9cbc52f0 --- /dev/null +++ b/drivers/rpmsg/rpmsg_char.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022, STMicroelectronics + */ + +#ifndef __RPMSG_CHRDEV_H__ +#define __RPMSG_CHRDEV_H__ + +#if IS_ENABLED(CONFIG_RPMSG_CHAR) +/** + * rpmsg_chrdev_eptdev_create() - register char device based on an endpoint + * @rpdev:  prepared rpdev to be used for creating endpoints + * @parent: parent device + * @chinfo: associated endpoint channel information. + * + * This function create a new rpmsg char endpoint device to instantiate a new + * endpoint based on chinfo information. + */ +int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent, +			       struct rpmsg_channel_info chinfo); + +/** + * rpmsg_chrdev_eptdev_destroy() - destroy created char device endpoint. + * @data: private data associated to the endpoint device + * + * This function destroys a rpmsg char endpoint device created by the RPMSG_DESTROY_EPT_IOCTL + * control. + */ +int rpmsg_chrdev_eptdev_destroy(struct device *dev, void *data); + +#else  /*IS_ENABLED(CONFIG_RPMSG_CHAR) */ + +static inline int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent, +					     struct rpmsg_channel_info chinfo) +{ +	return -ENXIO; +} + +static inline int rpmsg_chrdev_eptdev_destroy(struct device *dev, void *data) +{ +	return -ENXIO; +} + +#endif /*IS_ENABLED(CONFIG_RPMSG_CHAR) */ + +#endif /*__RPMSG_CHRDEV_H__ */ diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index e330ec4dfc33..d6dde00efdae 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -20,6 +20,53 @@  #include "rpmsg_internal.h" +struct class *rpmsg_class; +EXPORT_SYMBOL(rpmsg_class); + +/** + * rpmsg_create_channel() - create a new rpmsg channel + * using its name and address info. + * @rpdev: rpmsg device + * @chinfo: channel_info to bind + * + * Return: 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 + * + * Return: 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 @@ -58,7 +105,7 @@   * 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. + * Return: a pointer to the endpoint on success, or NULL on error.   */  struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,  					rpmsg_rx_cb_t cb, void *priv, @@ -81,7 +128,7 @@ EXPORT_SYMBOL(rpmsg_create_ept);   */  void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)  { -	if (ept) +	if (ept && ept->ops)  		ept->ops->destroy_ept(ept);  }  EXPORT_SYMBOL(rpmsg_destroy_ept); @@ -102,7 +149,7 @@ EXPORT_SYMBOL(rpmsg_destroy_ept);   *   * Can only be called from process context (for now).   * - * 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(struct rpmsg_endpoint *ept, void *data, int len)  { @@ -131,7 +178,7 @@ EXPORT_SYMBOL(rpmsg_send);   *   * Can only be called from process context (for now).   * - * Returns 0 on success and an appropriate error value on failure. + * Return: 0 on success and an appropriate error value on failure.   */  int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)  { @@ -162,7 +209,7 @@ EXPORT_SYMBOL(rpmsg_sendto);   *   * Can only be called from process context (for now).   * - * 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(struct rpmsg_endpoint *ept, u32 src, u32 dst,  			  void *data, int len) @@ -191,7 +238,7 @@ EXPORT_SYMBOL(rpmsg_send_offchannel);   *   * Can only be called from process context (for now).   * - * Returns 0 on success and an appropriate error value on failure. + * Return: 0 on success and an appropriate error value on failure.   */  int rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len)  { @@ -219,7 +266,7 @@ EXPORT_SYMBOL(rpmsg_trysend);   *   * Can only be called from process context (for now).   * - * Returns 0 on success and an appropriate error value on failure. + * Return: 0 on success and an appropriate error value on failure.   */  int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)  { @@ -238,7 +285,7 @@ EXPORT_SYMBOL(rpmsg_trysendto);   * @filp:	file for poll_wait()   * @wait:	poll_table for poll_wait()   * - * Returns mask representing the current state of the endpoint's send buffers + * Return: mask representing the current state of the endpoint's send buffers   */  __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,  			poll_table *wait) @@ -269,7 +316,7 @@ EXPORT_SYMBOL(rpmsg_poll);   *   * Can only be called from process context (for now).   * - * Returns 0 on success and an appropriate error value on failure. + * Return: 0 on success and an appropriate error value on failure.   */  int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,  			     void *data, int len) @@ -283,8 +330,29 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,  }  EXPORT_SYMBOL(rpmsg_trysend_offchannel); +/** + * rpmsg_get_mtu() - get maximum transmission buffer size for sending message. + * @ept: the rpmsg endpoint + * + * This function returns maximum buffer size available for a single outgoing message. + * + * Return: the maximum transmission size on success and an appropriate error + * value on failure. + */ + +ssize_t rpmsg_get_mtu(struct rpmsg_endpoint *ept) +{ +	if (WARN_ON(!ept)) +		return -EINVAL; +	if (!ept->ops->get_mtu) +		return -ENOTSUPP; + +	return ept->ops->get_mtu(ept); +} +EXPORT_SYMBOL(rpmsg_get_mtu); +  /* - * match an rpmsg channel with a channel info struct. + * match a rpmsg channel with a channel info struct.   * this is used to make sure we're not creating rpmsg devices for channels   * that already exist.   */ @@ -332,7 +400,8 @@ field##_store(struct device *dev, struct device_attribute *attr,	\  	      const char *buf, size_t sz)				\  {									\  	struct rpmsg_device *rpdev = to_rpmsg_device(dev);		\ -	char *new, *old;						\ +	const char *old;						\ +	char *new;							\  									\  	new = kstrndup(buf, sz, GFP_KERNEL);				\  	if (!new)							\ @@ -415,8 +484,10 @@ static int rpmsg_dev_match(struct device *dev, struct device_driver *drv)  	if (ids)  		for (i = 0; ids[i].name[0]; i++) -			if (rpmsg_id_match(rpdev, &ids[i])) +			if (rpmsg_id_match(rpdev, &ids[i])) { +				rpdev->id.driver_data = ids[i].driver_data;  				return 1; +			}  	return of_driver_match_device(dev, drv);  } @@ -473,25 +544,36 @@ static int rpmsg_dev_probe(struct device *dev)  	err = rpdrv->probe(rpdev);  	if (err) {  		dev_err(dev, "%s: failed: %d\n", __func__, err); -		if (ept) -			rpmsg_destroy_ept(ept); -		goto out; +		goto destroy_ept;  	} -	if (ept && rpdev->ops->announce_create) +	if (ept && rpdev->ops->announce_create) {  		err = rpdev->ops->announce_create(rpdev); +		if (err) { +			dev_err(dev, "failed to announce creation\n"); +			goto remove_rpdev; +		} +	} + +	return 0; + +remove_rpdev: +	if (rpdrv->remove) +		rpdrv->remove(rpdev); +destroy_ept: +	if (ept) +		rpmsg_destroy_ept(ept);  out:  	return err;  } -static int rpmsg_dev_remove(struct device *dev) +static void 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); +		rpdev->ops->announce_destroy(rpdev);  	if (rpdrv->remove)  		rpdrv->remove(rpdev); @@ -500,8 +582,6 @@ static int rpmsg_dev_remove(struct device *dev)  	if (rpdev->ept)  		rpmsg_destroy_ept(rpdev->ept); - -	return err;  }  static struct bus_type rpmsg_bus = { @@ -513,24 +593,52 @@ static struct bus_type rpmsg_bus = {  	.remove		= rpmsg_dev_remove,  }; -int rpmsg_register_device(struct rpmsg_device *rpdev) +/* + * A helper for registering rpmsg device with driver override and name. + * Drivers should not be using it, but instead rpmsg_register_device(). + */ +int rpmsg_register_device_override(struct rpmsg_device *rpdev, +				   const char *driver_override)  {  	struct device *dev = &rpdev->dev;  	int ret; -	dev_set_name(&rpdev->dev, "%s.%s.%d.%d", dev_name(dev->parent), +	if (driver_override) +		strscpy_pad(rpdev->id.name, driver_override, RPMSG_NAME_SIZE); + +	dev_set_name(dev, "%s.%s.%d.%d", dev_name(dev->parent),  		     rpdev->id.name, rpdev->src, rpdev->dst); -	rpdev->dev.bus = &rpmsg_bus; +	dev->bus = &rpmsg_bus; + +	device_initialize(dev); +	if (driver_override) { +		ret = driver_set_override(dev, &rpdev->driver_override, +					  driver_override, +					  strlen(driver_override)); +		if (ret) { +			dev_err(dev, "device_set_override failed: %d\n", ret); +			put_device(dev); +			return ret; +		} +	} -	ret = device_register(&rpdev->dev); +	ret = device_add(dev);  	if (ret) { -		dev_err(dev, "device_register failed: %d\n", ret); -		put_device(&rpdev->dev); +		dev_err(dev, "device_add failed: %d\n", ret); +		kfree(rpdev->driver_override); +		rpdev->driver_override = NULL; +		put_device(dev);  	}  	return ret;  } +EXPORT_SYMBOL(rpmsg_register_device_override); + +int rpmsg_register_device(struct rpmsg_device *rpdev) +{ +	return rpmsg_register_device_override(rpdev, NULL); +}  EXPORT_SYMBOL(rpmsg_register_device);  /* @@ -559,7 +667,7 @@ EXPORT_SYMBOL(rpmsg_unregister_device);   * @rpdrv: pointer to a struct rpmsg_driver   * @owner: owning module/driver   * - * Returns 0 on success, and an appropriate error value on failure. + * Return: 0 on success, and an appropriate error value on failure.   */  int __register_rpmsg_driver(struct rpmsg_driver *rpdrv, struct module *owner)  { @@ -573,7 +681,7 @@ 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. + * Return: 0 on success, and an appropriate error value on failure.   */  void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv)  { @@ -586,10 +694,17 @@ static int __init rpmsg_init(void)  {  	int ret; +	rpmsg_class = class_create(THIS_MODULE, "rpmsg"); +	if (IS_ERR(rpmsg_class)) { +		pr_err("failed to create rpmsg class\n"); +		return PTR_ERR(rpmsg_class); +	} +  	ret = bus_register(&rpmsg_bus); -	if (ret) +	if (ret) {  		pr_err("failed to register rpmsg bus: %d\n", ret); - +		class_destroy(rpmsg_class); +	}  	return ret;  }  postcore_initcall(rpmsg_init); @@ -597,6 +712,7 @@ postcore_initcall(rpmsg_init);  static void __exit rpmsg_fini(void)  {  	bus_unregister(&rpmsg_bus); +	class_destroy(rpmsg_class);  }  module_exit(rpmsg_fini); diff --git a/drivers/rpmsg/rpmsg_ctrl.c b/drivers/rpmsg/rpmsg_ctrl.c new file mode 100644 index 000000000000..107da70fdbaa --- /dev/null +++ b/drivers/rpmsg/rpmsg_ctrl.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022, STMicroelectronics + * Copyright (c) 2016, Linaro Ltd. + * Copyright (c) 2012, Michal Simek <monstr@monstr.eu> + * Copyright (c) 2012, PetaLogix + * Copyright (c) 2011, Texas Instruments, Inc. + * Copyright (c) 2011, Google, Inc. + * + * Based on rpmsg performance statistics driver by Michal Simek, which in turn + * was based on TI & Google OMX rpmsg driver. + */ + +#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt + +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/idr.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rpmsg.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <uapi/linux/rpmsg.h> + +#include "rpmsg_char.h" +#include "rpmsg_internal.h" + +#define RPMSG_DEV_MAX	(MINORMASK + 1) + +static dev_t rpmsg_major; + +static DEFINE_IDA(rpmsg_ctrl_ida); +static DEFINE_IDA(rpmsg_minor_ida); + +#define dev_to_ctrldev(dev) container_of(dev, struct rpmsg_ctrldev, dev) +#define cdev_to_ctrldev(i_cdev) container_of(i_cdev, struct rpmsg_ctrldev, cdev) + +/** + * struct rpmsg_ctrldev - control device for instantiating endpoint devices + * @rpdev:	underlaying rpmsg device + * @cdev:	cdev for the ctrl device + * @dev:	device for the ctrl device + * @ctrl_lock:	serialize the ioctrls. + */ +struct rpmsg_ctrldev { +	struct rpmsg_device *rpdev; +	struct cdev cdev; +	struct device dev; +	struct mutex ctrl_lock; +}; + +static int rpmsg_ctrldev_open(struct inode *inode, struct file *filp) +{ +	struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); + +	get_device(&ctrldev->dev); +	filp->private_data = ctrldev; + +	return 0; +} + +static int rpmsg_ctrldev_release(struct inode *inode, struct file *filp) +{ +	struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); + +	put_device(&ctrldev->dev); + +	return 0; +} + +static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd, +				unsigned long arg) +{ +	struct rpmsg_ctrldev *ctrldev = fp->private_data; +	void __user *argp = (void __user *)arg; +	struct rpmsg_endpoint_info eptinfo; +	struct rpmsg_channel_info chinfo; +	struct rpmsg_device *rpdev; +	int ret = 0; + +	if (copy_from_user(&eptinfo, argp, sizeof(eptinfo))) +		return -EFAULT; + +	memcpy(chinfo.name, eptinfo.name, RPMSG_NAME_SIZE); +	chinfo.name[RPMSG_NAME_SIZE - 1] = '\0'; +	chinfo.src = eptinfo.src; +	chinfo.dst = eptinfo.dst; + +	mutex_lock(&ctrldev->ctrl_lock); +	switch (cmd) { +	case RPMSG_CREATE_EPT_IOCTL: +		ret = rpmsg_chrdev_eptdev_create(ctrldev->rpdev, &ctrldev->dev, chinfo); +		break; + +	case RPMSG_CREATE_DEV_IOCTL: +		rpdev = rpmsg_create_channel(ctrldev->rpdev, &chinfo); +		if (!rpdev) { +			dev_err(&ctrldev->dev, "failed to create %s channel\n", chinfo.name); +			ret = -ENXIO; +		} +		break; + +	case RPMSG_RELEASE_DEV_IOCTL: +		ret = rpmsg_release_channel(ctrldev->rpdev, &chinfo); +		if (ret) +			dev_err(&ctrldev->dev, "failed to release %s channel (%d)\n", +				chinfo.name, ret); +		break; + +	default: +		ret = -EINVAL; +	} +	mutex_unlock(&ctrldev->ctrl_lock); + +	return ret; +}; + +static const struct file_operations rpmsg_ctrldev_fops = { +	.owner = THIS_MODULE, +	.open = rpmsg_ctrldev_open, +	.release = rpmsg_ctrldev_release, +	.unlocked_ioctl = rpmsg_ctrldev_ioctl, +	.compat_ioctl = compat_ptr_ioctl, +}; + +static void rpmsg_ctrldev_release_device(struct device *dev) +{ +	struct rpmsg_ctrldev *ctrldev = dev_to_ctrldev(dev); + +	ida_simple_remove(&rpmsg_ctrl_ida, dev->id); +	ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); +	kfree(ctrldev); +} + +static int rpmsg_ctrldev_probe(struct rpmsg_device *rpdev) +{ +	struct rpmsg_ctrldev *ctrldev; +	struct device *dev; +	int ret; + +	ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL); +	if (!ctrldev) +		return -ENOMEM; + +	ctrldev->rpdev = rpdev; + +	dev = &ctrldev->dev; +	device_initialize(dev); +	dev->parent = &rpdev->dev; +	dev->class = rpmsg_class; + +	mutex_init(&ctrldev->ctrl_lock); +	cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops); +	ctrldev->cdev.owner = THIS_MODULE; + +	ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL); +	if (ret < 0) +		goto free_ctrldev; +	dev->devt = MKDEV(MAJOR(rpmsg_major), ret); + +	ret = ida_simple_get(&rpmsg_ctrl_ida, 0, 0, GFP_KERNEL); +	if (ret < 0) +		goto free_minor_ida; +	dev->id = ret; +	dev_set_name(&ctrldev->dev, "rpmsg_ctrl%d", ret); + +	ret = cdev_device_add(&ctrldev->cdev, &ctrldev->dev); +	if (ret) +		goto free_ctrl_ida; + +	/* We can now rely on the release function for cleanup */ +	dev->release = rpmsg_ctrldev_release_device; + +	dev_set_drvdata(&rpdev->dev, ctrldev); + +	return ret; + +free_ctrl_ida: +	ida_simple_remove(&rpmsg_ctrl_ida, dev->id); +free_minor_ida: +	ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); +free_ctrldev: +	put_device(dev); +	kfree(ctrldev); + +	return ret; +} + +static void rpmsg_ctrldev_remove(struct rpmsg_device *rpdev) +{ +	struct rpmsg_ctrldev *ctrldev = dev_get_drvdata(&rpdev->dev); +	int ret; + +	/* Destroy all endpoints */ +	ret = device_for_each_child(&ctrldev->dev, NULL, rpmsg_chrdev_eptdev_destroy); +	if (ret) +		dev_warn(&rpdev->dev, "failed to nuke endpoints: %d\n", ret); + +	cdev_device_del(&ctrldev->cdev, &ctrldev->dev); +	put_device(&ctrldev->dev); +} + +static struct rpmsg_driver rpmsg_ctrldev_driver = { +	.probe = rpmsg_ctrldev_probe, +	.remove = rpmsg_ctrldev_remove, +	.drv = { +		.name = "rpmsg_ctrl", +	}, +}; + +static int rpmsg_ctrldev_init(void) +{ +	int ret; + +	ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg_ctrl"); +	if (ret < 0) { +		pr_err("failed to allocate char dev region\n"); +		return ret; +	} + +	ret = register_rpmsg_driver(&rpmsg_ctrldev_driver); +	if (ret < 0) { +		pr_err("failed to register rpmsg driver\n"); +		unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); +	} + +	return ret; +} +postcore_initcall(rpmsg_ctrldev_init); + +static void rpmsg_ctrldev_exit(void) +{ +	unregister_rpmsg_driver(&rpmsg_ctrldev_driver); +	unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); +} +module_exit(rpmsg_ctrldev_exit); + +MODULE_DESCRIPTION("rpmsg control interface"); +MODULE_ALIAS("rpmsg:" KBUILD_MODNAME); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index 3fc83cd50e98..39b646d0d40d 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -18,8 +18,12 @@  #define to_rpmsg_device(d) container_of(d, struct rpmsg_device, dev)  #define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) +extern struct class *rpmsg_class; +  /**   * 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,12 +33,16 @@   * 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); -	int (*announce_create)(struct rpmsg_device *ept); -	int (*announce_destroy)(struct rpmsg_device *ept); +	int (*announce_create)(struct rpmsg_device *rpdev); +	int (*announce_destroy)(struct rpmsg_device *rpdev);  };  /** @@ -47,6 +55,7 @@ struct rpmsg_device_ops {   * @trysendto:		see @rpmsg_trysendto(), optional   * @trysend_offchannel:	see @rpmsg_trysend_offchannel(), optional   * @poll:		see @rpmsg_poll(), optional + * @get_mtu:		see @rpmsg_get_mtu(), optional   *   * Indirection table for the operations that a rpmsg backend should implement.   * In addition to @destroy_ept, the backend must at least implement @send and @@ -66,28 +75,26 @@ struct rpmsg_endpoint_ops {  			     void *data, int len);  	__poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp,  			     poll_table *wait); +	ssize_t (*get_mtu)(struct rpmsg_endpoint *ept);  }; -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 + * rpmsg_ctrldev_register_device() - register a char device for control 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 chrdev.   */ -static inline int rpmsg_chrdev_register_device(struct rpmsg_device *rpdev) +static inline int rpmsg_ctrldev_register_device(struct rpmsg_device *rpdev)  { -	strcpy(rpdev->id.name, "rpmsg_chrdev"); -	rpdev->driver_override = "rpmsg_chrdev"; - -	return rpmsg_register_device(rpdev); +	return rpmsg_register_device_override(rpdev, "rpmsg_ctrl");  }  #endif diff --git a/drivers/rpmsg/rpmsg_ns.c b/drivers/rpmsg/rpmsg_ns.c new file mode 100644 index 000000000000..c70ad03ff2e9 --- /dev/null +++ b/drivers/rpmsg/rpmsg_ns.c @@ -0,0 +1,124 @@ +// 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) +{ +	rpdev->src = RPMSG_NS_ADDR; +	rpdev->dst = RPMSG_NS_ADDR; + +	return rpmsg_register_device_override(rpdev, "rpmsg_ns"); +} +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"); diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 376ebbf880d6..905ac7910c98 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -17,8 +17,9 @@  #include <linux/kernel.h>  #include <linux/module.h>  #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> @@ -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,9 +137,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, @@ -175,6 +148,9 @@ 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, @@ -184,6 +160,7 @@ static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {  	.trysend = virtio_rpmsg_trysend,  	.trysendto = virtio_rpmsg_trysendto,  	.trysend_offchannel = virtio_rpmsg_trysend_offchannel, +	.get_mtu = virtio_rpmsg_get_mtu,  };  /** @@ -279,6 +256,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, @@ -335,8 +330,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 = rpdev->ept->addr; -		nsm.flags = 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) @@ -359,8 +354,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 = rpdev->ept->addr; -		nsm.flags = 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) @@ -371,6 +366,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, @@ -389,8 +386,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 +416,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), @@ -549,7 +547,7 @@ static void rpmsg_downref_sleepers(struct virtproc_info *vrp)   * should use the appropriate rpmsg_{try}send{to, _offchannel} API   * (see include/linux/rpmsg.h).   * - * Returns 0 on success and an appropriate error value on failure. + * 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 +610,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); @@ -699,18 +697,31 @@ static int virtio_rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src,  	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); +		__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 +729,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 +750,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,60 +822,47 @@ 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) @@ -873,6 +871,8 @@ static int rpmsg_probe(struct virtio_device *vdev)  	static const char * const names[] = { "input", "output" };  	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; @@ -946,16 +946,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,6 +998,8 @@ 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, total_buf_space,  			  bufs_va, vrp->bufs_dma); @@ -1002,15 +1023,12 @@ 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);  | 
