diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-30 10:43:19 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-30 10:43:19 -0700 |
commit | 3312db01db06ace51bb4934e9de64da62fac3f38 (patch) | |
tree | 2aa2f5ff07332239d7817fe6c97880ddd98e1f81 /drivers/rpmsg/rpmsg_char.c | |
parent | f18e345dd156cc0fcf4a4911af2f959120613871 (diff) | |
parent | 8109517b394e6deab5fd21cc5460e82ffed229c6 (diff) |
Merge tag 'rpmsg-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux
Pull rpmsg updates from Bjorn Andersson:
"The major part of the rpmsg changes for v5.18 relates to improvements
in the rpmsg char driver, which now allow automatically attaching to
rpmsg channels as well as initiating new communication channels from
the Linux side.
The SMD driver is moved to arch_initcall with the purpose of
registering root clocks earlier during boot.
Also in the SMD driver, a workaround for the resource power management
(RPM) channel is introduced to resolve an issue where both the RPM and
Linux side waits for the other to close the communication established
by the bootloader - this unblocks support for clocks and regulators on
some older Qualcomm platforms"
* tag 'rpmsg-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux:
rpmsg: ctrl: Introduce new RPMSG_CREATE/RELEASE_DEV_IOCTL controls
rpmsg: char: Introduce the "rpmsg-raw" channel
rpmsg: char: Add possibility to use default endpoint of the rpmsg device
rpmsg: char: Refactor rpmsg_chrdev_eptdev_create function
rpmsg: Update rpmsg_chrdev_register_device function
rpmsg: Move the rpmsg control device from rpmsg_char to rpmsg_ctrl
rpmsg: Create the rpmsg class in core instead of in rpmsg char
rpmsg: char: Export eptdev create and destroy functions
rpmsg: char: treat rpmsg_trysend() ENOMEM as EAGAIN
rpmsg: qcom_smd: Fix redundant channel->registered assignment
rpmsg: use struct_size over open coded arithmetic
rpmsg: smd: allow opening rpm_requests even if already opened
rpmsg: qcom_smd: Promote to arch_initcall
Diffstat (limited to 'drivers/rpmsg/rpmsg_char.c')
-rw-r--r-- | drivers/rpmsg/rpmsg_char.c | 238 |
1 files changed, 90 insertions, 148 deletions
diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 5663cf799c95..b6183d4f62a2 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 @@ -25,33 +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 @@ -63,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; @@ -73,13 +62,15 @@ 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); @@ -98,6 +89,7 @@ static int rpmsg_eptdev_destroy(struct device *dev, void *data) 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) @@ -133,7 +125,15 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) 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); @@ -154,7 +154,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); @@ -242,10 +243,13 @@ static ssize_t rpmsg_eptdev_write_iter(struct kiocb *iocb, goto unlock_eptdev; } - if (filp->f_flags & O_NONBLOCK) + if (filp->f_flags & O_NONBLOCK) { ret = rpmsg_trysendto(eptdev->ept, kbuf, len, eptdev->chinfo.dst); - else + if (ret == -ENOMEM) + ret = -EAGAIN; + } else { ret = rpmsg_sendto(eptdev->ept, kbuf, len, eptdev->chinfo.dst); + } unlock_eptdev: mutex_unlock(&eptdev->ept_lock); @@ -281,7 +285,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 = { @@ -339,21 +347,18 @@ static void rpmsg_eptdev_release_device(struct device *dev) 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); @@ -362,13 +367,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; @@ -400,163 +415,91 @@ 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); -}; + struct rpmsg_eptdev *eptdev; + int 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, -}; + eptdev = rpmsg_chrdev_eptdev_alloc(rpdev, parent); + if (IS_ERR(eptdev)) + return PTR_ERR(eptdev); -static void rpmsg_ctrldev_release_device(struct device *dev) -{ - struct rpmsg_ctrldev *ctrldev = dev_to_ctrldev(dev); + ret = rpmsg_chrdev_eptdev_add(eptdev, chinfo); - ida_simple_remove(&rpmsg_ctrl_ida, dev->id); - ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); - kfree(ctrldev); + return ret; } +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); + struct rpmsg_channel_info chinfo; + struct rpmsg_eptdev *eptdev; + struct device *dev = &rpdev->dev; - ret = cdev_device_add(&ctrldev->cdev, &ctrldev->dev); - if (ret) - goto free_ctrl_ida; + memcpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); + chinfo.src = rpdev->src; + chinfo.dst = rpdev->dst; - /* We can now rely on the release function for cleanup */ - dev->release = rpmsg_ctrldev_release_device; + eptdev = rpmsg_chrdev_eptdev_alloc(rpdev, dev); + if (IS_ERR(eptdev)) + return PTR_ERR(eptdev); - dev_set_drvdata(&rpdev->dev, ctrldev); + /* Set the default_ept to the rpmsg device endpoint */ + eptdev->default_ept = rpdev->ept; - return ret; + /* + * The rpmsg_ept_cb uses *priv parameter to get its rpmsg_eptdev context. + * Storedit in default_ept *priv field. + */ + eptdev->default_ept->priv = eptdev; -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; + 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); - - cdev_device_del(&ctrldev->cdev, &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_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("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("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_chrdev_init); @@ -564,7 +507,6 @@ 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); |