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