1e624e6c3SBongsu Jeon // SPDX-License-Identifier: GPL-2.0-or-later 2e624e6c3SBongsu Jeon /* 3e624e6c3SBongsu Jeon * Virtual NCI device simulation driver 4e624e6c3SBongsu Jeon * 5e624e6c3SBongsu Jeon * Copyright (C) 2020 Samsung Electrnoics 6e624e6c3SBongsu Jeon * Bongsu Jeon <bongsu.jeon@samsung.com> 7e624e6c3SBongsu Jeon */ 8e624e6c3SBongsu Jeon 9e624e6c3SBongsu Jeon #include <linux/kernel.h> 10e624e6c3SBongsu Jeon #include <linux/module.h> 11e624e6c3SBongsu Jeon #include <linux/miscdevice.h> 12e624e6c3SBongsu Jeon #include <linux/mutex.h> 138675569dSBongsu Jeon #include <linux/wait.h> 14e624e6c3SBongsu Jeon #include <net/nfc/nci_core.h> 15e624e6c3SBongsu Jeon 16e624e6c3SBongsu Jeon #define IOCTL_GET_NCIDEV_IDX 0 17e624e6c3SBongsu Jeon #define VIRTUAL_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ 18e624e6c3SBongsu Jeon NFC_PROTO_MIFARE_MASK | \ 19e624e6c3SBongsu Jeon NFC_PROTO_FELICA_MASK | \ 20e624e6c3SBongsu Jeon NFC_PROTO_ISO14443_MASK | \ 21e624e6c3SBongsu Jeon NFC_PROTO_ISO14443_B_MASK | \ 22e624e6c3SBongsu Jeon NFC_PROTO_ISO15693_MASK) 23e624e6c3SBongsu Jeon 24b2e44aacSDmitry Vyukov struct virtual_nci_dev { 25b2e44aacSDmitry Vyukov struct nci_dev *ndev; 26b2e44aacSDmitry Vyukov struct mutex mtx; 27b2e44aacSDmitry Vyukov struct sk_buff *send_buff; 28b2e44aacSDmitry Vyukov struct wait_queue_head wq; 2984d2db91SNguyen Dinh Phi bool running; 30b2e44aacSDmitry Vyukov }; 31e624e6c3SBongsu Jeon 32e624e6c3SBongsu Jeon static int virtual_nci_open(struct nci_dev *ndev) 33e624e6c3SBongsu Jeon { 3484d2db91SNguyen Dinh Phi struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); 3584d2db91SNguyen Dinh Phi 3684d2db91SNguyen Dinh Phi vdev->running = true; 37e624e6c3SBongsu Jeon return 0; 38e624e6c3SBongsu Jeon } 39e624e6c3SBongsu Jeon 40e624e6c3SBongsu Jeon static int virtual_nci_close(struct nci_dev *ndev) 41e624e6c3SBongsu Jeon { 42b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); 43b2e44aacSDmitry Vyukov 44b2e44aacSDmitry Vyukov mutex_lock(&vdev->mtx); 45b2e44aacSDmitry Vyukov kfree_skb(vdev->send_buff); 46b2e44aacSDmitry Vyukov vdev->send_buff = NULL; 4784d2db91SNguyen Dinh Phi vdev->running = false; 48b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 49e624e6c3SBongsu Jeon 50e624e6c3SBongsu Jeon return 0; 51e624e6c3SBongsu Jeon } 52e624e6c3SBongsu Jeon 53e624e6c3SBongsu Jeon static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb) 54e624e6c3SBongsu Jeon { 55b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); 56e624e6c3SBongsu Jeon 57b2e44aacSDmitry Vyukov mutex_lock(&vdev->mtx); 5884d2db91SNguyen Dinh Phi if (vdev->send_buff || !vdev->running) { 59b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 60e840d8f4SShang XiaoJing kfree_skb(skb); 61e624e6c3SBongsu Jeon return -1; 62e624e6c3SBongsu Jeon } 63b2e44aacSDmitry Vyukov vdev->send_buff = skb_copy(skb, GFP_KERNEL); 64b2e44aacSDmitry Vyukov if (!vdev->send_buff) { 65b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 66b2e44aacSDmitry Vyukov kfree_skb(skb); 67b2e44aacSDmitry Vyukov return -1; 68b2e44aacSDmitry Vyukov } 69b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 70b2e44aacSDmitry Vyukov wake_up_interruptible(&vdev->wq); 71e840d8f4SShang XiaoJing consume_skb(skb); 72e624e6c3SBongsu Jeon 73e624e6c3SBongsu Jeon return 0; 74e624e6c3SBongsu Jeon } 75e624e6c3SBongsu Jeon 76b9c28286SKrzysztof Kozlowski static const struct nci_ops virtual_nci_ops = { 77e624e6c3SBongsu Jeon .open = virtual_nci_open, 78e624e6c3SBongsu Jeon .close = virtual_nci_close, 79e624e6c3SBongsu Jeon .send = virtual_nci_send 80e624e6c3SBongsu Jeon }; 81e624e6c3SBongsu Jeon 82e624e6c3SBongsu Jeon static ssize_t virtual_ncidev_read(struct file *file, char __user *buf, 83e624e6c3SBongsu Jeon size_t count, loff_t *ppos) 84e624e6c3SBongsu Jeon { 85b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 86e624e6c3SBongsu Jeon size_t actual_len; 87e624e6c3SBongsu Jeon 88b2e44aacSDmitry Vyukov mutex_lock(&vdev->mtx); 89b2e44aacSDmitry Vyukov while (!vdev->send_buff) { 90b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 91b2e44aacSDmitry Vyukov if (wait_event_interruptible(vdev->wq, vdev->send_buff)) 928675569dSBongsu Jeon return -EFAULT; 93b2e44aacSDmitry Vyukov mutex_lock(&vdev->mtx); 94e624e6c3SBongsu Jeon } 95e624e6c3SBongsu Jeon 96b2e44aacSDmitry Vyukov actual_len = min_t(size_t, count, vdev->send_buff->len); 97e624e6c3SBongsu Jeon 98b2e44aacSDmitry Vyukov if (copy_to_user(buf, vdev->send_buff->data, actual_len)) { 99b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 100e624e6c3SBongsu Jeon return -EFAULT; 101e624e6c3SBongsu Jeon } 102e624e6c3SBongsu Jeon 103b2e44aacSDmitry Vyukov skb_pull(vdev->send_buff, actual_len); 104b2e44aacSDmitry Vyukov if (vdev->send_buff->len == 0) { 105b2e44aacSDmitry Vyukov consume_skb(vdev->send_buff); 106b2e44aacSDmitry Vyukov vdev->send_buff = NULL; 107e624e6c3SBongsu Jeon } 108b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 109e624e6c3SBongsu Jeon 110e624e6c3SBongsu Jeon return actual_len; 111e624e6c3SBongsu Jeon } 112e624e6c3SBongsu Jeon 113e624e6c3SBongsu Jeon static ssize_t virtual_ncidev_write(struct file *file, 114e624e6c3SBongsu Jeon const char __user *buf, 115e624e6c3SBongsu Jeon size_t count, loff_t *ppos) 116e624e6c3SBongsu Jeon { 117b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 118e624e6c3SBongsu Jeon struct sk_buff *skb; 119e624e6c3SBongsu Jeon 120e624e6c3SBongsu Jeon skb = alloc_skb(count, GFP_KERNEL); 121e624e6c3SBongsu Jeon if (!skb) 122e624e6c3SBongsu Jeon return -ENOMEM; 123e624e6c3SBongsu Jeon 124e624e6c3SBongsu Jeon if (copy_from_user(skb_put(skb, count), buf, count)) { 125e624e6c3SBongsu Jeon kfree_skb(skb); 126e624e6c3SBongsu Jeon return -EFAULT; 127e624e6c3SBongsu Jeon } 128*068648aaSEdward Adam Davis if (strnlen(skb->data, count) != count) { 129*068648aaSEdward Adam Davis kfree_skb(skb); 130*068648aaSEdward Adam Davis return -EINVAL; 131*068648aaSEdward Adam Davis } 132e624e6c3SBongsu Jeon 133b2e44aacSDmitry Vyukov nci_recv_frame(vdev->ndev, skb); 134e624e6c3SBongsu Jeon return count; 135e624e6c3SBongsu Jeon } 136e624e6c3SBongsu Jeon 137e624e6c3SBongsu Jeon static int virtual_ncidev_open(struct inode *inode, struct file *file) 138e624e6c3SBongsu Jeon { 139e624e6c3SBongsu Jeon int ret = 0; 140b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev; 141e624e6c3SBongsu Jeon 142b2e44aacSDmitry Vyukov vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); 143b2e44aacSDmitry Vyukov if (!vdev) 144b2e44aacSDmitry Vyukov return -ENOMEM; 145b2e44aacSDmitry Vyukov vdev->ndev = nci_allocate_device(&virtual_nci_ops, 146b2e44aacSDmitry Vyukov VIRTUAL_NFC_PROTOCOLS, 0, 0); 147b2e44aacSDmitry Vyukov if (!vdev->ndev) { 148b2e44aacSDmitry Vyukov kfree(vdev); 149e624e6c3SBongsu Jeon return -ENOMEM; 150e624e6c3SBongsu Jeon } 151e624e6c3SBongsu Jeon 152b2e44aacSDmitry Vyukov mutex_init(&vdev->mtx); 153b2e44aacSDmitry Vyukov init_waitqueue_head(&vdev->wq); 154b2e44aacSDmitry Vyukov file->private_data = vdev; 155b2e44aacSDmitry Vyukov nci_set_drvdata(vdev->ndev, vdev); 156b2e44aacSDmitry Vyukov 157b2e44aacSDmitry Vyukov ret = nci_register_device(vdev->ndev); 158e624e6c3SBongsu Jeon if (ret < 0) { 159b2e44aacSDmitry Vyukov nci_free_device(vdev->ndev); 160b2e44aacSDmitry Vyukov mutex_destroy(&vdev->mtx); 161b2e44aacSDmitry Vyukov kfree(vdev); 162e624e6c3SBongsu Jeon return ret; 163e624e6c3SBongsu Jeon } 164e624e6c3SBongsu Jeon 165e624e6c3SBongsu Jeon return 0; 166e624e6c3SBongsu Jeon } 167e624e6c3SBongsu Jeon 168e624e6c3SBongsu Jeon static int virtual_ncidev_close(struct inode *inode, struct file *file) 169e624e6c3SBongsu Jeon { 170b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 171e624e6c3SBongsu Jeon 172b2e44aacSDmitry Vyukov nci_unregister_device(vdev->ndev); 173b2e44aacSDmitry Vyukov nci_free_device(vdev->ndev); 174b2e44aacSDmitry Vyukov mutex_destroy(&vdev->mtx); 175b2e44aacSDmitry Vyukov kfree(vdev); 176e624e6c3SBongsu Jeon 177e624e6c3SBongsu Jeon return 0; 178e624e6c3SBongsu Jeon } 179e624e6c3SBongsu Jeon 180b2e44aacSDmitry Vyukov static long virtual_ncidev_ioctl(struct file *file, unsigned int cmd, 181e624e6c3SBongsu Jeon unsigned long arg) 182e624e6c3SBongsu Jeon { 183b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 184b2e44aacSDmitry Vyukov const struct nfc_dev *nfc_dev = vdev->ndev->nfc_dev; 185e624e6c3SBongsu Jeon void __user *p = (void __user *)arg; 186e624e6c3SBongsu Jeon 187e624e6c3SBongsu Jeon if (cmd != IOCTL_GET_NCIDEV_IDX) 188e624e6c3SBongsu Jeon return -ENOTTY; 189e624e6c3SBongsu Jeon 190e624e6c3SBongsu Jeon if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx))) 191e624e6c3SBongsu Jeon return -EFAULT; 192e624e6c3SBongsu Jeon 193e624e6c3SBongsu Jeon return 0; 194e624e6c3SBongsu Jeon } 195e624e6c3SBongsu Jeon 196e624e6c3SBongsu Jeon static const struct file_operations virtual_ncidev_fops = { 197e624e6c3SBongsu Jeon .owner = THIS_MODULE, 198e624e6c3SBongsu Jeon .read = virtual_ncidev_read, 199e624e6c3SBongsu Jeon .write = virtual_ncidev_write, 200e624e6c3SBongsu Jeon .open = virtual_ncidev_open, 201e624e6c3SBongsu Jeon .release = virtual_ncidev_close, 202e624e6c3SBongsu Jeon .unlocked_ioctl = virtual_ncidev_ioctl 203e624e6c3SBongsu Jeon }; 204e624e6c3SBongsu Jeon 205b2e44aacSDmitry Vyukov static struct miscdevice miscdev = { 206b2e44aacSDmitry Vyukov .minor = MISC_DYNAMIC_MINOR, 207b2e44aacSDmitry Vyukov .name = "virtual_nci", 208b2e44aacSDmitry Vyukov .fops = &virtual_ncidev_fops, 209b2e44aacSDmitry Vyukov .mode = 0600, 210b2e44aacSDmitry Vyukov }; 211b2e44aacSDmitry Vyukov 21261a9b174SLi Zetao module_misc_device(miscdev); 213e624e6c3SBongsu Jeon 214e624e6c3SBongsu Jeon MODULE_LICENSE("GPL"); 215e624e6c3SBongsu Jeon MODULE_DESCRIPTION("Virtual NCI device simulation driver"); 216e624e6c3SBongsu Jeon MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>"); 217