18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2016, Linaro Ltd. 48c2ecf20Sopenharmony_ci * Copyright (c) 2012, Michal Simek <monstr@monstr.eu> 58c2ecf20Sopenharmony_ci * Copyright (c) 2012, PetaLogix 68c2ecf20Sopenharmony_ci * Copyright (c) 2011, Texas Instruments, Inc. 78c2ecf20Sopenharmony_ci * Copyright (c) 2011, Google, Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on rpmsg performance statistics driver by Michal Simek, which in turn 108c2ecf20Sopenharmony_ci * was based on TI & Google OMX rpmsg driver. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#include <linux/cdev.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/fs.h> 158c2ecf20Sopenharmony_ci#include <linux/idr.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/poll.h> 198c2ecf20Sopenharmony_ci#include <linux/rpmsg.h> 208c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 238c2ecf20Sopenharmony_ci#include <uapi/linux/rpmsg.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "rpmsg_internal.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define RPMSG_DEV_MAX (MINORMASK + 1) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic dev_t rpmsg_major; 308c2ecf20Sopenharmony_cistatic struct class *rpmsg_class; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic DEFINE_IDA(rpmsg_ctrl_ida); 338c2ecf20Sopenharmony_cistatic DEFINE_IDA(rpmsg_ept_ida); 348c2ecf20Sopenharmony_cistatic DEFINE_IDA(rpmsg_minor_ida); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define dev_to_eptdev(dev) container_of(dev, struct rpmsg_eptdev, dev) 378c2ecf20Sopenharmony_ci#define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_eptdev, cdev) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define dev_to_ctrldev(dev) container_of(dev, struct rpmsg_ctrldev, dev) 408c2ecf20Sopenharmony_ci#define cdev_to_ctrldev(i_cdev) container_of(i_cdev, struct rpmsg_ctrldev, cdev) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/** 438c2ecf20Sopenharmony_ci * struct rpmsg_ctrldev - control device for instantiating endpoint devices 448c2ecf20Sopenharmony_ci * @rpdev: underlaying rpmsg device 458c2ecf20Sopenharmony_ci * @cdev: cdev for the ctrl device 468c2ecf20Sopenharmony_ci * @dev: device for the ctrl device 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistruct rpmsg_ctrldev { 498c2ecf20Sopenharmony_ci struct rpmsg_device *rpdev; 508c2ecf20Sopenharmony_ci struct cdev cdev; 518c2ecf20Sopenharmony_ci struct device dev; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * struct rpmsg_eptdev - endpoint device context 568c2ecf20Sopenharmony_ci * @dev: endpoint device 578c2ecf20Sopenharmony_ci * @cdev: cdev for the endpoint device 588c2ecf20Sopenharmony_ci * @rpdev: underlaying rpmsg device 598c2ecf20Sopenharmony_ci * @chinfo: info used to open the endpoint 608c2ecf20Sopenharmony_ci * @ept_lock: synchronization of @ept modifications 618c2ecf20Sopenharmony_ci * @ept: rpmsg endpoint reference, when open 628c2ecf20Sopenharmony_ci * @queue_lock: synchronization of @queue operations 638c2ecf20Sopenharmony_ci * @queue: incoming message queue 648c2ecf20Sopenharmony_ci * @readq: wait object for incoming queue 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistruct rpmsg_eptdev { 678c2ecf20Sopenharmony_ci struct device dev; 688c2ecf20Sopenharmony_ci struct cdev cdev; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci struct rpmsg_device *rpdev; 718c2ecf20Sopenharmony_ci struct rpmsg_channel_info chinfo; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci struct mutex ept_lock; 748c2ecf20Sopenharmony_ci struct rpmsg_endpoint *ept; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci spinlock_t queue_lock; 778c2ecf20Sopenharmony_ci struct sk_buff_head queue; 788c2ecf20Sopenharmony_ci wait_queue_head_t readq; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int rpmsg_eptdev_destroy(struct device *dev, void *data) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci mutex_lock(&eptdev->ept_lock); 868c2ecf20Sopenharmony_ci if (eptdev->ept) { 878c2ecf20Sopenharmony_ci rpmsg_destroy_ept(eptdev->ept); 888c2ecf20Sopenharmony_ci eptdev->ept = NULL; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci mutex_unlock(&eptdev->ept_lock); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* wake up any blocked readers */ 938c2ecf20Sopenharmony_ci wake_up_interruptible(&eptdev->readq); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci cdev_device_del(&eptdev->cdev, &eptdev->dev); 968c2ecf20Sopenharmony_ci put_device(&eptdev->dev); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, 1028c2ecf20Sopenharmony_ci void *priv, u32 addr) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = priv; 1058c2ecf20Sopenharmony_ci struct sk_buff *skb; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci skb = alloc_skb(len, GFP_ATOMIC); 1088c2ecf20Sopenharmony_ci if (!skb) 1098c2ecf20Sopenharmony_ci return -ENOMEM; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci skb_put_data(skb, buf, len); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci spin_lock(&eptdev->queue_lock); 1148c2ecf20Sopenharmony_ci skb_queue_tail(&eptdev->queue, skb); 1158c2ecf20Sopenharmony_ci spin_unlock(&eptdev->queue_lock); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* wake up any blocking processes, waiting for new data */ 1188c2ecf20Sopenharmony_ci wake_up_interruptible(&eptdev->readq); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int rpmsg_eptdev_open(struct inode *inode, struct file *filp) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); 1268c2ecf20Sopenharmony_ci struct rpmsg_endpoint *ept; 1278c2ecf20Sopenharmony_ci struct rpmsg_device *rpdev = eptdev->rpdev; 1288c2ecf20Sopenharmony_ci struct device *dev = &eptdev->dev; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci get_device(dev); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo); 1338c2ecf20Sopenharmony_ci if (!ept) { 1348c2ecf20Sopenharmony_ci dev_err(dev, "failed to open %s\n", eptdev->chinfo.name); 1358c2ecf20Sopenharmony_ci put_device(dev); 1368c2ecf20Sopenharmony_ci return -EINVAL; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci eptdev->ept = ept; 1408c2ecf20Sopenharmony_ci filp->private_data = eptdev; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int rpmsg_eptdev_release(struct inode *inode, struct file *filp) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); 1488c2ecf20Sopenharmony_ci struct device *dev = &eptdev->dev; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Close the endpoint, if it's not already destroyed by the parent */ 1518c2ecf20Sopenharmony_ci mutex_lock(&eptdev->ept_lock); 1528c2ecf20Sopenharmony_ci if (eptdev->ept) { 1538c2ecf20Sopenharmony_ci rpmsg_destroy_ept(eptdev->ept); 1548c2ecf20Sopenharmony_ci eptdev->ept = NULL; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci mutex_unlock(&eptdev->ept_lock); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Discard all SKBs */ 1598c2ecf20Sopenharmony_ci skb_queue_purge(&eptdev->queue); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci put_device(dev); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic ssize_t rpmsg_eptdev_read_iter(struct kiocb *iocb, struct iov_iter *to) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct file *filp = iocb->ki_filp; 1698c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = filp->private_data; 1708c2ecf20Sopenharmony_ci unsigned long flags; 1718c2ecf20Sopenharmony_ci struct sk_buff *skb; 1728c2ecf20Sopenharmony_ci int use; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!eptdev->ept) 1758c2ecf20Sopenharmony_ci return -EPIPE; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci spin_lock_irqsave(&eptdev->queue_lock, flags); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Wait for data in the queue */ 1808c2ecf20Sopenharmony_ci if (skb_queue_empty(&eptdev->queue)) { 1818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&eptdev->queue_lock, flags); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (filp->f_flags & O_NONBLOCK) 1848c2ecf20Sopenharmony_ci return -EAGAIN; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Wait until we get data or the endpoint goes away */ 1878c2ecf20Sopenharmony_ci if (wait_event_interruptible(eptdev->readq, 1888c2ecf20Sopenharmony_ci !skb_queue_empty(&eptdev->queue) || 1898c2ecf20Sopenharmony_ci !eptdev->ept)) 1908c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* We lost the endpoint while waiting */ 1938c2ecf20Sopenharmony_ci if (!eptdev->ept) 1948c2ecf20Sopenharmony_ci return -EPIPE; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci spin_lock_irqsave(&eptdev->queue_lock, flags); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci skb = skb_dequeue(&eptdev->queue); 2008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&eptdev->queue_lock, flags); 2018c2ecf20Sopenharmony_ci if (!skb) 2028c2ecf20Sopenharmony_ci return -EFAULT; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci use = min_t(size_t, iov_iter_count(to), skb->len); 2058c2ecf20Sopenharmony_ci if (copy_to_iter(skb->data, use, to) != use) 2068c2ecf20Sopenharmony_ci use = -EFAULT; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci kfree_skb(skb); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return use; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic ssize_t rpmsg_eptdev_write_iter(struct kiocb *iocb, 2148c2ecf20Sopenharmony_ci struct iov_iter *from) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct file *filp = iocb->ki_filp; 2178c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = filp->private_data; 2188c2ecf20Sopenharmony_ci size_t len = iov_iter_count(from); 2198c2ecf20Sopenharmony_ci void *kbuf; 2208c2ecf20Sopenharmony_ci int ret; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci kbuf = kzalloc(len, GFP_KERNEL); 2238c2ecf20Sopenharmony_ci if (!kbuf) 2248c2ecf20Sopenharmony_ci return -ENOMEM; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!copy_from_iter_full(kbuf, len, from)) { 2278c2ecf20Sopenharmony_ci ret = -EFAULT; 2288c2ecf20Sopenharmony_ci goto free_kbuf; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&eptdev->ept_lock)) { 2328c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 2338c2ecf20Sopenharmony_ci goto free_kbuf; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (!eptdev->ept) { 2378c2ecf20Sopenharmony_ci ret = -EPIPE; 2388c2ecf20Sopenharmony_ci goto unlock_eptdev; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (filp->f_flags & O_NONBLOCK) 2428c2ecf20Sopenharmony_ci ret = rpmsg_trysend(eptdev->ept, kbuf, len); 2438c2ecf20Sopenharmony_ci else 2448c2ecf20Sopenharmony_ci ret = rpmsg_send(eptdev->ept, kbuf, len); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciunlock_eptdev: 2478c2ecf20Sopenharmony_ci mutex_unlock(&eptdev->ept_lock); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cifree_kbuf: 2508c2ecf20Sopenharmony_ci kfree(kbuf); 2518c2ecf20Sopenharmony_ci return ret < 0 ? ret : len; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = filp->private_data; 2578c2ecf20Sopenharmony_ci __poll_t mask = 0; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!eptdev->ept) 2608c2ecf20Sopenharmony_ci return EPOLLERR; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci poll_wait(filp, &eptdev->readq, wait); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (!skb_queue_empty(&eptdev->queue)) 2658c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci mask |= rpmsg_poll(eptdev->ept, filp, wait); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return mask; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, 2738c2ecf20Sopenharmony_ci unsigned long arg) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = fp->private_data; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (cmd != RPMSG_DESTROY_EPT_IOCTL) 2788c2ecf20Sopenharmony_ci return -EINVAL; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return rpmsg_eptdev_destroy(&eptdev->dev, NULL); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic const struct file_operations rpmsg_eptdev_fops = { 2848c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2858c2ecf20Sopenharmony_ci .open = rpmsg_eptdev_open, 2868c2ecf20Sopenharmony_ci .release = rpmsg_eptdev_release, 2878c2ecf20Sopenharmony_ci .read_iter = rpmsg_eptdev_read_iter, 2888c2ecf20Sopenharmony_ci .write_iter = rpmsg_eptdev_write_iter, 2898c2ecf20Sopenharmony_ci .poll = rpmsg_eptdev_poll, 2908c2ecf20Sopenharmony_ci .unlocked_ioctl = rpmsg_eptdev_ioctl, 2918c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr, 2958c2ecf20Sopenharmony_ci char *buf) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", eptdev->chinfo.name); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic ssize_t src_show(struct device *dev, struct device_attribute *attr, 3048c2ecf20Sopenharmony_ci char *buf) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", eptdev->chinfo.src); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(src); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic ssize_t dst_show(struct device *dev, struct device_attribute *attr, 3138c2ecf20Sopenharmony_ci char *buf) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", eptdev->chinfo.dst); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(dst); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic struct attribute *rpmsg_eptdev_attrs[] = { 3228c2ecf20Sopenharmony_ci &dev_attr_name.attr, 3238c2ecf20Sopenharmony_ci &dev_attr_src.attr, 3248c2ecf20Sopenharmony_ci &dev_attr_dst.attr, 3258c2ecf20Sopenharmony_ci NULL 3268c2ecf20Sopenharmony_ci}; 3278c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(rpmsg_eptdev); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void rpmsg_eptdev_release_device(struct device *dev) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ida_simple_remove(&rpmsg_ept_ida, dev->id); 3348c2ecf20Sopenharmony_ci ida_simple_remove(&rpmsg_minor_ida, MINOR(eptdev->dev.devt)); 3358c2ecf20Sopenharmony_ci kfree(eptdev); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev, 3398c2ecf20Sopenharmony_ci struct rpmsg_channel_info chinfo) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct rpmsg_device *rpdev = ctrldev->rpdev; 3428c2ecf20Sopenharmony_ci struct rpmsg_eptdev *eptdev; 3438c2ecf20Sopenharmony_ci struct device *dev; 3448c2ecf20Sopenharmony_ci int ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL); 3478c2ecf20Sopenharmony_ci if (!eptdev) 3488c2ecf20Sopenharmony_ci return -ENOMEM; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci dev = &eptdev->dev; 3518c2ecf20Sopenharmony_ci eptdev->rpdev = rpdev; 3528c2ecf20Sopenharmony_ci eptdev->chinfo = chinfo; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci mutex_init(&eptdev->ept_lock); 3558c2ecf20Sopenharmony_ci spin_lock_init(&eptdev->queue_lock); 3568c2ecf20Sopenharmony_ci skb_queue_head_init(&eptdev->queue); 3578c2ecf20Sopenharmony_ci init_waitqueue_head(&eptdev->readq); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci device_initialize(dev); 3608c2ecf20Sopenharmony_ci dev->class = rpmsg_class; 3618c2ecf20Sopenharmony_ci dev->parent = &ctrldev->dev; 3628c2ecf20Sopenharmony_ci dev->groups = rpmsg_eptdev_groups; 3638c2ecf20Sopenharmony_ci dev_set_drvdata(dev, eptdev); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci cdev_init(&eptdev->cdev, &rpmsg_eptdev_fops); 3668c2ecf20Sopenharmony_ci eptdev->cdev.owner = THIS_MODULE; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL); 3698c2ecf20Sopenharmony_ci if (ret < 0) 3708c2ecf20Sopenharmony_ci goto free_eptdev; 3718c2ecf20Sopenharmony_ci dev->devt = MKDEV(MAJOR(rpmsg_major), ret); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci ret = ida_simple_get(&rpmsg_ept_ida, 0, 0, GFP_KERNEL); 3748c2ecf20Sopenharmony_ci if (ret < 0) 3758c2ecf20Sopenharmony_ci goto free_minor_ida; 3768c2ecf20Sopenharmony_ci dev->id = ret; 3778c2ecf20Sopenharmony_ci dev_set_name(dev, "rpmsg%d", ret); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ret = cdev_device_add(&eptdev->cdev, &eptdev->dev); 3808c2ecf20Sopenharmony_ci if (ret) 3818c2ecf20Sopenharmony_ci goto free_ept_ida; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* We can now rely on the release function for cleanup */ 3848c2ecf20Sopenharmony_ci dev->release = rpmsg_eptdev_release_device; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return ret; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cifree_ept_ida: 3898c2ecf20Sopenharmony_ci ida_simple_remove(&rpmsg_ept_ida, dev->id); 3908c2ecf20Sopenharmony_cifree_minor_ida: 3918c2ecf20Sopenharmony_ci ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); 3928c2ecf20Sopenharmony_cifree_eptdev: 3938c2ecf20Sopenharmony_ci put_device(dev); 3948c2ecf20Sopenharmony_ci kfree(eptdev); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return ret; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int rpmsg_ctrldev_open(struct inode *inode, struct file *filp) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci get_device(&ctrldev->dev); 4048c2ecf20Sopenharmony_ci filp->private_data = ctrldev; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int rpmsg_ctrldev_release(struct inode *inode, struct file *filp) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci put_device(&ctrldev->dev); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd, 4198c2ecf20Sopenharmony_ci unsigned long arg) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct rpmsg_ctrldev *ctrldev = fp->private_data; 4228c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 4238c2ecf20Sopenharmony_ci struct rpmsg_endpoint_info eptinfo; 4248c2ecf20Sopenharmony_ci struct rpmsg_channel_info chinfo; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (cmd != RPMSG_CREATE_EPT_IOCTL) 4278c2ecf20Sopenharmony_ci return -EINVAL; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (copy_from_user(&eptinfo, argp, sizeof(eptinfo))) 4308c2ecf20Sopenharmony_ci return -EFAULT; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci memcpy(chinfo.name, eptinfo.name, RPMSG_NAME_SIZE); 4338c2ecf20Sopenharmony_ci chinfo.name[RPMSG_NAME_SIZE-1] = '\0'; 4348c2ecf20Sopenharmony_ci chinfo.src = eptinfo.src; 4358c2ecf20Sopenharmony_ci chinfo.dst = eptinfo.dst; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return rpmsg_eptdev_create(ctrldev, chinfo); 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic const struct file_operations rpmsg_ctrldev_fops = { 4418c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4428c2ecf20Sopenharmony_ci .open = rpmsg_ctrldev_open, 4438c2ecf20Sopenharmony_ci .release = rpmsg_ctrldev_release, 4448c2ecf20Sopenharmony_ci .unlocked_ioctl = rpmsg_ctrldev_ioctl, 4458c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 4468c2ecf20Sopenharmony_ci}; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void rpmsg_ctrldev_release_device(struct device *dev) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct rpmsg_ctrldev *ctrldev = dev_to_ctrldev(dev); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci ida_simple_remove(&rpmsg_ctrl_ida, dev->id); 4538c2ecf20Sopenharmony_ci ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); 4548c2ecf20Sopenharmony_ci kfree(ctrldev); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct rpmsg_ctrldev *ctrldev; 4608c2ecf20Sopenharmony_ci struct device *dev; 4618c2ecf20Sopenharmony_ci int ret; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL); 4648c2ecf20Sopenharmony_ci if (!ctrldev) 4658c2ecf20Sopenharmony_ci return -ENOMEM; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci ctrldev->rpdev = rpdev; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci dev = &ctrldev->dev; 4708c2ecf20Sopenharmony_ci device_initialize(dev); 4718c2ecf20Sopenharmony_ci dev->parent = &rpdev->dev; 4728c2ecf20Sopenharmony_ci dev->class = rpmsg_class; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops); 4758c2ecf20Sopenharmony_ci ctrldev->cdev.owner = THIS_MODULE; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL); 4788c2ecf20Sopenharmony_ci if (ret < 0) 4798c2ecf20Sopenharmony_ci goto free_ctrldev; 4808c2ecf20Sopenharmony_ci dev->devt = MKDEV(MAJOR(rpmsg_major), ret); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = ida_simple_get(&rpmsg_ctrl_ida, 0, 0, GFP_KERNEL); 4838c2ecf20Sopenharmony_ci if (ret < 0) 4848c2ecf20Sopenharmony_ci goto free_minor_ida; 4858c2ecf20Sopenharmony_ci dev->id = ret; 4868c2ecf20Sopenharmony_ci dev_set_name(&ctrldev->dev, "rpmsg_ctrl%d", ret); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = cdev_device_add(&ctrldev->cdev, &ctrldev->dev); 4898c2ecf20Sopenharmony_ci if (ret) 4908c2ecf20Sopenharmony_ci goto free_ctrl_ida; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* We can now rely on the release function for cleanup */ 4938c2ecf20Sopenharmony_ci dev->release = rpmsg_ctrldev_release_device; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci dev_set_drvdata(&rpdev->dev, ctrldev); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return ret; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cifree_ctrl_ida: 5008c2ecf20Sopenharmony_ci ida_simple_remove(&rpmsg_ctrl_ida, dev->id); 5018c2ecf20Sopenharmony_cifree_minor_ida: 5028c2ecf20Sopenharmony_ci ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); 5038c2ecf20Sopenharmony_cifree_ctrldev: 5048c2ecf20Sopenharmony_ci put_device(dev); 5058c2ecf20Sopenharmony_ci kfree(ctrldev); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return ret; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic void rpmsg_chrdev_remove(struct rpmsg_device *rpdev) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct rpmsg_ctrldev *ctrldev = dev_get_drvdata(&rpdev->dev); 5138c2ecf20Sopenharmony_ci int ret; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Destroy all endpoints */ 5168c2ecf20Sopenharmony_ci ret = device_for_each_child(&ctrldev->dev, NULL, rpmsg_eptdev_destroy); 5178c2ecf20Sopenharmony_ci if (ret) 5188c2ecf20Sopenharmony_ci dev_warn(&rpdev->dev, "failed to nuke endpoints: %d\n", ret); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci cdev_device_del(&ctrldev->cdev, &ctrldev->dev); 5218c2ecf20Sopenharmony_ci put_device(&ctrldev->dev); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic struct rpmsg_driver rpmsg_chrdev_driver = { 5258c2ecf20Sopenharmony_ci .probe = rpmsg_chrdev_probe, 5268c2ecf20Sopenharmony_ci .remove = rpmsg_chrdev_remove, 5278c2ecf20Sopenharmony_ci .drv = { 5288c2ecf20Sopenharmony_ci .name = "rpmsg_chrdev", 5298c2ecf20Sopenharmony_ci }, 5308c2ecf20Sopenharmony_ci}; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic int rpmsg_char_init(void) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci int ret; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg"); 5378c2ecf20Sopenharmony_ci if (ret < 0) { 5388c2ecf20Sopenharmony_ci pr_err("rpmsg: failed to allocate char dev region\n"); 5398c2ecf20Sopenharmony_ci return ret; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci rpmsg_class = class_create(THIS_MODULE, "rpmsg"); 5438c2ecf20Sopenharmony_ci if (IS_ERR(rpmsg_class)) { 5448c2ecf20Sopenharmony_ci pr_err("failed to create rpmsg class\n"); 5458c2ecf20Sopenharmony_ci unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); 5468c2ecf20Sopenharmony_ci return PTR_ERR(rpmsg_class); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci ret = register_rpmsg_driver(&rpmsg_chrdev_driver); 5508c2ecf20Sopenharmony_ci if (ret < 0) { 5518c2ecf20Sopenharmony_ci pr_err("rpmsgchr: failed to register rpmsg driver\n"); 5528c2ecf20Sopenharmony_ci class_destroy(rpmsg_class); 5538c2ecf20Sopenharmony_ci unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci return ret; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_cipostcore_initcall(rpmsg_char_init); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic void rpmsg_chrdev_exit(void) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci unregister_rpmsg_driver(&rpmsg_chrdev_driver); 5638c2ecf20Sopenharmony_ci class_destroy(rpmsg_class); 5648c2ecf20Sopenharmony_ci unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_cimodule_exit(rpmsg_chrdev_exit); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ciMODULE_ALIAS("rpmsg:rpmsg_chrdev"); 5698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 570