All of lore.kernel.org
 help / color / mirror / Atom feed
From: Arnaud Pouliquen <arnaud.pouliquen@st.com>
To: Bjorn Andersson <bjorn.andersson@linaro.org>,
	Ohad Ben-Cohen <ohad@wizery.com>,
	Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: <linux-remoteproc@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	<linux-stm32@st-md-mailman.stormreply.com>,
	<arnaud.pouliquen@st.com>
Subject: [PATCH 01/13] rpmsg: introduce rpmsg raw driver
Date: Fri, 31 Jul 2020 14:10:31 +0200	[thread overview]
Message-ID: <20200731121043.24199-2-arnaud.pouliquen@st.com> (raw)
In-Reply-To: <20200731121043.24199-1-arnaud.pouliquen@st.com>

This driver is a copy past of the rpmsg_char driver with following
update:
- The control part has been removed.
- The rpmsg class has been removed because the information already
  available in /sys/bus.
- The device is now an RPMsg device probed by a RPMsg bus driver.
- The associated endpoint is now created by the bus not on the fs open.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/rpmsg/Kconfig     |   9 +
 drivers/rpmsg/Makefile    |   1 +
 drivers/rpmsg/rpmsg_raw.c | 347 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 357 insertions(+)
 create mode 100644 drivers/rpmsg/rpmsg_raw.c

diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index 0143c9864c45..900ec8f54081 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -23,6 +23,15 @@ config RPMSG_NS
 	  channel that probes the associate RPMsg device on remote endpoint
 	  service announcement.
 
+config RPMSG_RAW
+	tristate "RPMSG raw service driver"
+	depends on RPMSG
+	depends on NET
+	help
+	  Say Y here to export rpmsg endpoints as char device files, found
+	  in /dev. They make it possible for user-space programs to send and
+	  receive rpmsg packets through a basic char device.
+
 config RPMSG_MTK_SCP
 	tristate "MediaTek SCP"
 	depends on MTK_SCP
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
index 8d452656f0ee..d75f0a65e71d 100644
--- a/drivers/rpmsg/Makefile
+++ b/drivers/rpmsg/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.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
+obj-$(CONFIG_RPMSG_RAW)	+= rpmsg_raw.o
diff --git a/drivers/rpmsg/rpmsg_raw.c b/drivers/rpmsg/rpmsg_raw.c
new file mode 100644
index 000000000000..8684a78ab4d1
--- /dev/null
+++ b/drivers/rpmsg/rpmsg_raw.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2020 - All Rights Reserved
+ *
+ * Based on rpmsg_char driver.
+ */
+#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/poll.h>
+#include <linux/rpmsg.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/rpmsg.h>
+
+#include "rpmsg_internal.h"
+
+#define RPMSG_DEV_MAX	(MINORMASK + 1)
+
+#define dev_to_eptdev(dev) container_of(dev, struct rpmsg_raw, dev)
+#define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_raw, cdev)
+
+static dev_t rpmsg_raw_major;
+
+static DEFINE_IDA(rpmsg_minor_ida);
+static DEFINE_IDA(rpmsg_ept_ida);
+
+/**
+ * struct rpmsg_raw - endpoint device context
+ * @dev:	endpoint device
+ * @cdev:	cdev for the endpoint device
+ * @rpdev:	underlaying rpmsg device
+ * @ept_lock:	synchronization of @ept modifications
+ * @queue_lock:	synchronization of @queue operations
+ * @queue:	incoming message queue
+ * @readq:	wait object for incoming queue
+ */
+struct rpmsg_raw {
+	struct device dev;
+	struct cdev cdev;
+	struct rpmsg_device *rpdev;
+	struct mutex ept_lock;
+	spinlock_t queue_lock;
+	struct sk_buff_head queue;
+	wait_queue_head_t readq;
+};
+
+static int rpmsg_raw_cb(struct rpmsg_device *rpdev, void *buf, int len,
+			void *priv, u32 addr)
+{
+	struct rpmsg_raw *eptdev = dev_get_drvdata(&rpdev->dev);
+	struct sk_buff *skb;
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_put_data(skb, buf, len);
+
+	spin_lock(&eptdev->queue_lock);
+	skb_queue_tail(&eptdev->queue, skb);
+	spin_unlock(&eptdev->queue_lock);
+
+	/* wake up any blocking processes, waiting for new data */
+	wake_up_interruptible(&eptdev->readq);
+
+	return 0;
+}
+
+static int rpmsg_raw_open(struct inode *inode, struct file *filp)
+{
+	struct rpmsg_raw *eptdev = cdev_to_eptdev(inode->i_cdev);
+	struct device *dev = &eptdev->dev;
+
+	get_device(dev);
+
+	filp->private_data = eptdev;
+
+	return 0;
+}
+
+static int rpmsg_raw_release(struct inode *inode, struct file *filp)
+{
+	struct rpmsg_raw *eptdev = cdev_to_eptdev(inode->i_cdev);
+
+	/* Discard all SKBs */
+	skb_queue_purge(&eptdev->queue);
+
+	put_device(&eptdev->dev);
+
+	return 0;
+}
+
+static ssize_t rpmsg_raw_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+	struct file *filp = iocb->ki_filp;
+	struct rpmsg_raw *eptdev = filp->private_data;
+	unsigned long flags;
+	struct sk_buff *skb;
+	int use;
+
+	if (!eptdev->rpdev->ept)
+		return -EPIPE;
+
+	spin_lock_irqsave(&eptdev->queue_lock, flags);
+
+	/* Wait for data in the queue */
+	if (skb_queue_empty(&eptdev->queue)) {
+		spin_unlock_irqrestore(&eptdev->queue_lock, flags);
+
+		if (filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		/* Wait until we get data or the endpoint goes away */
+		if (wait_event_interruptible(eptdev->readq,
+					     !skb_queue_empty(&eptdev->queue) ||
+					     !eptdev->rpdev->ept))
+			return -ERESTARTSYS;
+
+		/* We lost the endpoint while waiting */
+		if (!eptdev->rpdev->ept)
+			return -EPIPE;
+
+		spin_lock_irqsave(&eptdev->queue_lock, flags);
+	}
+
+	skb = skb_dequeue(&eptdev->queue);
+	spin_unlock_irqrestore(&eptdev->queue_lock, flags);
+	if (!skb)
+		return -EFAULT;
+
+	use = min_t(size_t, iov_iter_count(to), skb->len);
+	if (copy_to_iter(skb->data, use, to) != use)
+		use = -EFAULT;
+
+	kfree_skb(skb);
+
+	return use;
+}
+
+static ssize_t rpmsg_raw_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+	struct file *filp = iocb->ki_filp;
+	struct rpmsg_raw *eptdev = filp->private_data;
+	size_t len = iov_iter_count(from);
+	void *kbuf;
+	int ret;
+
+	kbuf = kzalloc(len, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	if (!copy_from_iter_full(kbuf, len, from)) {
+		ret = -EFAULT;
+		goto free_kbuf;
+	}
+
+	if (mutex_lock_interruptible(&eptdev->ept_lock)) {
+		ret = -ERESTARTSYS;
+		goto free_kbuf;
+	}
+
+	if (!eptdev->rpdev->ept) {
+		ret = -EPIPE;
+		goto unlock_eptdev;
+	}
+
+	if (filp->f_flags & O_NONBLOCK)
+		ret = rpmsg_trysend(eptdev->rpdev->ept, kbuf, len);
+	else
+		ret = rpmsg_send(eptdev->rpdev->ept, kbuf, len);
+
+unlock_eptdev:
+	mutex_unlock(&eptdev->ept_lock);
+
+free_kbuf:
+	kfree(kbuf);
+	return ret < 0 ? ret : len;
+}
+
+static __poll_t rpmsg_raw_poll(struct file *filp, poll_table *wait)
+{
+	struct rpmsg_raw *eptdev = filp->private_data;
+	__poll_t mask = 0;
+
+	if (!eptdev->rpdev->ept)
+		return EPOLLERR;
+
+	poll_wait(filp, &eptdev->readq, wait);
+
+	if (!skb_queue_empty(&eptdev->queue))
+		mask |= EPOLLIN | EPOLLRDNORM;
+
+	mask |= rpmsg_poll(eptdev->rpdev->ept, filp, wait);
+
+	return mask;
+}
+
+static void rpmsg_raw_release_device(struct device *dev)
+{
+	struct rpmsg_raw *eptdev = dev_to_eptdev(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 const struct file_operations rpmsg_raw_fops = {
+	.owner = THIS_MODULE,
+	.open = rpmsg_raw_open,
+	.release = rpmsg_raw_release,
+	.read_iter = rpmsg_raw_read_iter,
+	.write_iter = rpmsg_raw_write_iter,
+	.poll = rpmsg_raw_poll,
+};
+
+static int rpmsg_raw_probe(struct rpmsg_device *rpdev)
+{
+	struct rpmsg_raw *eptdev;
+	struct device *dev;
+	int ret;
+
+	eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL);
+	if (!eptdev)
+		return -ENOMEM;
+
+	dev = &eptdev->dev;
+	eptdev->rpdev = rpdev;
+
+	mutex_init(&eptdev->ept_lock);
+	spin_lock_init(&eptdev->queue_lock);
+	skb_queue_head_init(&eptdev->queue);
+	init_waitqueue_head(&eptdev->readq);
+
+	device_initialize(dev);
+	dev->parent = &rpdev->dev;
+	dev_set_drvdata(dev, eptdev);
+
+	cdev_init(&eptdev->cdev, &rpmsg_raw_fops);
+	eptdev->cdev.owner = THIS_MODULE;
+
+	ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
+	if (ret < 0) {
+		dev_err(dev, "failed to get IDA (%d)\n", ret);
+		goto free_eptdev;
+	}
+
+	dev->devt = MKDEV(MAJOR(rpmsg_raw_major), ret);
+
+	ret = ida_simple_get(&rpmsg_ept_ida, 0, 0, GFP_KERNEL);
+	if (ret < 0)
+		goto free_minor_ida;
+	dev->id = ret;
+	dev_set_name(dev, "rpmsg%d", dev->id);
+
+	ret = cdev_add(&eptdev->cdev, dev->devt, 1);
+	if (ret) {
+		ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));
+		dev_err(&rpdev->dev, "failed to add char device(%d)\n", ret);
+		goto free_ept_ida;
+	}
+
+	/* We can now rely on the release function for cleanup */
+	dev->release = rpmsg_raw_release_device;
+
+	ret = device_add(dev);
+	if (ret) {
+		dev_err(dev, "device_add failed: %d\n", ret);
+		put_device(dev);
+	}
+
+	dev_set_drvdata(&rpdev->dev, eptdev);
+
+	return ret;
+
+free_ept_ida:
+	ida_simple_remove(&rpmsg_ept_ida, dev->id);
+free_minor_ida:
+	ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));
+free_eptdev:
+	put_device(dev);
+	kfree(eptdev);
+
+	return ret;
+}
+
+static void rpmsg_raw_remove(struct rpmsg_device *rpdev)
+{
+	struct rpmsg_raw *eptdev = dev_get_drvdata(&rpdev->dev);
+
+	/* wake up any blocked readers */
+	wake_up_interruptible(&eptdev->readq);
+
+	device_del(&eptdev->dev);
+
+	put_device(&eptdev->dev);
+}
+
+static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = {
+	{ .name	= "rpmsg-raw" },
+	{ },
+};
+MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table);
+
+static struct rpmsg_driver rpmsg_raw_client = {
+	.drv.name	= KBUILD_MODNAME,
+	.id_table	= rpmsg_driver_sample_id_table,
+	.probe		= rpmsg_raw_probe,
+	.callback	= rpmsg_raw_cb,
+	.remove		= rpmsg_raw_remove,
+};
+
+static int rpmsg_raw_init(void)
+{
+	int ret;
+
+	ret = alloc_chrdev_region(&rpmsg_raw_major, 0, RPMSG_DEV_MAX,
+				  "rpmsg");
+	if (ret < 0) {
+		pr_err("rpmsg: failed to allocate char dev region\n");
+		return ret;
+	}
+
+	ret = register_rpmsg_driver(&rpmsg_raw_client);
+	if (ret < 0)
+		unregister_chrdev_region(rpmsg_raw_major, RPMSG_DEV_MAX);
+
+	return ret;
+}
+module_init(rpmsg_raw_init);
+
+static void rpmsg_raw_exit(void)
+{
+	unregister_rpmsg_driver(&rpmsg_raw_client);
+	unregister_chrdev_region(rpmsg_raw_major, RPMSG_DEV_MAX);
+}
+module_exit(rpmsg_raw_exit);
+
+MODULE_DESCRIPTION("RPMSG raw service driver");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_ALIAS("rpmsg_raw");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


  reply	other threads:[~2020-07-31 12:12 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-07-31 12:10 [PATCH 00/13] introduce IOCTL interface for RPMsg channel management Arnaud Pouliquen
2020-07-31 12:10 ` Arnaud Pouliquen [this message]
2020-07-31 12:10 ` [PATCH 02/13] rpmsg: introduce rpmsg_control driver for channel creation Arnaud Pouliquen
2020-07-31 15:56   ` Randy Dunlap
2020-07-31 12:10 ` [PATCH 03/13] rpmsg: add helper to create the rpmsg ctrl device Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 04/13] rpmsg: virtio: probe the rpmsg_ctrl device Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 05/13] rpmsg: uapi: add service param for create destroy ioctls Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 06/13] rpmsg: add RPMsg control info structure Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 07/13] rpmsg: control: add driver registration API Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 08/13] rpmsg: raw: register service to the rpmsg control Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 09/13] rpmsg: add override field in channel info Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 10/13] rpmsg: ns: initialize channel info override field Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 11/13] rpmsg: virtio: use the driver_override in channel creation Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 12/13] rpmsg: control: implement the ioctrl function to create device Arnaud Pouliquen
2020-07-31 12:10 ` [PATCH 13/13] rpmsg: ctrl: add support of the endpoints release Arnaud Pouliquen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200731121043.24199-2-arnaud.pouliquen@st.com \
    --to=arnaud.pouliquen@st.com \
    --cc=bjorn.andersson@linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-remoteproc@vger.kernel.org \
    --cc=linux-stm32@st-md-mailman.stormreply.com \
    --cc=mathieu.poirier@linaro.org \
    --cc=ohad@wizery.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.