1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2016, Linaro Ltd.
4*4882a593Smuzhiyun * Copyright (c) 2012, Michal Simek <monstr@monstr.eu>
5*4882a593Smuzhiyun * Copyright (c) 2012, PetaLogix
6*4882a593Smuzhiyun * Copyright (c) 2011, Texas Instruments, Inc.
7*4882a593Smuzhiyun * Copyright (c) 2011, Google, Inc.
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * Based on rpmsg performance statistics driver by Michal Simek, which in turn
10*4882a593Smuzhiyun * was based on TI & Google OMX rpmsg driver.
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun #include <linux/cdev.h>
13*4882a593Smuzhiyun #include <linux/device.h>
14*4882a593Smuzhiyun #include <linux/fs.h>
15*4882a593Smuzhiyun #include <linux/idr.h>
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/poll.h>
19*4882a593Smuzhiyun #include <linux/rpmsg.h>
20*4882a593Smuzhiyun #include <linux/skbuff.h>
21*4882a593Smuzhiyun #include <linux/slab.h>
22*4882a593Smuzhiyun #include <linux/uaccess.h>
23*4882a593Smuzhiyun #include <uapi/linux/rpmsg.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include "rpmsg_internal.h"
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define RPMSG_DEV_MAX (MINORMASK + 1)
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun static dev_t rpmsg_major;
30*4882a593Smuzhiyun static struct class *rpmsg_class;
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun static DEFINE_IDA(rpmsg_ctrl_ida);
33*4882a593Smuzhiyun static DEFINE_IDA(rpmsg_ept_ida);
34*4882a593Smuzhiyun static DEFINE_IDA(rpmsg_minor_ida);
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define dev_to_eptdev(dev) container_of(dev, struct rpmsg_eptdev, dev)
37*4882a593Smuzhiyun #define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_eptdev, cdev)
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun #define dev_to_ctrldev(dev) container_of(dev, struct rpmsg_ctrldev, dev)
40*4882a593Smuzhiyun #define cdev_to_ctrldev(i_cdev) container_of(i_cdev, struct rpmsg_ctrldev, cdev)
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun /**
43*4882a593Smuzhiyun * struct rpmsg_ctrldev - control device for instantiating endpoint devices
44*4882a593Smuzhiyun * @rpdev: underlaying rpmsg device
45*4882a593Smuzhiyun * @cdev: cdev for the ctrl device
46*4882a593Smuzhiyun * @dev: device for the ctrl device
47*4882a593Smuzhiyun */
48*4882a593Smuzhiyun struct rpmsg_ctrldev {
49*4882a593Smuzhiyun struct rpmsg_device *rpdev;
50*4882a593Smuzhiyun struct cdev cdev;
51*4882a593Smuzhiyun struct device dev;
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /**
55*4882a593Smuzhiyun * struct rpmsg_eptdev - endpoint device context
56*4882a593Smuzhiyun * @dev: endpoint device
57*4882a593Smuzhiyun * @cdev: cdev for the endpoint device
58*4882a593Smuzhiyun * @rpdev: underlaying rpmsg device
59*4882a593Smuzhiyun * @chinfo: info used to open the endpoint
60*4882a593Smuzhiyun * @ept_lock: synchronization of @ept modifications
61*4882a593Smuzhiyun * @ept: rpmsg endpoint reference, when open
62*4882a593Smuzhiyun * @queue_lock: synchronization of @queue operations
63*4882a593Smuzhiyun * @queue: incoming message queue
64*4882a593Smuzhiyun * @readq: wait object for incoming queue
65*4882a593Smuzhiyun */
66*4882a593Smuzhiyun struct rpmsg_eptdev {
67*4882a593Smuzhiyun struct device dev;
68*4882a593Smuzhiyun struct cdev cdev;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun struct rpmsg_device *rpdev;
71*4882a593Smuzhiyun struct rpmsg_channel_info chinfo;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun struct mutex ept_lock;
74*4882a593Smuzhiyun struct rpmsg_endpoint *ept;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun spinlock_t queue_lock;
77*4882a593Smuzhiyun struct sk_buff_head queue;
78*4882a593Smuzhiyun wait_queue_head_t readq;
79*4882a593Smuzhiyun };
80*4882a593Smuzhiyun
rpmsg_eptdev_destroy(struct device * dev,void * data)81*4882a593Smuzhiyun static int rpmsg_eptdev_destroy(struct device *dev, void *data)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun mutex_lock(&eptdev->ept_lock);
86*4882a593Smuzhiyun if (eptdev->ept) {
87*4882a593Smuzhiyun rpmsg_destroy_ept(eptdev->ept);
88*4882a593Smuzhiyun eptdev->ept = NULL;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun mutex_unlock(&eptdev->ept_lock);
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun /* wake up any blocked readers */
93*4882a593Smuzhiyun wake_up_interruptible(&eptdev->readq);
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun cdev_device_del(&eptdev->cdev, &eptdev->dev);
96*4882a593Smuzhiyun put_device(&eptdev->dev);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun return 0;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
rpmsg_ept_cb(struct rpmsg_device * rpdev,void * buf,int len,void * priv,u32 addr)101*4882a593Smuzhiyun static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len,
102*4882a593Smuzhiyun void *priv, u32 addr)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = priv;
105*4882a593Smuzhiyun struct sk_buff *skb;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun skb = alloc_skb(len, GFP_ATOMIC);
108*4882a593Smuzhiyun if (!skb)
109*4882a593Smuzhiyun return -ENOMEM;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun skb_put_data(skb, buf, len);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun spin_lock(&eptdev->queue_lock);
114*4882a593Smuzhiyun skb_queue_tail(&eptdev->queue, skb);
115*4882a593Smuzhiyun spin_unlock(&eptdev->queue_lock);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun /* wake up any blocking processes, waiting for new data */
118*4882a593Smuzhiyun wake_up_interruptible(&eptdev->readq);
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun return 0;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
rpmsg_eptdev_open(struct inode * inode,struct file * filp)123*4882a593Smuzhiyun static int rpmsg_eptdev_open(struct inode *inode, struct file *filp)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev);
126*4882a593Smuzhiyun struct rpmsg_endpoint *ept;
127*4882a593Smuzhiyun struct rpmsg_device *rpdev = eptdev->rpdev;
128*4882a593Smuzhiyun struct device *dev = &eptdev->dev;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun get_device(dev);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
133*4882a593Smuzhiyun if (!ept) {
134*4882a593Smuzhiyun dev_err(dev, "failed to open %s\n", eptdev->chinfo.name);
135*4882a593Smuzhiyun put_device(dev);
136*4882a593Smuzhiyun return -EINVAL;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun eptdev->ept = ept;
140*4882a593Smuzhiyun filp->private_data = eptdev;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun return 0;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
rpmsg_eptdev_release(struct inode * inode,struct file * filp)145*4882a593Smuzhiyun static int rpmsg_eptdev_release(struct inode *inode, struct file *filp)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev);
148*4882a593Smuzhiyun struct device *dev = &eptdev->dev;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun /* Close the endpoint, if it's not already destroyed by the parent */
151*4882a593Smuzhiyun mutex_lock(&eptdev->ept_lock);
152*4882a593Smuzhiyun if (eptdev->ept) {
153*4882a593Smuzhiyun rpmsg_destroy_ept(eptdev->ept);
154*4882a593Smuzhiyun eptdev->ept = NULL;
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun mutex_unlock(&eptdev->ept_lock);
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun /* Discard all SKBs */
159*4882a593Smuzhiyun skb_queue_purge(&eptdev->queue);
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun put_device(dev);
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun return 0;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
rpmsg_eptdev_read_iter(struct kiocb * iocb,struct iov_iter * to)166*4882a593Smuzhiyun static ssize_t rpmsg_eptdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun struct file *filp = iocb->ki_filp;
169*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = filp->private_data;
170*4882a593Smuzhiyun unsigned long flags;
171*4882a593Smuzhiyun struct sk_buff *skb;
172*4882a593Smuzhiyun int use;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun if (!eptdev->ept)
175*4882a593Smuzhiyun return -EPIPE;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun spin_lock_irqsave(&eptdev->queue_lock, flags);
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun /* Wait for data in the queue */
180*4882a593Smuzhiyun if (skb_queue_empty(&eptdev->queue)) {
181*4882a593Smuzhiyun spin_unlock_irqrestore(&eptdev->queue_lock, flags);
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun if (filp->f_flags & O_NONBLOCK)
184*4882a593Smuzhiyun return -EAGAIN;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun /* Wait until we get data or the endpoint goes away */
187*4882a593Smuzhiyun if (wait_event_interruptible(eptdev->readq,
188*4882a593Smuzhiyun !skb_queue_empty(&eptdev->queue) ||
189*4882a593Smuzhiyun !eptdev->ept))
190*4882a593Smuzhiyun return -ERESTARTSYS;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun /* We lost the endpoint while waiting */
193*4882a593Smuzhiyun if (!eptdev->ept)
194*4882a593Smuzhiyun return -EPIPE;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun spin_lock_irqsave(&eptdev->queue_lock, flags);
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun skb = skb_dequeue(&eptdev->queue);
200*4882a593Smuzhiyun spin_unlock_irqrestore(&eptdev->queue_lock, flags);
201*4882a593Smuzhiyun if (!skb)
202*4882a593Smuzhiyun return -EFAULT;
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun use = min_t(size_t, iov_iter_count(to), skb->len);
205*4882a593Smuzhiyun if (copy_to_iter(skb->data, use, to) != use)
206*4882a593Smuzhiyun use = -EFAULT;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun kfree_skb(skb);
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun return use;
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun
rpmsg_eptdev_write_iter(struct kiocb * iocb,struct iov_iter * from)213*4882a593Smuzhiyun static ssize_t rpmsg_eptdev_write_iter(struct kiocb *iocb,
214*4882a593Smuzhiyun struct iov_iter *from)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun struct file *filp = iocb->ki_filp;
217*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = filp->private_data;
218*4882a593Smuzhiyun size_t len = iov_iter_count(from);
219*4882a593Smuzhiyun void *kbuf;
220*4882a593Smuzhiyun int ret;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun kbuf = kzalloc(len, GFP_KERNEL);
223*4882a593Smuzhiyun if (!kbuf)
224*4882a593Smuzhiyun return -ENOMEM;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun if (!copy_from_iter_full(kbuf, len, from)) {
227*4882a593Smuzhiyun ret = -EFAULT;
228*4882a593Smuzhiyun goto free_kbuf;
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun if (mutex_lock_interruptible(&eptdev->ept_lock)) {
232*4882a593Smuzhiyun ret = -ERESTARTSYS;
233*4882a593Smuzhiyun goto free_kbuf;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun if (!eptdev->ept) {
237*4882a593Smuzhiyun ret = -EPIPE;
238*4882a593Smuzhiyun goto unlock_eptdev;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun if (filp->f_flags & O_NONBLOCK)
242*4882a593Smuzhiyun ret = rpmsg_trysend(eptdev->ept, kbuf, len);
243*4882a593Smuzhiyun else
244*4882a593Smuzhiyun ret = rpmsg_send(eptdev->ept, kbuf, len);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun unlock_eptdev:
247*4882a593Smuzhiyun mutex_unlock(&eptdev->ept_lock);
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun free_kbuf:
250*4882a593Smuzhiyun kfree(kbuf);
251*4882a593Smuzhiyun return ret < 0 ? ret : len;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
rpmsg_eptdev_poll(struct file * filp,poll_table * wait)254*4882a593Smuzhiyun static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = filp->private_data;
257*4882a593Smuzhiyun __poll_t mask = 0;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun if (!eptdev->ept)
260*4882a593Smuzhiyun return EPOLLERR;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun poll_wait(filp, &eptdev->readq, wait);
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun if (!skb_queue_empty(&eptdev->queue))
265*4882a593Smuzhiyun mask |= EPOLLIN | EPOLLRDNORM;
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun mask |= rpmsg_poll(eptdev->ept, filp, wait);
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun return mask;
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
rpmsg_eptdev_ioctl(struct file * fp,unsigned int cmd,unsigned long arg)272*4882a593Smuzhiyun static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd,
273*4882a593Smuzhiyun unsigned long arg)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = fp->private_data;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun if (cmd != RPMSG_DESTROY_EPT_IOCTL)
278*4882a593Smuzhiyun return -EINVAL;
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun return rpmsg_eptdev_destroy(&eptdev->dev, NULL);
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun static const struct file_operations rpmsg_eptdev_fops = {
284*4882a593Smuzhiyun .owner = THIS_MODULE,
285*4882a593Smuzhiyun .open = rpmsg_eptdev_open,
286*4882a593Smuzhiyun .release = rpmsg_eptdev_release,
287*4882a593Smuzhiyun .read_iter = rpmsg_eptdev_read_iter,
288*4882a593Smuzhiyun .write_iter = rpmsg_eptdev_write_iter,
289*4882a593Smuzhiyun .poll = rpmsg_eptdev_poll,
290*4882a593Smuzhiyun .unlocked_ioctl = rpmsg_eptdev_ioctl,
291*4882a593Smuzhiyun .compat_ioctl = compat_ptr_ioctl,
292*4882a593Smuzhiyun };
293*4882a593Smuzhiyun
name_show(struct device * dev,struct device_attribute * attr,char * buf)294*4882a593Smuzhiyun static ssize_t name_show(struct device *dev, struct device_attribute *attr,
295*4882a593Smuzhiyun char *buf)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev);
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun return sprintf(buf, "%s\n", eptdev->chinfo.name);
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun static DEVICE_ATTR_RO(name);
302*4882a593Smuzhiyun
src_show(struct device * dev,struct device_attribute * attr,char * buf)303*4882a593Smuzhiyun static ssize_t src_show(struct device *dev, struct device_attribute *attr,
304*4882a593Smuzhiyun char *buf)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev);
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun return sprintf(buf, "%d\n", eptdev->chinfo.src);
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun static DEVICE_ATTR_RO(src);
311*4882a593Smuzhiyun
dst_show(struct device * dev,struct device_attribute * attr,char * buf)312*4882a593Smuzhiyun static ssize_t dst_show(struct device *dev, struct device_attribute *attr,
313*4882a593Smuzhiyun char *buf)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev);
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun return sprintf(buf, "%d\n", eptdev->chinfo.dst);
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun static DEVICE_ATTR_RO(dst);
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun static struct attribute *rpmsg_eptdev_attrs[] = {
322*4882a593Smuzhiyun &dev_attr_name.attr,
323*4882a593Smuzhiyun &dev_attr_src.attr,
324*4882a593Smuzhiyun &dev_attr_dst.attr,
325*4882a593Smuzhiyun NULL
326*4882a593Smuzhiyun };
327*4882a593Smuzhiyun ATTRIBUTE_GROUPS(rpmsg_eptdev);
328*4882a593Smuzhiyun
rpmsg_eptdev_release_device(struct device * dev)329*4882a593Smuzhiyun static void rpmsg_eptdev_release_device(struct device *dev)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev);
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun ida_simple_remove(&rpmsg_ept_ida, dev->id);
334*4882a593Smuzhiyun ida_simple_remove(&rpmsg_minor_ida, MINOR(eptdev->dev.devt));
335*4882a593Smuzhiyun kfree(eptdev);
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun
rpmsg_eptdev_create(struct rpmsg_ctrldev * ctrldev,struct rpmsg_channel_info chinfo)338*4882a593Smuzhiyun static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev,
339*4882a593Smuzhiyun struct rpmsg_channel_info chinfo)
340*4882a593Smuzhiyun {
341*4882a593Smuzhiyun struct rpmsg_device *rpdev = ctrldev->rpdev;
342*4882a593Smuzhiyun struct rpmsg_eptdev *eptdev;
343*4882a593Smuzhiyun struct device *dev;
344*4882a593Smuzhiyun int ret;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL);
347*4882a593Smuzhiyun if (!eptdev)
348*4882a593Smuzhiyun return -ENOMEM;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun dev = &eptdev->dev;
351*4882a593Smuzhiyun eptdev->rpdev = rpdev;
352*4882a593Smuzhiyun eptdev->chinfo = chinfo;
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun mutex_init(&eptdev->ept_lock);
355*4882a593Smuzhiyun spin_lock_init(&eptdev->queue_lock);
356*4882a593Smuzhiyun skb_queue_head_init(&eptdev->queue);
357*4882a593Smuzhiyun init_waitqueue_head(&eptdev->readq);
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun device_initialize(dev);
360*4882a593Smuzhiyun dev->class = rpmsg_class;
361*4882a593Smuzhiyun dev->parent = &ctrldev->dev;
362*4882a593Smuzhiyun dev->groups = rpmsg_eptdev_groups;
363*4882a593Smuzhiyun dev_set_drvdata(dev, eptdev);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun cdev_init(&eptdev->cdev, &rpmsg_eptdev_fops);
366*4882a593Smuzhiyun eptdev->cdev.owner = THIS_MODULE;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
369*4882a593Smuzhiyun if (ret < 0)
370*4882a593Smuzhiyun goto free_eptdev;
371*4882a593Smuzhiyun dev->devt = MKDEV(MAJOR(rpmsg_major), ret);
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun ret = ida_simple_get(&rpmsg_ept_ida, 0, 0, GFP_KERNEL);
374*4882a593Smuzhiyun if (ret < 0)
375*4882a593Smuzhiyun goto free_minor_ida;
376*4882a593Smuzhiyun dev->id = ret;
377*4882a593Smuzhiyun dev_set_name(dev, "rpmsg%d", ret);
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun ret = cdev_device_add(&eptdev->cdev, &eptdev->dev);
380*4882a593Smuzhiyun if (ret)
381*4882a593Smuzhiyun goto free_ept_ida;
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun /* We can now rely on the release function for cleanup */
384*4882a593Smuzhiyun dev->release = rpmsg_eptdev_release_device;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun return ret;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun free_ept_ida:
389*4882a593Smuzhiyun ida_simple_remove(&rpmsg_ept_ida, dev->id);
390*4882a593Smuzhiyun free_minor_ida:
391*4882a593Smuzhiyun ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));
392*4882a593Smuzhiyun free_eptdev:
393*4882a593Smuzhiyun put_device(dev);
394*4882a593Smuzhiyun kfree(eptdev);
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun return ret;
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun
rpmsg_ctrldev_open(struct inode * inode,struct file * filp)399*4882a593Smuzhiyun static int rpmsg_ctrldev_open(struct inode *inode, struct file *filp)
400*4882a593Smuzhiyun {
401*4882a593Smuzhiyun struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev);
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun get_device(&ctrldev->dev);
404*4882a593Smuzhiyun filp->private_data = ctrldev;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun return 0;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun
rpmsg_ctrldev_release(struct inode * inode,struct file * filp)409*4882a593Smuzhiyun static int rpmsg_ctrldev_release(struct inode *inode, struct file *filp)
410*4882a593Smuzhiyun {
411*4882a593Smuzhiyun struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev);
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun put_device(&ctrldev->dev);
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun return 0;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun
rpmsg_ctrldev_ioctl(struct file * fp,unsigned int cmd,unsigned long arg)418*4882a593Smuzhiyun static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd,
419*4882a593Smuzhiyun unsigned long arg)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun struct rpmsg_ctrldev *ctrldev = fp->private_data;
422*4882a593Smuzhiyun void __user *argp = (void __user *)arg;
423*4882a593Smuzhiyun struct rpmsg_endpoint_info eptinfo;
424*4882a593Smuzhiyun struct rpmsg_channel_info chinfo;
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun if (cmd != RPMSG_CREATE_EPT_IOCTL)
427*4882a593Smuzhiyun return -EINVAL;
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun if (copy_from_user(&eptinfo, argp, sizeof(eptinfo)))
430*4882a593Smuzhiyun return -EFAULT;
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun memcpy(chinfo.name, eptinfo.name, RPMSG_NAME_SIZE);
433*4882a593Smuzhiyun chinfo.name[RPMSG_NAME_SIZE-1] = '\0';
434*4882a593Smuzhiyun chinfo.src = eptinfo.src;
435*4882a593Smuzhiyun chinfo.dst = eptinfo.dst;
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun return rpmsg_eptdev_create(ctrldev, chinfo);
438*4882a593Smuzhiyun };
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun static const struct file_operations rpmsg_ctrldev_fops = {
441*4882a593Smuzhiyun .owner = THIS_MODULE,
442*4882a593Smuzhiyun .open = rpmsg_ctrldev_open,
443*4882a593Smuzhiyun .release = rpmsg_ctrldev_release,
444*4882a593Smuzhiyun .unlocked_ioctl = rpmsg_ctrldev_ioctl,
445*4882a593Smuzhiyun .compat_ioctl = compat_ptr_ioctl,
446*4882a593Smuzhiyun };
447*4882a593Smuzhiyun
rpmsg_ctrldev_release_device(struct device * dev)448*4882a593Smuzhiyun static void rpmsg_ctrldev_release_device(struct device *dev)
449*4882a593Smuzhiyun {
450*4882a593Smuzhiyun struct rpmsg_ctrldev *ctrldev = dev_to_ctrldev(dev);
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun ida_simple_remove(&rpmsg_ctrl_ida, dev->id);
453*4882a593Smuzhiyun ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));
454*4882a593Smuzhiyun kfree(ctrldev);
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun
rpmsg_chrdev_probe(struct rpmsg_device * rpdev)457*4882a593Smuzhiyun static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)
458*4882a593Smuzhiyun {
459*4882a593Smuzhiyun struct rpmsg_ctrldev *ctrldev;
460*4882a593Smuzhiyun struct device *dev;
461*4882a593Smuzhiyun int ret;
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL);
464*4882a593Smuzhiyun if (!ctrldev)
465*4882a593Smuzhiyun return -ENOMEM;
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun ctrldev->rpdev = rpdev;
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun dev = &ctrldev->dev;
470*4882a593Smuzhiyun device_initialize(dev);
471*4882a593Smuzhiyun dev->parent = &rpdev->dev;
472*4882a593Smuzhiyun dev->class = rpmsg_class;
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops);
475*4882a593Smuzhiyun ctrldev->cdev.owner = THIS_MODULE;
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
478*4882a593Smuzhiyun if (ret < 0)
479*4882a593Smuzhiyun goto free_ctrldev;
480*4882a593Smuzhiyun dev->devt = MKDEV(MAJOR(rpmsg_major), ret);
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun ret = ida_simple_get(&rpmsg_ctrl_ida, 0, 0, GFP_KERNEL);
483*4882a593Smuzhiyun if (ret < 0)
484*4882a593Smuzhiyun goto free_minor_ida;
485*4882a593Smuzhiyun dev->id = ret;
486*4882a593Smuzhiyun dev_set_name(&ctrldev->dev, "rpmsg_ctrl%d", ret);
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun ret = cdev_device_add(&ctrldev->cdev, &ctrldev->dev);
489*4882a593Smuzhiyun if (ret)
490*4882a593Smuzhiyun goto free_ctrl_ida;
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun /* We can now rely on the release function for cleanup */
493*4882a593Smuzhiyun dev->release = rpmsg_ctrldev_release_device;
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun dev_set_drvdata(&rpdev->dev, ctrldev);
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun return ret;
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun free_ctrl_ida:
500*4882a593Smuzhiyun ida_simple_remove(&rpmsg_ctrl_ida, dev->id);
501*4882a593Smuzhiyun free_minor_ida:
502*4882a593Smuzhiyun ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));
503*4882a593Smuzhiyun free_ctrldev:
504*4882a593Smuzhiyun put_device(dev);
505*4882a593Smuzhiyun kfree(ctrldev);
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun return ret;
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun
rpmsg_chrdev_remove(struct rpmsg_device * rpdev)510*4882a593Smuzhiyun static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun struct rpmsg_ctrldev *ctrldev = dev_get_drvdata(&rpdev->dev);
513*4882a593Smuzhiyun int ret;
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun /* Destroy all endpoints */
516*4882a593Smuzhiyun ret = device_for_each_child(&ctrldev->dev, NULL, rpmsg_eptdev_destroy);
517*4882a593Smuzhiyun if (ret)
518*4882a593Smuzhiyun dev_warn(&rpdev->dev, "failed to nuke endpoints: %d\n", ret);
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun cdev_device_del(&ctrldev->cdev, &ctrldev->dev);
521*4882a593Smuzhiyun put_device(&ctrldev->dev);
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun static struct rpmsg_driver rpmsg_chrdev_driver = {
525*4882a593Smuzhiyun .probe = rpmsg_chrdev_probe,
526*4882a593Smuzhiyun .remove = rpmsg_chrdev_remove,
527*4882a593Smuzhiyun .drv = {
528*4882a593Smuzhiyun .name = "rpmsg_chrdev",
529*4882a593Smuzhiyun },
530*4882a593Smuzhiyun };
531*4882a593Smuzhiyun
rpmsg_char_init(void)532*4882a593Smuzhiyun static int rpmsg_char_init(void)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun int ret;
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg");
537*4882a593Smuzhiyun if (ret < 0) {
538*4882a593Smuzhiyun pr_err("rpmsg: failed to allocate char dev region\n");
539*4882a593Smuzhiyun return ret;
540*4882a593Smuzhiyun }
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun rpmsg_class = class_create(THIS_MODULE, "rpmsg");
543*4882a593Smuzhiyun if (IS_ERR(rpmsg_class)) {
544*4882a593Smuzhiyun pr_err("failed to create rpmsg class\n");
545*4882a593Smuzhiyun unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
546*4882a593Smuzhiyun return PTR_ERR(rpmsg_class);
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun ret = register_rpmsg_driver(&rpmsg_chrdev_driver);
550*4882a593Smuzhiyun if (ret < 0) {
551*4882a593Smuzhiyun pr_err("rpmsgchr: failed to register rpmsg driver\n");
552*4882a593Smuzhiyun class_destroy(rpmsg_class);
553*4882a593Smuzhiyun unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun return ret;
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun postcore_initcall(rpmsg_char_init);
559*4882a593Smuzhiyun
rpmsg_chrdev_exit(void)560*4882a593Smuzhiyun static void rpmsg_chrdev_exit(void)
561*4882a593Smuzhiyun {
562*4882a593Smuzhiyun unregister_rpmsg_driver(&rpmsg_chrdev_driver);
563*4882a593Smuzhiyun class_destroy(rpmsg_class);
564*4882a593Smuzhiyun unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun module_exit(rpmsg_chrdev_exit);
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun MODULE_ALIAS("rpmsg:rpmsg_chrdev");
569*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
570