162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2018, Linaro Ltd */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/miscdevice.h>
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/poll.h>
762306a36Sopenharmony_ci#include <linux/skbuff.h>
862306a36Sopenharmony_ci#include <linux/uaccess.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "qrtr.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistruct qrtr_tun {
1362306a36Sopenharmony_ci	struct qrtr_endpoint ep;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci	struct sk_buff_head queue;
1662306a36Sopenharmony_ci	wait_queue_head_t readq;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int qrtr_tun_send(struct qrtr_endpoint *ep, struct sk_buff *skb)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct qrtr_tun *tun = container_of(ep, struct qrtr_tun, ep);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	skb_queue_tail(&tun->queue, skb);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	/* wake up any blocking processes, waiting for new data */
2662306a36Sopenharmony_ci	wake_up_interruptible(&tun->readq);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	return 0;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int qrtr_tun_open(struct inode *inode, struct file *filp)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct qrtr_tun *tun;
3462306a36Sopenharmony_ci	int ret;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	tun = kzalloc(sizeof(*tun), GFP_KERNEL);
3762306a36Sopenharmony_ci	if (!tun)
3862306a36Sopenharmony_ci		return -ENOMEM;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	skb_queue_head_init(&tun->queue);
4162306a36Sopenharmony_ci	init_waitqueue_head(&tun->readq);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	tun->ep.xmit = qrtr_tun_send;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	filp->private_data = tun;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	ret = qrtr_endpoint_register(&tun->ep, QRTR_EP_NID_AUTO);
4862306a36Sopenharmony_ci	if (ret)
4962306a36Sopenharmony_ci		goto out;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	return 0;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciout:
5462306a36Sopenharmony_ci	filp->private_data = NULL;
5562306a36Sopenharmony_ci	kfree(tun);
5662306a36Sopenharmony_ci	return ret;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic ssize_t qrtr_tun_read_iter(struct kiocb *iocb, struct iov_iter *to)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct file *filp = iocb->ki_filp;
6262306a36Sopenharmony_ci	struct qrtr_tun *tun = filp->private_data;
6362306a36Sopenharmony_ci	struct sk_buff *skb;
6462306a36Sopenharmony_ci	int count;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	while (!(skb = skb_dequeue(&tun->queue))) {
6762306a36Sopenharmony_ci		if (filp->f_flags & O_NONBLOCK)
6862306a36Sopenharmony_ci			return -EAGAIN;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		/* Wait until we get data or the endpoint goes away */
7162306a36Sopenharmony_ci		if (wait_event_interruptible(tun->readq,
7262306a36Sopenharmony_ci					     !skb_queue_empty(&tun->queue)))
7362306a36Sopenharmony_ci			return -ERESTARTSYS;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	count = min_t(size_t, iov_iter_count(to), skb->len);
7762306a36Sopenharmony_ci	if (copy_to_iter(skb->data, count, to) != count)
7862306a36Sopenharmony_ci		count = -EFAULT;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	kfree_skb(skb);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return count;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic ssize_t qrtr_tun_write_iter(struct kiocb *iocb, struct iov_iter *from)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct file *filp = iocb->ki_filp;
8862306a36Sopenharmony_ci	struct qrtr_tun *tun = filp->private_data;
8962306a36Sopenharmony_ci	size_t len = iov_iter_count(from);
9062306a36Sopenharmony_ci	ssize_t ret;
9162306a36Sopenharmony_ci	void *kbuf;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (!len)
9462306a36Sopenharmony_ci		return -EINVAL;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (len > KMALLOC_MAX_SIZE)
9762306a36Sopenharmony_ci		return -ENOMEM;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	kbuf = kzalloc(len, GFP_KERNEL);
10062306a36Sopenharmony_ci	if (!kbuf)
10162306a36Sopenharmony_ci		return -ENOMEM;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (!copy_from_iter_full(kbuf, len, from)) {
10462306a36Sopenharmony_ci		kfree(kbuf);
10562306a36Sopenharmony_ci		return -EFAULT;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	ret = qrtr_endpoint_post(&tun->ep, kbuf, len);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	kfree(kbuf);
11162306a36Sopenharmony_ci	return ret < 0 ? ret : len;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic __poll_t qrtr_tun_poll(struct file *filp, poll_table *wait)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct qrtr_tun *tun = filp->private_data;
11762306a36Sopenharmony_ci	__poll_t mask = 0;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	poll_wait(filp, &tun->readq, wait);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (!skb_queue_empty(&tun->queue))
12262306a36Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return mask;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int qrtr_tun_release(struct inode *inode, struct file *filp)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct qrtr_tun *tun = filp->private_data;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	qrtr_endpoint_unregister(&tun->ep);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* Discard all SKBs */
13462306a36Sopenharmony_ci	skb_queue_purge(&tun->queue);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	kfree(tun);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic const struct file_operations qrtr_tun_ops = {
14262306a36Sopenharmony_ci	.owner = THIS_MODULE,
14362306a36Sopenharmony_ci	.open = qrtr_tun_open,
14462306a36Sopenharmony_ci	.poll = qrtr_tun_poll,
14562306a36Sopenharmony_ci	.read_iter = qrtr_tun_read_iter,
14662306a36Sopenharmony_ci	.write_iter = qrtr_tun_write_iter,
14762306a36Sopenharmony_ci	.release = qrtr_tun_release,
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic struct miscdevice qrtr_tun_miscdev = {
15162306a36Sopenharmony_ci	MISC_DYNAMIC_MINOR,
15262306a36Sopenharmony_ci	"qrtr-tun",
15362306a36Sopenharmony_ci	&qrtr_tun_ops,
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int __init qrtr_tun_init(void)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	int ret;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	ret = misc_register(&qrtr_tun_miscdev);
16162306a36Sopenharmony_ci	if (ret)
16262306a36Sopenharmony_ci		pr_err("failed to register Qualcomm IPC Router tun device\n");
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return ret;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic void __exit qrtr_tun_exit(void)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	misc_deregister(&qrtr_tun_miscdev);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cimodule_init(qrtr_tun_init);
17362306a36Sopenharmony_cimodule_exit(qrtr_tun_exit);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm IPC Router TUN device");
17662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
177