18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2018, Linaro Ltd */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
58c2ecf20Sopenharmony_ci#include <linux/module.h>
68c2ecf20Sopenharmony_ci#include <linux/poll.h>
78c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
88c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "qrtr.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistruct qrtr_tun {
138c2ecf20Sopenharmony_ci	struct qrtr_endpoint ep;
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci	struct sk_buff_head queue;
168c2ecf20Sopenharmony_ci	wait_queue_head_t readq;
178c2ecf20Sopenharmony_ci};
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int qrtr_tun_send(struct qrtr_endpoint *ep, struct sk_buff *skb)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct qrtr_tun *tun = container_of(ep, struct qrtr_tun, ep);
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	skb_queue_tail(&tun->queue, skb);
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	/* wake up any blocking processes, waiting for new data */
268c2ecf20Sopenharmony_ci	wake_up_interruptible(&tun->readq);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	return 0;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int qrtr_tun_open(struct inode *inode, struct file *filp)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct qrtr_tun *tun;
348c2ecf20Sopenharmony_ci	int ret;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	tun = kzalloc(sizeof(*tun), GFP_KERNEL);
378c2ecf20Sopenharmony_ci	if (!tun)
388c2ecf20Sopenharmony_ci		return -ENOMEM;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	skb_queue_head_init(&tun->queue);
418c2ecf20Sopenharmony_ci	init_waitqueue_head(&tun->readq);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	tun->ep.xmit = qrtr_tun_send;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	filp->private_data = tun;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	ret = qrtr_endpoint_register(&tun->ep, QRTR_EP_NID_AUTO);
488c2ecf20Sopenharmony_ci	if (ret)
498c2ecf20Sopenharmony_ci		goto out;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	return 0;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciout:
548c2ecf20Sopenharmony_ci	filp->private_data = NULL;
558c2ecf20Sopenharmony_ci	kfree(tun);
568c2ecf20Sopenharmony_ci	return ret;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic ssize_t qrtr_tun_read_iter(struct kiocb *iocb, struct iov_iter *to)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct file *filp = iocb->ki_filp;
628c2ecf20Sopenharmony_ci	struct qrtr_tun *tun = filp->private_data;
638c2ecf20Sopenharmony_ci	struct sk_buff *skb;
648c2ecf20Sopenharmony_ci	int count;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	while (!(skb = skb_dequeue(&tun->queue))) {
678c2ecf20Sopenharmony_ci		if (filp->f_flags & O_NONBLOCK)
688c2ecf20Sopenharmony_ci			return -EAGAIN;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		/* Wait until we get data or the endpoint goes away */
718c2ecf20Sopenharmony_ci		if (wait_event_interruptible(tun->readq,
728c2ecf20Sopenharmony_ci					     !skb_queue_empty(&tun->queue)))
738c2ecf20Sopenharmony_ci			return -ERESTARTSYS;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	count = min_t(size_t, iov_iter_count(to), skb->len);
778c2ecf20Sopenharmony_ci	if (copy_to_iter(skb->data, count, to) != count)
788c2ecf20Sopenharmony_ci		count = -EFAULT;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	kfree_skb(skb);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return count;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic ssize_t qrtr_tun_write_iter(struct kiocb *iocb, struct iov_iter *from)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct file *filp = iocb->ki_filp;
888c2ecf20Sopenharmony_ci	struct qrtr_tun *tun = filp->private_data;
898c2ecf20Sopenharmony_ci	size_t len = iov_iter_count(from);
908c2ecf20Sopenharmony_ci	ssize_t ret;
918c2ecf20Sopenharmony_ci	void *kbuf;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (!len)
948c2ecf20Sopenharmony_ci		return -EINVAL;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (len > KMALLOC_MAX_SIZE)
978c2ecf20Sopenharmony_ci		return -ENOMEM;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	kbuf = kzalloc(len, GFP_KERNEL);
1008c2ecf20Sopenharmony_ci	if (!kbuf)
1018c2ecf20Sopenharmony_ci		return -ENOMEM;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (!copy_from_iter_full(kbuf, len, from)) {
1048c2ecf20Sopenharmony_ci		kfree(kbuf);
1058c2ecf20Sopenharmony_ci		return -EFAULT;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ret = qrtr_endpoint_post(&tun->ep, kbuf, len);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	kfree(kbuf);
1118c2ecf20Sopenharmony_ci	return ret < 0 ? ret : len;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic __poll_t qrtr_tun_poll(struct file *filp, poll_table *wait)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct qrtr_tun *tun = filp->private_data;
1178c2ecf20Sopenharmony_ci	__poll_t mask = 0;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	poll_wait(filp, &tun->readq, wait);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (!skb_queue_empty(&tun->queue))
1228c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return mask;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int qrtr_tun_release(struct inode *inode, struct file *filp)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct qrtr_tun *tun = filp->private_data;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	qrtr_endpoint_unregister(&tun->ep);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* Discard all SKBs */
1348c2ecf20Sopenharmony_ci	skb_queue_purge(&tun->queue);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	kfree(tun);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return 0;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic const struct file_operations qrtr_tun_ops = {
1428c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1438c2ecf20Sopenharmony_ci	.open = qrtr_tun_open,
1448c2ecf20Sopenharmony_ci	.poll = qrtr_tun_poll,
1458c2ecf20Sopenharmony_ci	.read_iter = qrtr_tun_read_iter,
1468c2ecf20Sopenharmony_ci	.write_iter = qrtr_tun_write_iter,
1478c2ecf20Sopenharmony_ci	.release = qrtr_tun_release,
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic struct miscdevice qrtr_tun_miscdev = {
1518c2ecf20Sopenharmony_ci	MISC_DYNAMIC_MINOR,
1528c2ecf20Sopenharmony_ci	"qrtr-tun",
1538c2ecf20Sopenharmony_ci	&qrtr_tun_ops,
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic int __init qrtr_tun_init(void)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	int ret;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	ret = misc_register(&qrtr_tun_miscdev);
1618c2ecf20Sopenharmony_ci	if (ret)
1628c2ecf20Sopenharmony_ci		pr_err("failed to register Qualcomm IPC Router tun device\n");
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return ret;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void __exit qrtr_tun_exit(void)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	misc_deregister(&qrtr_tun_miscdev);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cimodule_init(qrtr_tun_init);
1738c2ecf20Sopenharmony_cimodule_exit(qrtr_tun_exit);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm IPC Router TUN device");
1768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
177