1136200f4SSuman Anna // SPDX-License-Identifier: GPL-2.0
2c0cdc19fSBjorn Andersson /*
369265bc1SArnaud Pouliquen * Copyright (C) 2022, STMicroelectronics
4c0cdc19fSBjorn Andersson * Copyright (c) 2016, Linaro Ltd.
5c0cdc19fSBjorn Andersson * Copyright (c) 2012, Michal Simek <monstr@monstr.eu>
6c0cdc19fSBjorn Andersson * Copyright (c) 2012, PetaLogix
7c0cdc19fSBjorn Andersson * Copyright (c) 2011, Texas Instruments, Inc.
8c0cdc19fSBjorn Andersson * Copyright (c) 2011, Google, Inc.
9c0cdc19fSBjorn Andersson *
10c0cdc19fSBjorn Andersson * Based on rpmsg performance statistics driver by Michal Simek, which in turn
11c0cdc19fSBjorn Andersson * was based on TI & Google OMX rpmsg driver.
12c0cdc19fSBjorn Andersson */
13c5727244SArnaud Pouliquen
14c5727244SArnaud Pouliquen #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15c5727244SArnaud Pouliquen
16c0cdc19fSBjorn Andersson #include <linux/cdev.h>
17c0cdc19fSBjorn Andersson #include <linux/device.h>
18c0cdc19fSBjorn Andersson #include <linux/fs.h>
19c0cdc19fSBjorn Andersson #include <linux/idr.h>
20c0cdc19fSBjorn Andersson #include <linux/kernel.h>
21c0cdc19fSBjorn Andersson #include <linux/module.h>
22c0cdc19fSBjorn Andersson #include <linux/poll.h>
23c0cdc19fSBjorn Andersson #include <linux/rpmsg.h>
24c0cdc19fSBjorn Andersson #include <linux/skbuff.h>
25c0cdc19fSBjorn Andersson #include <linux/slab.h>
26c0cdc19fSBjorn Andersson #include <linux/uaccess.h>
27c0cdc19fSBjorn Andersson #include <uapi/linux/rpmsg.h>
28c0cdc19fSBjorn Andersson
2969265bc1SArnaud Pouliquen #include "rpmsg_char.h"
30608edd96SArnaud Pouliquen #include "rpmsg_internal.h"
3169265bc1SArnaud Pouliquen
32c0cdc19fSBjorn Andersson #define RPMSG_DEV_MAX (MINORMASK + 1)
33c0cdc19fSBjorn Andersson
34c0cdc19fSBjorn Andersson static dev_t rpmsg_major;
35c0cdc19fSBjorn Andersson
36c0cdc19fSBjorn Andersson static DEFINE_IDA(rpmsg_ept_ida);
37c0cdc19fSBjorn Andersson static DEFINE_IDA(rpmsg_minor_ida);
38c0cdc19fSBjorn Andersson
39c0cdc19fSBjorn Andersson #define dev_to_eptdev(dev) container_of(dev, struct rpmsg_eptdev, dev)
40c0cdc19fSBjorn Andersson #define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_eptdev, cdev)
41c0cdc19fSBjorn Andersson
42c0cdc19fSBjorn Andersson /**
43c0cdc19fSBjorn Andersson * struct rpmsg_eptdev - endpoint device context
44c0cdc19fSBjorn Andersson * @dev: endpoint device
45c0cdc19fSBjorn Andersson * @cdev: cdev for the endpoint device
46c0cdc19fSBjorn Andersson * @rpdev: underlaying rpmsg device
47c0cdc19fSBjorn Andersson * @chinfo: info used to open the endpoint
48c0cdc19fSBjorn Andersson * @ept_lock: synchronization of @ept modifications
49c0cdc19fSBjorn Andersson * @ept: rpmsg endpoint reference, when open
50c0cdc19fSBjorn Andersson * @queue_lock: synchronization of @queue operations
51c0cdc19fSBjorn Andersson * @queue: incoming message queue
52c0cdc19fSBjorn Andersson * @readq: wait object for incoming queue
53bea9b79cSArnaud Pouliquen * @default_ept: set to channel default endpoint if the default endpoint should be re-used
54bea9b79cSArnaud Pouliquen * on device open to prevent endpoint address update.
55*5550201cSChris Lew * remote_flow_restricted: to indicate if the remote has requested for flow to be limited
56*5550201cSChris Lew * remote_flow_updated: to indicate if the flow control has been requested
57c0cdc19fSBjorn Andersson */
58c0cdc19fSBjorn Andersson struct rpmsg_eptdev {
59c0cdc19fSBjorn Andersson struct device dev;
60c0cdc19fSBjorn Andersson struct cdev cdev;
61c0cdc19fSBjorn Andersson
62c0cdc19fSBjorn Andersson struct rpmsg_device *rpdev;
63c0cdc19fSBjorn Andersson struct rpmsg_channel_info chinfo;
64c0cdc19fSBjorn Andersson
65c0cdc19fSBjorn Andersson struct mutex ept_lock;
66c0cdc19fSBjorn Andersson struct rpmsg_endpoint *ept;
67bea9b79cSArnaud Pouliquen struct rpmsg_endpoint *default_ept;
68c0cdc19fSBjorn Andersson
69c0cdc19fSBjorn Andersson spinlock_t queue_lock;
70c0cdc19fSBjorn Andersson struct sk_buff_head queue;
71c0cdc19fSBjorn Andersson wait_queue_head_t readq;
72bea9b79cSArnaud Pouliquen
73*5550201cSChris Lew bool remote_flow_restricted;
74*5550201cSChris Lew bool remote_flow_updated;
75c0cdc19fSBjorn Andersson };
76c0cdc19fSBjorn Andersson
rpmsg_chrdev_eptdev_destroy(struct device * dev,void * data)7769265bc1SArnaud Pouliquen int rpmsg_chrdev_eptdev_destroy(struct device *dev, void *data)
78c0cdc19fSBjorn Andersson {
79c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev);
80c0cdc19fSBjorn Andersson
81c0cdc19fSBjorn Andersson mutex_lock(&eptdev->ept_lock);
8217b88a20SDeepak Kumar Singh eptdev->rpdev = NULL;
83c0cdc19fSBjorn Andersson if (eptdev->ept) {
84467233a4SShengjiu Wang /* The default endpoint is released by the rpmsg core */
85467233a4SShengjiu Wang if (!eptdev->default_ept)
86c0cdc19fSBjorn Andersson rpmsg_destroy_ept(eptdev->ept);
87c0cdc19fSBjorn Andersson eptdev->ept = NULL;
88c0cdc19fSBjorn Andersson }
89c0cdc19fSBjorn Andersson mutex_unlock(&eptdev->ept_lock);
90c0cdc19fSBjorn Andersson
91c0cdc19fSBjorn Andersson /* wake up any blocked readers */
92c0cdc19fSBjorn Andersson wake_up_interruptible(&eptdev->readq);
93c0cdc19fSBjorn Andersson
947a534ae8SMatthias Kaehlcke cdev_device_del(&eptdev->cdev, &eptdev->dev);
95c0cdc19fSBjorn Andersson put_device(&eptdev->dev);
96c0cdc19fSBjorn Andersson
97c0cdc19fSBjorn Andersson return 0;
98c0cdc19fSBjorn Andersson }
9969265bc1SArnaud Pouliquen EXPORT_SYMBOL(rpmsg_chrdev_eptdev_destroy);
100c0cdc19fSBjorn Andersson
rpmsg_ept_cb(struct rpmsg_device * rpdev,void * buf,int len,void * priv,u32 addr)101c0cdc19fSBjorn Andersson static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len,
102c0cdc19fSBjorn Andersson void *priv, u32 addr)
103c0cdc19fSBjorn Andersson {
104c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = priv;
105c0cdc19fSBjorn Andersson struct sk_buff *skb;
106c0cdc19fSBjorn Andersson
107c0cdc19fSBjorn Andersson skb = alloc_skb(len, GFP_ATOMIC);
108c0cdc19fSBjorn Andersson if (!skb)
109c0cdc19fSBjorn Andersson return -ENOMEM;
110c0cdc19fSBjorn Andersson
11159ae1d12SJohannes Berg skb_put_data(skb, buf, len);
112c0cdc19fSBjorn Andersson
113c0cdc19fSBjorn Andersson spin_lock(&eptdev->queue_lock);
114c0cdc19fSBjorn Andersson skb_queue_tail(&eptdev->queue, skb);
115c0cdc19fSBjorn Andersson spin_unlock(&eptdev->queue_lock);
116c0cdc19fSBjorn Andersson
117c0cdc19fSBjorn Andersson /* wake up any blocking processes, waiting for new data */
118c0cdc19fSBjorn Andersson wake_up_interruptible(&eptdev->readq);
119c0cdc19fSBjorn Andersson
120c0cdc19fSBjorn Andersson return 0;
121c0cdc19fSBjorn Andersson }
122c0cdc19fSBjorn Andersson
rpmsg_ept_flow_cb(struct rpmsg_device * rpdev,void * priv,bool enable)123*5550201cSChris Lew static int rpmsg_ept_flow_cb(struct rpmsg_device *rpdev, void *priv, bool enable)
124*5550201cSChris Lew {
125*5550201cSChris Lew struct rpmsg_eptdev *eptdev = priv;
126*5550201cSChris Lew
127*5550201cSChris Lew eptdev->remote_flow_restricted = enable;
128*5550201cSChris Lew eptdev->remote_flow_updated = true;
129*5550201cSChris Lew
130*5550201cSChris Lew wake_up_interruptible(&eptdev->readq);
131*5550201cSChris Lew
132*5550201cSChris Lew return 0;
133*5550201cSChris Lew }
134*5550201cSChris Lew
rpmsg_eptdev_open(struct inode * inode,struct file * filp)135c0cdc19fSBjorn Andersson static int rpmsg_eptdev_open(struct inode *inode, struct file *filp)
136c0cdc19fSBjorn Andersson {
137c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev);
138c0cdc19fSBjorn Andersson struct rpmsg_endpoint *ept;
139c0cdc19fSBjorn Andersson struct rpmsg_device *rpdev = eptdev->rpdev;
140c0cdc19fSBjorn Andersson struct device *dev = &eptdev->dev;
141c0cdc19fSBjorn Andersson
142abe13e9aSShengjiu Wang mutex_lock(&eptdev->ept_lock);
143abe13e9aSShengjiu Wang if (eptdev->ept) {
144abe13e9aSShengjiu Wang mutex_unlock(&eptdev->ept_lock);
145964e8bedSArnaud Pouliquen return -EBUSY;
146abe13e9aSShengjiu Wang }
147964e8bedSArnaud Pouliquen
14817b88a20SDeepak Kumar Singh if (!eptdev->rpdev) {
14917b88a20SDeepak Kumar Singh mutex_unlock(&eptdev->ept_lock);
15017b88a20SDeepak Kumar Singh return -ENETRESET;
15117b88a20SDeepak Kumar Singh }
15217b88a20SDeepak Kumar Singh
153c0cdc19fSBjorn Andersson get_device(dev);
154c0cdc19fSBjorn Andersson
155bea9b79cSArnaud Pouliquen /*
156bea9b79cSArnaud Pouliquen * If the default_ept is set, the rpmsg device default endpoint is used.
157bea9b79cSArnaud Pouliquen * Else a new endpoint is created on open that will be destroyed on release.
158bea9b79cSArnaud Pouliquen */
159bea9b79cSArnaud Pouliquen if (eptdev->default_ept)
160bea9b79cSArnaud Pouliquen ept = eptdev->default_ept;
161bea9b79cSArnaud Pouliquen else
162c0cdc19fSBjorn Andersson ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
163bea9b79cSArnaud Pouliquen
164c0cdc19fSBjorn Andersson if (!ept) {
165c0cdc19fSBjorn Andersson dev_err(dev, "failed to open %s\n", eptdev->chinfo.name);
166c0cdc19fSBjorn Andersson put_device(dev);
167abe13e9aSShengjiu Wang mutex_unlock(&eptdev->ept_lock);
168c0cdc19fSBjorn Andersson return -EINVAL;
169c0cdc19fSBjorn Andersson }
170c0cdc19fSBjorn Andersson
171*5550201cSChris Lew ept->flow_cb = rpmsg_ept_flow_cb;
172c0cdc19fSBjorn Andersson eptdev->ept = ept;
173c0cdc19fSBjorn Andersson filp->private_data = eptdev;
174abe13e9aSShengjiu Wang mutex_unlock(&eptdev->ept_lock);
175c0cdc19fSBjorn Andersson
176c0cdc19fSBjorn Andersson return 0;
177c0cdc19fSBjorn Andersson }
178c0cdc19fSBjorn Andersson
rpmsg_eptdev_release(struct inode * inode,struct file * filp)179c0cdc19fSBjorn Andersson static int rpmsg_eptdev_release(struct inode *inode, struct file *filp)
180c0cdc19fSBjorn Andersson {
181c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev);
182c0cdc19fSBjorn Andersson struct device *dev = &eptdev->dev;
183c0cdc19fSBjorn Andersson
184c0cdc19fSBjorn Andersson /* Close the endpoint, if it's not already destroyed by the parent */
185c0cdc19fSBjorn Andersson mutex_lock(&eptdev->ept_lock);
186c0cdc19fSBjorn Andersson if (eptdev->ept) {
187bea9b79cSArnaud Pouliquen if (!eptdev->default_ept)
188c0cdc19fSBjorn Andersson rpmsg_destroy_ept(eptdev->ept);
189c0cdc19fSBjorn Andersson eptdev->ept = NULL;
190c0cdc19fSBjorn Andersson }
191c0cdc19fSBjorn Andersson mutex_unlock(&eptdev->ept_lock);
192*5550201cSChris Lew eptdev->remote_flow_updated = false;
193c0cdc19fSBjorn Andersson
194c0cdc19fSBjorn Andersson /* Discard all SKBs */
195bb06a5ceSChristophe JAILLET skb_queue_purge(&eptdev->queue);
196c0cdc19fSBjorn Andersson
197c0cdc19fSBjorn Andersson put_device(dev);
198c0cdc19fSBjorn Andersson
199c0cdc19fSBjorn Andersson return 0;
200c0cdc19fSBjorn Andersson }
201c0cdc19fSBjorn Andersson
rpmsg_eptdev_read_iter(struct kiocb * iocb,struct iov_iter * to)202ccf45b18SBjorn Andersson static ssize_t rpmsg_eptdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
203c0cdc19fSBjorn Andersson {
204ccf45b18SBjorn Andersson struct file *filp = iocb->ki_filp;
205c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = filp->private_data;
206c0cdc19fSBjorn Andersson unsigned long flags;
207c0cdc19fSBjorn Andersson struct sk_buff *skb;
208c0cdc19fSBjorn Andersson int use;
209c0cdc19fSBjorn Andersson
210c0cdc19fSBjorn Andersson if (!eptdev->ept)
211c0cdc19fSBjorn Andersson return -EPIPE;
212c0cdc19fSBjorn Andersson
213c0cdc19fSBjorn Andersson spin_lock_irqsave(&eptdev->queue_lock, flags);
214c0cdc19fSBjorn Andersson
215c0cdc19fSBjorn Andersson /* Wait for data in the queue */
216c0cdc19fSBjorn Andersson if (skb_queue_empty(&eptdev->queue)) {
217c0cdc19fSBjorn Andersson spin_unlock_irqrestore(&eptdev->queue_lock, flags);
218c0cdc19fSBjorn Andersson
219c0cdc19fSBjorn Andersson if (filp->f_flags & O_NONBLOCK)
220c0cdc19fSBjorn Andersson return -EAGAIN;
221c0cdc19fSBjorn Andersson
222c0cdc19fSBjorn Andersson /* Wait until we get data or the endpoint goes away */
223c0cdc19fSBjorn Andersson if (wait_event_interruptible(eptdev->readq,
224c0cdc19fSBjorn Andersson !skb_queue_empty(&eptdev->queue) ||
225c0cdc19fSBjorn Andersson !eptdev->ept))
226c0cdc19fSBjorn Andersson return -ERESTARTSYS;
227c0cdc19fSBjorn Andersson
228c0cdc19fSBjorn Andersson /* We lost the endpoint while waiting */
229c0cdc19fSBjorn Andersson if (!eptdev->ept)
230c0cdc19fSBjorn Andersson return -EPIPE;
231c0cdc19fSBjorn Andersson
232c0cdc19fSBjorn Andersson spin_lock_irqsave(&eptdev->queue_lock, flags);
233c0cdc19fSBjorn Andersson }
234c0cdc19fSBjorn Andersson
235c0cdc19fSBjorn Andersson skb = skb_dequeue(&eptdev->queue);
2360abd6bddSDan Carpenter spin_unlock_irqrestore(&eptdev->queue_lock, flags);
237c0cdc19fSBjorn Andersson if (!skb)
238c0cdc19fSBjorn Andersson return -EFAULT;
239c0cdc19fSBjorn Andersson
240ccf45b18SBjorn Andersson use = min_t(size_t, iov_iter_count(to), skb->len);
241ccf45b18SBjorn Andersson if (copy_to_iter(skb->data, use, to) != use)
242c0cdc19fSBjorn Andersson use = -EFAULT;
243c0cdc19fSBjorn Andersson
244c0cdc19fSBjorn Andersson kfree_skb(skb);
245c0cdc19fSBjorn Andersson
246c0cdc19fSBjorn Andersson return use;
247c0cdc19fSBjorn Andersson }
248c0cdc19fSBjorn Andersson
rpmsg_eptdev_write_iter(struct kiocb * iocb,struct iov_iter * from)249ccf45b18SBjorn Andersson static ssize_t rpmsg_eptdev_write_iter(struct kiocb *iocb,
250ccf45b18SBjorn Andersson struct iov_iter *from)
251c0cdc19fSBjorn Andersson {
252ccf45b18SBjorn Andersson struct file *filp = iocb->ki_filp;
253c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = filp->private_data;
254ccf45b18SBjorn Andersson size_t len = iov_iter_count(from);
255c0cdc19fSBjorn Andersson void *kbuf;
256c0cdc19fSBjorn Andersson int ret;
257c0cdc19fSBjorn Andersson
258ccf45b18SBjorn Andersson kbuf = kzalloc(len, GFP_KERNEL);
259ccf45b18SBjorn Andersson if (!kbuf)
260ccf45b18SBjorn Andersson return -ENOMEM;
261ccf45b18SBjorn Andersson
262bbe692e3SNavid Emamdoost if (!copy_from_iter_full(kbuf, len, from)) {
263bbe692e3SNavid Emamdoost ret = -EFAULT;
264bbe692e3SNavid Emamdoost goto free_kbuf;
265bbe692e3SNavid Emamdoost }
266c0cdc19fSBjorn Andersson
267c0cdc19fSBjorn Andersson if (mutex_lock_interruptible(&eptdev->ept_lock)) {
268c0cdc19fSBjorn Andersson ret = -ERESTARTSYS;
269c0cdc19fSBjorn Andersson goto free_kbuf;
270c0cdc19fSBjorn Andersson }
271c0cdc19fSBjorn Andersson
272c0cdc19fSBjorn Andersson if (!eptdev->ept) {
273c0cdc19fSBjorn Andersson ret = -EPIPE;
274c0cdc19fSBjorn Andersson goto unlock_eptdev;
275c0cdc19fSBjorn Andersson }
276c0cdc19fSBjorn Andersson
277cbf58250STim Blechmann if (filp->f_flags & O_NONBLOCK) {
278b4ce7e2eSArnaud Pouliquen ret = rpmsg_trysendto(eptdev->ept, kbuf, len, eptdev->chinfo.dst);
279cbf58250STim Blechmann if (ret == -ENOMEM)
280cbf58250STim Blechmann ret = -EAGAIN;
281cbf58250STim Blechmann } else {
282b4ce7e2eSArnaud Pouliquen ret = rpmsg_sendto(eptdev->ept, kbuf, len, eptdev->chinfo.dst);
283cbf58250STim Blechmann }
284c0cdc19fSBjorn Andersson
285c0cdc19fSBjorn Andersson unlock_eptdev:
286c0cdc19fSBjorn Andersson mutex_unlock(&eptdev->ept_lock);
287c0cdc19fSBjorn Andersson
288c0cdc19fSBjorn Andersson free_kbuf:
289c0cdc19fSBjorn Andersson kfree(kbuf);
290c0cdc19fSBjorn Andersson return ret < 0 ? ret : len;
291c0cdc19fSBjorn Andersson }
292c0cdc19fSBjorn Andersson
rpmsg_eptdev_poll(struct file * filp,poll_table * wait)293afc9a42bSAl Viro static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait)
294c0cdc19fSBjorn Andersson {
295c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = filp->private_data;
296afc9a42bSAl Viro __poll_t mask = 0;
297c0cdc19fSBjorn Andersson
298c0cdc19fSBjorn Andersson if (!eptdev->ept)
299a9a08845SLinus Torvalds return EPOLLERR;
300c0cdc19fSBjorn Andersson
301c0cdc19fSBjorn Andersson poll_wait(filp, &eptdev->readq, wait);
302c0cdc19fSBjorn Andersson
303c0cdc19fSBjorn Andersson if (!skb_queue_empty(&eptdev->queue))
304a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
305c0cdc19fSBjorn Andersson
306*5550201cSChris Lew if (eptdev->remote_flow_updated)
307*5550201cSChris Lew mask |= EPOLLPRI;
308*5550201cSChris Lew
30917b88a20SDeepak Kumar Singh mutex_lock(&eptdev->ept_lock);
310c0cdc19fSBjorn Andersson mask |= rpmsg_poll(eptdev->ept, filp, wait);
31117b88a20SDeepak Kumar Singh mutex_unlock(&eptdev->ept_lock);
312c0cdc19fSBjorn Andersson
313c0cdc19fSBjorn Andersson return mask;
314c0cdc19fSBjorn Andersson }
315c0cdc19fSBjorn Andersson
rpmsg_eptdev_ioctl(struct file * fp,unsigned int cmd,unsigned long arg)316c0cdc19fSBjorn Andersson static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd,
317c0cdc19fSBjorn Andersson unsigned long arg)
318c0cdc19fSBjorn Andersson {
319c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = fp->private_data;
320c0cdc19fSBjorn Andersson
321*5550201cSChris Lew bool set;
322*5550201cSChris Lew int ret;
323c0cdc19fSBjorn Andersson
324*5550201cSChris Lew switch (cmd) {
325*5550201cSChris Lew case RPMSG_GET_OUTGOING_FLOWCONTROL:
326*5550201cSChris Lew eptdev->remote_flow_updated = false;
327*5550201cSChris Lew ret = put_user(eptdev->remote_flow_restricted, (int __user *)arg);
328*5550201cSChris Lew break;
329*5550201cSChris Lew case RPMSG_SET_INCOMING_FLOWCONTROL:
330*5550201cSChris Lew if (arg > 1) {
331*5550201cSChris Lew ret = -EINVAL;
332*5550201cSChris Lew break;
333*5550201cSChris Lew }
334*5550201cSChris Lew set = !!arg;
335*5550201cSChris Lew ret = rpmsg_set_flow_control(eptdev->ept, set, eptdev->chinfo.dst);
336*5550201cSChris Lew break;
337*5550201cSChris Lew case RPMSG_DESTROY_EPT_IOCTL:
338bea9b79cSArnaud Pouliquen /* Don't allow to destroy a default endpoint. */
339*5550201cSChris Lew if (eptdev->default_ept) {
340*5550201cSChris Lew ret = -EINVAL;
341*5550201cSChris Lew break;
342*5550201cSChris Lew }
343*5550201cSChris Lew ret = rpmsg_chrdev_eptdev_destroy(&eptdev->dev, NULL);
344*5550201cSChris Lew break;
345*5550201cSChris Lew default:
346*5550201cSChris Lew ret = -EINVAL;
347*5550201cSChris Lew }
348bea9b79cSArnaud Pouliquen
349*5550201cSChris Lew return ret;
350c0cdc19fSBjorn Andersson }
351c0cdc19fSBjorn Andersson
352c0cdc19fSBjorn Andersson static const struct file_operations rpmsg_eptdev_fops = {
353c0cdc19fSBjorn Andersson .owner = THIS_MODULE,
354c0cdc19fSBjorn Andersson .open = rpmsg_eptdev_open,
355c0cdc19fSBjorn Andersson .release = rpmsg_eptdev_release,
356ccf45b18SBjorn Andersson .read_iter = rpmsg_eptdev_read_iter,
357ccf45b18SBjorn Andersson .write_iter = rpmsg_eptdev_write_iter,
358c0cdc19fSBjorn Andersson .poll = rpmsg_eptdev_poll,
359c0cdc19fSBjorn Andersson .unlocked_ioctl = rpmsg_eptdev_ioctl,
3601832f2d8SArnd Bergmann .compat_ioctl = compat_ptr_ioctl,
361c0cdc19fSBjorn Andersson };
362c0cdc19fSBjorn Andersson
name_show(struct device * dev,struct device_attribute * attr,char * buf)363c0cdc19fSBjorn Andersson static ssize_t name_show(struct device *dev, struct device_attribute *attr,
364c0cdc19fSBjorn Andersson char *buf)
365c0cdc19fSBjorn Andersson {
366c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev);
367c0cdc19fSBjorn Andersson
368c0cdc19fSBjorn Andersson return sprintf(buf, "%s\n", eptdev->chinfo.name);
369c0cdc19fSBjorn Andersson }
370c0cdc19fSBjorn Andersson static DEVICE_ATTR_RO(name);
371c0cdc19fSBjorn Andersson
src_show(struct device * dev,struct device_attribute * attr,char * buf)372c0cdc19fSBjorn Andersson static ssize_t src_show(struct device *dev, struct device_attribute *attr,
373c0cdc19fSBjorn Andersson char *buf)
374c0cdc19fSBjorn Andersson {
375c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev);
376c0cdc19fSBjorn Andersson
377c0cdc19fSBjorn Andersson return sprintf(buf, "%d\n", eptdev->chinfo.src);
378c0cdc19fSBjorn Andersson }
379c0cdc19fSBjorn Andersson static DEVICE_ATTR_RO(src);
380c0cdc19fSBjorn Andersson
dst_show(struct device * dev,struct device_attribute * attr,char * buf)381c0cdc19fSBjorn Andersson static ssize_t dst_show(struct device *dev, struct device_attribute *attr,
382c0cdc19fSBjorn Andersson char *buf)
383c0cdc19fSBjorn Andersson {
384c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev);
385c0cdc19fSBjorn Andersson
386c0cdc19fSBjorn Andersson return sprintf(buf, "%d\n", eptdev->chinfo.dst);
387c0cdc19fSBjorn Andersson }
388c0cdc19fSBjorn Andersson static DEVICE_ATTR_RO(dst);
389c0cdc19fSBjorn Andersson
390c0cdc19fSBjorn Andersson static struct attribute *rpmsg_eptdev_attrs[] = {
391c0cdc19fSBjorn Andersson &dev_attr_name.attr,
392c0cdc19fSBjorn Andersson &dev_attr_src.attr,
393c0cdc19fSBjorn Andersson &dev_attr_dst.attr,
394c0cdc19fSBjorn Andersson NULL
395c0cdc19fSBjorn Andersson };
396c0cdc19fSBjorn Andersson ATTRIBUTE_GROUPS(rpmsg_eptdev);
397c0cdc19fSBjorn Andersson
rpmsg_eptdev_release_device(struct device * dev)398c0cdc19fSBjorn Andersson static void rpmsg_eptdev_release_device(struct device *dev)
399c0cdc19fSBjorn Andersson {
400c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev);
401c0cdc19fSBjorn Andersson
402c0cdc19fSBjorn Andersson ida_simple_remove(&rpmsg_ept_ida, dev->id);
403c0cdc19fSBjorn Andersson ida_simple_remove(&rpmsg_minor_ida, MINOR(eptdev->dev.devt));
404c0cdc19fSBjorn Andersson kfree(eptdev);
405c0cdc19fSBjorn Andersson }
406c0cdc19fSBjorn Andersson
rpmsg_chrdev_eptdev_alloc(struct rpmsg_device * rpdev,struct device * parent)407cc9da7deSArnaud Pouliquen static struct rpmsg_eptdev *rpmsg_chrdev_eptdev_alloc(struct rpmsg_device *rpdev,
408cc9da7deSArnaud Pouliquen struct device *parent)
409c0cdc19fSBjorn Andersson {
410c0cdc19fSBjorn Andersson struct rpmsg_eptdev *eptdev;
411c0cdc19fSBjorn Andersson struct device *dev;
412c0cdc19fSBjorn Andersson
413c0cdc19fSBjorn Andersson eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL);
414c0cdc19fSBjorn Andersson if (!eptdev)
415cc9da7deSArnaud Pouliquen return ERR_PTR(-ENOMEM);
416c0cdc19fSBjorn Andersson
417c0cdc19fSBjorn Andersson dev = &eptdev->dev;
418c0cdc19fSBjorn Andersson eptdev->rpdev = rpdev;
419c0cdc19fSBjorn Andersson
420c0cdc19fSBjorn Andersson mutex_init(&eptdev->ept_lock);
421c0cdc19fSBjorn Andersson spin_lock_init(&eptdev->queue_lock);
422c0cdc19fSBjorn Andersson skb_queue_head_init(&eptdev->queue);
423c0cdc19fSBjorn Andersson init_waitqueue_head(&eptdev->readq);
424c0cdc19fSBjorn Andersson
425c0cdc19fSBjorn Andersson device_initialize(dev);
426c0cdc19fSBjorn Andersson dev->class = rpmsg_class;
42769265bc1SArnaud Pouliquen dev->parent = parent;
428c0cdc19fSBjorn Andersson dev->groups = rpmsg_eptdev_groups;
429c0cdc19fSBjorn Andersson dev_set_drvdata(dev, eptdev);
430c0cdc19fSBjorn Andersson
431c0cdc19fSBjorn Andersson cdev_init(&eptdev->cdev, &rpmsg_eptdev_fops);
432c0cdc19fSBjorn Andersson eptdev->cdev.owner = THIS_MODULE;
433c0cdc19fSBjorn Andersson
434cc9da7deSArnaud Pouliquen return eptdev;
435cc9da7deSArnaud Pouliquen }
436cc9da7deSArnaud Pouliquen
rpmsg_chrdev_eptdev_add(struct rpmsg_eptdev * eptdev,struct rpmsg_channel_info chinfo)437cc9da7deSArnaud Pouliquen static int rpmsg_chrdev_eptdev_add(struct rpmsg_eptdev *eptdev, struct rpmsg_channel_info chinfo)
438cc9da7deSArnaud Pouliquen {
439cc9da7deSArnaud Pouliquen struct device *dev = &eptdev->dev;
440cc9da7deSArnaud Pouliquen int ret;
441cc9da7deSArnaud Pouliquen
442cc9da7deSArnaud Pouliquen eptdev->chinfo = chinfo;
443cc9da7deSArnaud Pouliquen
444c0cdc19fSBjorn Andersson ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
445c0cdc19fSBjorn Andersson if (ret < 0)
446c0cdc19fSBjorn Andersson goto free_eptdev;
447c0cdc19fSBjorn Andersson dev->devt = MKDEV(MAJOR(rpmsg_major), ret);
448c0cdc19fSBjorn Andersson
449c0cdc19fSBjorn Andersson ret = ida_simple_get(&rpmsg_ept_ida, 0, 0, GFP_KERNEL);
450c0cdc19fSBjorn Andersson if (ret < 0)
451c0cdc19fSBjorn Andersson goto free_minor_ida;
452c0cdc19fSBjorn Andersson dev->id = ret;
453c0cdc19fSBjorn Andersson dev_set_name(dev, "rpmsg%d", ret);
454c0cdc19fSBjorn Andersson
4557a534ae8SMatthias Kaehlcke ret = cdev_device_add(&eptdev->cdev, &eptdev->dev);
456c0cdc19fSBjorn Andersson if (ret)
457c0cdc19fSBjorn Andersson goto free_ept_ida;
458c0cdc19fSBjorn Andersson
459c0cdc19fSBjorn Andersson /* We can now rely on the release function for cleanup */
460c0cdc19fSBjorn Andersson dev->release = rpmsg_eptdev_release_device;
461c0cdc19fSBjorn Andersson
462c0cdc19fSBjorn Andersson return ret;
463c0cdc19fSBjorn Andersson
464c0cdc19fSBjorn Andersson free_ept_ida:
465c0cdc19fSBjorn Andersson ida_simple_remove(&rpmsg_ept_ida, dev->id);
466c0cdc19fSBjorn Andersson free_minor_ida:
467c0cdc19fSBjorn Andersson ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));
468c0cdc19fSBjorn Andersson free_eptdev:
469c0cdc19fSBjorn Andersson put_device(dev);
470c0cdc19fSBjorn Andersson kfree(eptdev);
471c0cdc19fSBjorn Andersson
472c0cdc19fSBjorn Andersson return ret;
473c0cdc19fSBjorn Andersson }
474cc9da7deSArnaud Pouliquen
rpmsg_chrdev_eptdev_create(struct rpmsg_device * rpdev,struct device * parent,struct rpmsg_channel_info chinfo)475cc9da7deSArnaud Pouliquen int rpmsg_chrdev_eptdev_create(struct rpmsg_device *rpdev, struct device *parent,
476cc9da7deSArnaud Pouliquen struct rpmsg_channel_info chinfo)
477cc9da7deSArnaud Pouliquen {
478cc9da7deSArnaud Pouliquen struct rpmsg_eptdev *eptdev;
479cc9da7deSArnaud Pouliquen
480cc9da7deSArnaud Pouliquen eptdev = rpmsg_chrdev_eptdev_alloc(rpdev, parent);
481cc9da7deSArnaud Pouliquen if (IS_ERR(eptdev))
482cc9da7deSArnaud Pouliquen return PTR_ERR(eptdev);
483cc9da7deSArnaud Pouliquen
48406564be4Sye xingchen return rpmsg_chrdev_eptdev_add(eptdev, chinfo);
485cc9da7deSArnaud Pouliquen }
48669265bc1SArnaud Pouliquen EXPORT_SYMBOL(rpmsg_chrdev_eptdev_create);
487c0cdc19fSBjorn Andersson
rpmsg_chrdev_probe(struct rpmsg_device * rpdev)488bc69d106SArnaud Pouliquen static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)
489bc69d106SArnaud Pouliquen {
490bc69d106SArnaud Pouliquen struct rpmsg_channel_info chinfo;
491bc69d106SArnaud Pouliquen struct rpmsg_eptdev *eptdev;
492bc69d106SArnaud Pouliquen struct device *dev = &rpdev->dev;
493bc69d106SArnaud Pouliquen
494bc69d106SArnaud Pouliquen memcpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);
495bc69d106SArnaud Pouliquen chinfo.src = rpdev->src;
496bc69d106SArnaud Pouliquen chinfo.dst = rpdev->dst;
497bc69d106SArnaud Pouliquen
498bc69d106SArnaud Pouliquen eptdev = rpmsg_chrdev_eptdev_alloc(rpdev, dev);
499bc69d106SArnaud Pouliquen if (IS_ERR(eptdev))
500bc69d106SArnaud Pouliquen return PTR_ERR(eptdev);
501bc69d106SArnaud Pouliquen
502bc69d106SArnaud Pouliquen /* Set the default_ept to the rpmsg device endpoint */
503bc69d106SArnaud Pouliquen eptdev->default_ept = rpdev->ept;
504bc69d106SArnaud Pouliquen
505bc69d106SArnaud Pouliquen /*
506bc69d106SArnaud Pouliquen * The rpmsg_ept_cb uses *priv parameter to get its rpmsg_eptdev context.
507bc69d106SArnaud Pouliquen * Storedit in default_ept *priv field.
508bc69d106SArnaud Pouliquen */
509bc69d106SArnaud Pouliquen eptdev->default_ept->priv = eptdev;
510bc69d106SArnaud Pouliquen
511bc69d106SArnaud Pouliquen return rpmsg_chrdev_eptdev_add(eptdev, chinfo);
512bc69d106SArnaud Pouliquen }
513bc69d106SArnaud Pouliquen
rpmsg_chrdev_remove(struct rpmsg_device * rpdev)514bc69d106SArnaud Pouliquen static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev)
515bc69d106SArnaud Pouliquen {
516bc69d106SArnaud Pouliquen int ret;
517bc69d106SArnaud Pouliquen
518bc69d106SArnaud Pouliquen ret = device_for_each_child(&rpdev->dev, NULL, rpmsg_chrdev_eptdev_destroy);
519bc69d106SArnaud Pouliquen if (ret)
520bc69d106SArnaud Pouliquen dev_warn(&rpdev->dev, "failed to destroy endpoints: %d\n", ret);
521bc69d106SArnaud Pouliquen }
522bc69d106SArnaud Pouliquen
523bc69d106SArnaud Pouliquen static struct rpmsg_device_id rpmsg_chrdev_id_table[] = {
524bc69d106SArnaud Pouliquen { .name = "rpmsg-raw" },
525bc69d106SArnaud Pouliquen { },
526bc69d106SArnaud Pouliquen };
527bc69d106SArnaud Pouliquen
528bc69d106SArnaud Pouliquen static struct rpmsg_driver rpmsg_chrdev_driver = {
529bc69d106SArnaud Pouliquen .probe = rpmsg_chrdev_probe,
530bc69d106SArnaud Pouliquen .remove = rpmsg_chrdev_remove,
531bc69d106SArnaud Pouliquen .callback = rpmsg_ept_cb,
532bc69d106SArnaud Pouliquen .id_table = rpmsg_chrdev_id_table,
533bc69d106SArnaud Pouliquen .drv.name = "rpmsg_chrdev",
534bc69d106SArnaud Pouliquen };
535bc69d106SArnaud Pouliquen
rpmsg_chrdev_init(void)53660d7b22dSArnaud Pouliquen static int rpmsg_chrdev_init(void)
537c0cdc19fSBjorn Andersson {
538c0cdc19fSBjorn Andersson int ret;
539c0cdc19fSBjorn Andersson
540617d3293SArnaud Pouliquen ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg_char");
541c0cdc19fSBjorn Andersson if (ret < 0) {
542c5727244SArnaud Pouliquen pr_err("failed to allocate char dev region\n");
543c0cdc19fSBjorn Andersson return ret;
544c0cdc19fSBjorn Andersson }
545c0cdc19fSBjorn Andersson
546bc69d106SArnaud Pouliquen ret = register_rpmsg_driver(&rpmsg_chrdev_driver);
547bc69d106SArnaud Pouliquen if (ret < 0) {
548bc69d106SArnaud Pouliquen pr_err("rpmsg: failed to register rpmsg raw driver\n");
549bc69d106SArnaud Pouliquen goto free_region;
550bc69d106SArnaud Pouliquen }
551bc69d106SArnaud Pouliquen
552617d3293SArnaud Pouliquen return 0;
553bc69d106SArnaud Pouliquen
554bc69d106SArnaud Pouliquen free_region:
555bc69d106SArnaud Pouliquen unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
556bc69d106SArnaud Pouliquen
557bc69d106SArnaud Pouliquen return ret;
558c0cdc19fSBjorn Andersson }
55960d7b22dSArnaud Pouliquen postcore_initcall(rpmsg_chrdev_init);
560c0cdc19fSBjorn Andersson
rpmsg_chrdev_exit(void)561c0cdc19fSBjorn Andersson static void rpmsg_chrdev_exit(void)
562c0cdc19fSBjorn Andersson {
563bc69d106SArnaud Pouliquen unregister_rpmsg_driver(&rpmsg_chrdev_driver);
564c0cdc19fSBjorn Andersson unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);
565c0cdc19fSBjorn Andersson }
566c0cdc19fSBjorn Andersson module_exit(rpmsg_chrdev_exit);
56793dd4e73SRamon Fried
56893dd4e73SRamon Fried MODULE_ALIAS("rpmsg:rpmsg_chrdev");
569c0cdc19fSBjorn Andersson MODULE_LICENSE("GPL v2");
570