128fb4e59SBjorn Andersson // SPDX-License-Identifier: GPL-2.0
228fb4e59SBjorn Andersson /* Copyright (c) 2018, Linaro Ltd */
328fb4e59SBjorn Andersson
428fb4e59SBjorn Andersson #include <linux/miscdevice.h>
528fb4e59SBjorn Andersson #include <linux/module.h>
628fb4e59SBjorn Andersson #include <linux/poll.h>
728fb4e59SBjorn Andersson #include <linux/skbuff.h>
828fb4e59SBjorn Andersson #include <linux/uaccess.h>
928fb4e59SBjorn Andersson
1028fb4e59SBjorn Andersson #include "qrtr.h"
1128fb4e59SBjorn Andersson
1228fb4e59SBjorn Andersson struct qrtr_tun {
1328fb4e59SBjorn Andersson struct qrtr_endpoint ep;
1428fb4e59SBjorn Andersson
1528fb4e59SBjorn Andersson struct sk_buff_head queue;
1628fb4e59SBjorn Andersson wait_queue_head_t readq;
1728fb4e59SBjorn Andersson };
1828fb4e59SBjorn Andersson
qrtr_tun_send(struct qrtr_endpoint * ep,struct sk_buff * skb)1928fb4e59SBjorn Andersson static int qrtr_tun_send(struct qrtr_endpoint *ep, struct sk_buff *skb)
2028fb4e59SBjorn Andersson {
2128fb4e59SBjorn Andersson struct qrtr_tun *tun = container_of(ep, struct qrtr_tun, ep);
2228fb4e59SBjorn Andersson
2328fb4e59SBjorn Andersson skb_queue_tail(&tun->queue, skb);
2428fb4e59SBjorn Andersson
2528fb4e59SBjorn Andersson /* wake up any blocking processes, waiting for new data */
2628fb4e59SBjorn Andersson wake_up_interruptible(&tun->readq);
2728fb4e59SBjorn Andersson
2828fb4e59SBjorn Andersson return 0;
2928fb4e59SBjorn Andersson }
3028fb4e59SBjorn Andersson
qrtr_tun_open(struct inode * inode,struct file * filp)3128fb4e59SBjorn Andersson static int qrtr_tun_open(struct inode *inode, struct file *filp)
3228fb4e59SBjorn Andersson {
3328fb4e59SBjorn Andersson struct qrtr_tun *tun;
34*fc0494eaSTakeshi Misawa int ret;
3528fb4e59SBjorn Andersson
3628fb4e59SBjorn Andersson tun = kzalloc(sizeof(*tun), GFP_KERNEL);
3728fb4e59SBjorn Andersson if (!tun)
3828fb4e59SBjorn Andersson return -ENOMEM;
3928fb4e59SBjorn Andersson
4028fb4e59SBjorn Andersson skb_queue_head_init(&tun->queue);
4128fb4e59SBjorn Andersson init_waitqueue_head(&tun->readq);
4228fb4e59SBjorn Andersson
4328fb4e59SBjorn Andersson tun->ep.xmit = qrtr_tun_send;
4428fb4e59SBjorn Andersson
4528fb4e59SBjorn Andersson filp->private_data = tun;
4628fb4e59SBjorn Andersson
47*fc0494eaSTakeshi Misawa ret = qrtr_endpoint_register(&tun->ep, QRTR_EP_NID_AUTO);
48*fc0494eaSTakeshi Misawa if (ret)
49*fc0494eaSTakeshi Misawa goto out;
50*fc0494eaSTakeshi Misawa
51*fc0494eaSTakeshi Misawa return 0;
52*fc0494eaSTakeshi Misawa
53*fc0494eaSTakeshi Misawa out:
54*fc0494eaSTakeshi Misawa filp->private_data = NULL;
55*fc0494eaSTakeshi Misawa kfree(tun);
56*fc0494eaSTakeshi Misawa return ret;
5728fb4e59SBjorn Andersson }
5828fb4e59SBjorn Andersson
qrtr_tun_read_iter(struct kiocb * iocb,struct iov_iter * to)5928fb4e59SBjorn Andersson static ssize_t qrtr_tun_read_iter(struct kiocb *iocb, struct iov_iter *to)
6028fb4e59SBjorn Andersson {
6128fb4e59SBjorn Andersson struct file *filp = iocb->ki_filp;
6228fb4e59SBjorn Andersson struct qrtr_tun *tun = filp->private_data;
6328fb4e59SBjorn Andersson struct sk_buff *skb;
6428fb4e59SBjorn Andersson int count;
6528fb4e59SBjorn Andersson
6628fb4e59SBjorn Andersson while (!(skb = skb_dequeue(&tun->queue))) {
6728fb4e59SBjorn Andersson if (filp->f_flags & O_NONBLOCK)
6828fb4e59SBjorn Andersson return -EAGAIN;
6928fb4e59SBjorn Andersson
7028fb4e59SBjorn Andersson /* Wait until we get data or the endpoint goes away */
7128fb4e59SBjorn Andersson if (wait_event_interruptible(tun->readq,
7228fb4e59SBjorn Andersson !skb_queue_empty(&tun->queue)))
7328fb4e59SBjorn Andersson return -ERESTARTSYS;
7428fb4e59SBjorn Andersson }
7528fb4e59SBjorn Andersson
7628fb4e59SBjorn Andersson count = min_t(size_t, iov_iter_count(to), skb->len);
7728fb4e59SBjorn Andersson if (copy_to_iter(skb->data, count, to) != count)
7828fb4e59SBjorn Andersson count = -EFAULT;
7928fb4e59SBjorn Andersson
8028fb4e59SBjorn Andersson kfree_skb(skb);
8128fb4e59SBjorn Andersson
8228fb4e59SBjorn Andersson return count;
8328fb4e59SBjorn Andersson }
8428fb4e59SBjorn Andersson
qrtr_tun_write_iter(struct kiocb * iocb,struct iov_iter * from)8528fb4e59SBjorn Andersson static ssize_t qrtr_tun_write_iter(struct kiocb *iocb, struct iov_iter *from)
8628fb4e59SBjorn Andersson {
8728fb4e59SBjorn Andersson struct file *filp = iocb->ki_filp;
8828fb4e59SBjorn Andersson struct qrtr_tun *tun = filp->private_data;
8928fb4e59SBjorn Andersson size_t len = iov_iter_count(from);
9028fb4e59SBjorn Andersson ssize_t ret;
9128fb4e59SBjorn Andersson void *kbuf;
9228fb4e59SBjorn Andersson
932a80c158SSabyrzhan Tasbolatov if (!len)
942a80c158SSabyrzhan Tasbolatov return -EINVAL;
952a80c158SSabyrzhan Tasbolatov
962a80c158SSabyrzhan Tasbolatov if (len > KMALLOC_MAX_SIZE)
972a80c158SSabyrzhan Tasbolatov return -ENOMEM;
982a80c158SSabyrzhan Tasbolatov
9928fb4e59SBjorn Andersson kbuf = kzalloc(len, GFP_KERNEL);
10028fb4e59SBjorn Andersson if (!kbuf)
10128fb4e59SBjorn Andersson return -ENOMEM;
10228fb4e59SBjorn Andersson
103a21b7f0cSNavid Emamdoost if (!copy_from_iter_full(kbuf, len, from)) {
104a21b7f0cSNavid Emamdoost kfree(kbuf);
10528fb4e59SBjorn Andersson return -EFAULT;
106a21b7f0cSNavid Emamdoost }
10728fb4e59SBjorn Andersson
10828fb4e59SBjorn Andersson ret = qrtr_endpoint_post(&tun->ep, kbuf, len);
10928fb4e59SBjorn Andersson
110a21b7f0cSNavid Emamdoost kfree(kbuf);
11128fb4e59SBjorn Andersson return ret < 0 ? ret : len;
11228fb4e59SBjorn Andersson }
11328fb4e59SBjorn Andersson
qrtr_tun_poll(struct file * filp,poll_table * wait)11428fb4e59SBjorn Andersson static __poll_t qrtr_tun_poll(struct file *filp, poll_table *wait)
11528fb4e59SBjorn Andersson {
11628fb4e59SBjorn Andersson struct qrtr_tun *tun = filp->private_data;
11728fb4e59SBjorn Andersson __poll_t mask = 0;
11828fb4e59SBjorn Andersson
11928fb4e59SBjorn Andersson poll_wait(filp, &tun->readq, wait);
12028fb4e59SBjorn Andersson
12128fb4e59SBjorn Andersson if (!skb_queue_empty(&tun->queue))
12228fb4e59SBjorn Andersson mask |= EPOLLIN | EPOLLRDNORM;
12328fb4e59SBjorn Andersson
12428fb4e59SBjorn Andersson return mask;
12528fb4e59SBjorn Andersson }
12628fb4e59SBjorn Andersson
qrtr_tun_release(struct inode * inode,struct file * filp)12728fb4e59SBjorn Andersson static int qrtr_tun_release(struct inode *inode, struct file *filp)
12828fb4e59SBjorn Andersson {
12928fb4e59SBjorn Andersson struct qrtr_tun *tun = filp->private_data;
13028fb4e59SBjorn Andersson
13128fb4e59SBjorn Andersson qrtr_endpoint_unregister(&tun->ep);
13228fb4e59SBjorn Andersson
13328fb4e59SBjorn Andersson /* Discard all SKBs */
13421d8bd12SChristophe JAILLET skb_queue_purge(&tun->queue);
13528fb4e59SBjorn Andersson
13628fb4e59SBjorn Andersson kfree(tun);
13728fb4e59SBjorn Andersson
13828fb4e59SBjorn Andersson return 0;
13928fb4e59SBjorn Andersson }
14028fb4e59SBjorn Andersson
14128fb4e59SBjorn Andersson static const struct file_operations qrtr_tun_ops = {
14228fb4e59SBjorn Andersson .owner = THIS_MODULE,
14328fb4e59SBjorn Andersson .open = qrtr_tun_open,
14428fb4e59SBjorn Andersson .poll = qrtr_tun_poll,
14528fb4e59SBjorn Andersson .read_iter = qrtr_tun_read_iter,
14628fb4e59SBjorn Andersson .write_iter = qrtr_tun_write_iter,
14728fb4e59SBjorn Andersson .release = qrtr_tun_release,
14828fb4e59SBjorn Andersson };
14928fb4e59SBjorn Andersson
15028fb4e59SBjorn Andersson static struct miscdevice qrtr_tun_miscdev = {
15128fb4e59SBjorn Andersson MISC_DYNAMIC_MINOR,
15228fb4e59SBjorn Andersson "qrtr-tun",
15328fb4e59SBjorn Andersson &qrtr_tun_ops,
15428fb4e59SBjorn Andersson };
15528fb4e59SBjorn Andersson
qrtr_tun_init(void)15628fb4e59SBjorn Andersson static int __init qrtr_tun_init(void)
15728fb4e59SBjorn Andersson {
15828fb4e59SBjorn Andersson int ret;
15928fb4e59SBjorn Andersson
16028fb4e59SBjorn Andersson ret = misc_register(&qrtr_tun_miscdev);
16128fb4e59SBjorn Andersson if (ret)
16228fb4e59SBjorn Andersson pr_err("failed to register Qualcomm IPC Router tun device\n");
16328fb4e59SBjorn Andersson
16428fb4e59SBjorn Andersson return ret;
16528fb4e59SBjorn Andersson }
16628fb4e59SBjorn Andersson
qrtr_tun_exit(void)16728fb4e59SBjorn Andersson static void __exit qrtr_tun_exit(void)
16828fb4e59SBjorn Andersson {
16928fb4e59SBjorn Andersson misc_deregister(&qrtr_tun_miscdev);
17028fb4e59SBjorn Andersson }
17128fb4e59SBjorn Andersson
17228fb4e59SBjorn Andersson module_init(qrtr_tun_init);
17328fb4e59SBjorn Andersson module_exit(qrtr_tun_exit);
17428fb4e59SBjorn Andersson
17528fb4e59SBjorn Andersson MODULE_DESCRIPTION("Qualcomm IPC Router TUN device");
17628fb4e59SBjorn Andersson MODULE_LICENSE("GPL v2");
177