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; 29b2e44aacSDmitry Vyukov }; 30e624e6c3SBongsu Jeon 31e624e6c3SBongsu Jeon static int virtual_nci_open(struct nci_dev *ndev) 32e624e6c3SBongsu Jeon { 33e624e6c3SBongsu Jeon return 0; 34e624e6c3SBongsu Jeon } 35e624e6c3SBongsu Jeon 36e624e6c3SBongsu Jeon static int virtual_nci_close(struct nci_dev *ndev) 37e624e6c3SBongsu Jeon { 38b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); 39b2e44aacSDmitry Vyukov 40b2e44aacSDmitry Vyukov mutex_lock(&vdev->mtx); 41b2e44aacSDmitry Vyukov kfree_skb(vdev->send_buff); 42b2e44aacSDmitry Vyukov vdev->send_buff = NULL; 43b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 44e624e6c3SBongsu Jeon 45e624e6c3SBongsu Jeon return 0; 46e624e6c3SBongsu Jeon } 47e624e6c3SBongsu Jeon 48e624e6c3SBongsu Jeon static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb) 49e624e6c3SBongsu Jeon { 50b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); 51e624e6c3SBongsu Jeon 52b2e44aacSDmitry Vyukov mutex_lock(&vdev->mtx); 53b2e44aacSDmitry Vyukov if (vdev->send_buff) { 54b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 55e840d8f4SShang XiaoJing kfree_skb(skb); 56e624e6c3SBongsu Jeon return -1; 57e624e6c3SBongsu Jeon } 58b2e44aacSDmitry Vyukov vdev->send_buff = skb_copy(skb, GFP_KERNEL); 59b2e44aacSDmitry Vyukov if (!vdev->send_buff) { 60b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 61b2e44aacSDmitry Vyukov kfree_skb(skb); 62b2e44aacSDmitry Vyukov return -1; 63b2e44aacSDmitry Vyukov } 64b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 65b2e44aacSDmitry Vyukov wake_up_interruptible(&vdev->wq); 66e840d8f4SShang XiaoJing consume_skb(skb); 67e624e6c3SBongsu Jeon 68e624e6c3SBongsu Jeon return 0; 69e624e6c3SBongsu Jeon } 70e624e6c3SBongsu Jeon 71b9c28286SKrzysztof Kozlowski static const struct nci_ops virtual_nci_ops = { 72e624e6c3SBongsu Jeon .open = virtual_nci_open, 73e624e6c3SBongsu Jeon .close = virtual_nci_close, 74e624e6c3SBongsu Jeon .send = virtual_nci_send 75e624e6c3SBongsu Jeon }; 76e624e6c3SBongsu Jeon 77e624e6c3SBongsu Jeon static ssize_t virtual_ncidev_read(struct file *file, char __user *buf, 78e624e6c3SBongsu Jeon size_t count, loff_t *ppos) 79e624e6c3SBongsu Jeon { 80b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 81e624e6c3SBongsu Jeon size_t actual_len; 82e624e6c3SBongsu Jeon 83b2e44aacSDmitry Vyukov mutex_lock(&vdev->mtx); 84b2e44aacSDmitry Vyukov while (!vdev->send_buff) { 85b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 86b2e44aacSDmitry Vyukov if (wait_event_interruptible(vdev->wq, vdev->send_buff)) 878675569dSBongsu Jeon return -EFAULT; 88b2e44aacSDmitry Vyukov mutex_lock(&vdev->mtx); 89e624e6c3SBongsu Jeon } 90e624e6c3SBongsu Jeon 91b2e44aacSDmitry Vyukov actual_len = min_t(size_t, count, vdev->send_buff->len); 92e624e6c3SBongsu Jeon 93b2e44aacSDmitry Vyukov if (copy_to_user(buf, vdev->send_buff->data, actual_len)) { 94b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 95e624e6c3SBongsu Jeon return -EFAULT; 96e624e6c3SBongsu Jeon } 97e624e6c3SBongsu Jeon 98b2e44aacSDmitry Vyukov skb_pull(vdev->send_buff, actual_len); 99b2e44aacSDmitry Vyukov if (vdev->send_buff->len == 0) { 100b2e44aacSDmitry Vyukov consume_skb(vdev->send_buff); 101b2e44aacSDmitry Vyukov vdev->send_buff = NULL; 102e624e6c3SBongsu Jeon } 103b2e44aacSDmitry Vyukov mutex_unlock(&vdev->mtx); 104e624e6c3SBongsu Jeon 105e624e6c3SBongsu Jeon return actual_len; 106e624e6c3SBongsu Jeon } 107e624e6c3SBongsu Jeon 108e624e6c3SBongsu Jeon static ssize_t virtual_ncidev_write(struct file *file, 109e624e6c3SBongsu Jeon const char __user *buf, 110e624e6c3SBongsu Jeon size_t count, loff_t *ppos) 111e624e6c3SBongsu Jeon { 112b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 113e624e6c3SBongsu Jeon struct sk_buff *skb; 114e624e6c3SBongsu Jeon 115e624e6c3SBongsu Jeon skb = alloc_skb(count, GFP_KERNEL); 116e624e6c3SBongsu Jeon if (!skb) 117e624e6c3SBongsu Jeon return -ENOMEM; 118e624e6c3SBongsu Jeon 119e624e6c3SBongsu Jeon if (copy_from_user(skb_put(skb, count), buf, count)) { 120e624e6c3SBongsu Jeon kfree_skb(skb); 121e624e6c3SBongsu Jeon return -EFAULT; 122e624e6c3SBongsu Jeon } 123e624e6c3SBongsu Jeon 124b2e44aacSDmitry Vyukov nci_recv_frame(vdev->ndev, skb); 125e624e6c3SBongsu Jeon return count; 126e624e6c3SBongsu Jeon } 127e624e6c3SBongsu Jeon 128e624e6c3SBongsu Jeon static int virtual_ncidev_open(struct inode *inode, struct file *file) 129e624e6c3SBongsu Jeon { 130e624e6c3SBongsu Jeon int ret = 0; 131b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev; 132e624e6c3SBongsu Jeon 133b2e44aacSDmitry Vyukov vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); 134b2e44aacSDmitry Vyukov if (!vdev) 135b2e44aacSDmitry Vyukov return -ENOMEM; 136b2e44aacSDmitry Vyukov vdev->ndev = nci_allocate_device(&virtual_nci_ops, 137b2e44aacSDmitry Vyukov VIRTUAL_NFC_PROTOCOLS, 0, 0); 138b2e44aacSDmitry Vyukov if (!vdev->ndev) { 139b2e44aacSDmitry Vyukov kfree(vdev); 140e624e6c3SBongsu Jeon return -ENOMEM; 141e624e6c3SBongsu Jeon } 142e624e6c3SBongsu Jeon 143b2e44aacSDmitry Vyukov mutex_init(&vdev->mtx); 144b2e44aacSDmitry Vyukov init_waitqueue_head(&vdev->wq); 145b2e44aacSDmitry Vyukov file->private_data = vdev; 146b2e44aacSDmitry Vyukov nci_set_drvdata(vdev->ndev, vdev); 147b2e44aacSDmitry Vyukov 148b2e44aacSDmitry Vyukov ret = nci_register_device(vdev->ndev); 149e624e6c3SBongsu Jeon if (ret < 0) { 150b2e44aacSDmitry Vyukov nci_free_device(vdev->ndev); 151b2e44aacSDmitry Vyukov mutex_destroy(&vdev->mtx); 152b2e44aacSDmitry Vyukov kfree(vdev); 153e624e6c3SBongsu Jeon return ret; 154e624e6c3SBongsu Jeon } 155e624e6c3SBongsu Jeon 156e624e6c3SBongsu Jeon return 0; 157e624e6c3SBongsu Jeon } 158e624e6c3SBongsu Jeon 159e624e6c3SBongsu Jeon static int virtual_ncidev_close(struct inode *inode, struct file *file) 160e624e6c3SBongsu Jeon { 161b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 162e624e6c3SBongsu Jeon 163b2e44aacSDmitry Vyukov nci_unregister_device(vdev->ndev); 164b2e44aacSDmitry Vyukov nci_free_device(vdev->ndev); 165b2e44aacSDmitry Vyukov mutex_destroy(&vdev->mtx); 166b2e44aacSDmitry Vyukov kfree(vdev); 167e624e6c3SBongsu Jeon 168e624e6c3SBongsu Jeon return 0; 169e624e6c3SBongsu Jeon } 170e624e6c3SBongsu Jeon 171b2e44aacSDmitry Vyukov static long virtual_ncidev_ioctl(struct file *file, unsigned int cmd, 172e624e6c3SBongsu Jeon unsigned long arg) 173e624e6c3SBongsu Jeon { 174b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 175b2e44aacSDmitry Vyukov const struct nfc_dev *nfc_dev = vdev->ndev->nfc_dev; 176e624e6c3SBongsu Jeon void __user *p = (void __user *)arg; 177e624e6c3SBongsu Jeon 178e624e6c3SBongsu Jeon if (cmd != IOCTL_GET_NCIDEV_IDX) 179e624e6c3SBongsu Jeon return -ENOTTY; 180e624e6c3SBongsu Jeon 181e624e6c3SBongsu Jeon if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx))) 182e624e6c3SBongsu Jeon return -EFAULT; 183e624e6c3SBongsu Jeon 184e624e6c3SBongsu Jeon return 0; 185e624e6c3SBongsu Jeon } 186e624e6c3SBongsu Jeon 187e624e6c3SBongsu Jeon static const struct file_operations virtual_ncidev_fops = { 188e624e6c3SBongsu Jeon .owner = THIS_MODULE, 189e624e6c3SBongsu Jeon .read = virtual_ncidev_read, 190e624e6c3SBongsu Jeon .write = virtual_ncidev_write, 191e624e6c3SBongsu Jeon .open = virtual_ncidev_open, 192e624e6c3SBongsu Jeon .release = virtual_ncidev_close, 193e624e6c3SBongsu Jeon .unlocked_ioctl = virtual_ncidev_ioctl 194e624e6c3SBongsu Jeon }; 195e624e6c3SBongsu Jeon 196b2e44aacSDmitry Vyukov static struct miscdevice miscdev = { 197b2e44aacSDmitry Vyukov .minor = MISC_DYNAMIC_MINOR, 198b2e44aacSDmitry Vyukov .name = "virtual_nci", 199b2e44aacSDmitry Vyukov .fops = &virtual_ncidev_fops, 200b2e44aacSDmitry Vyukov .mode = 0600, 201b2e44aacSDmitry Vyukov }; 202b2e44aacSDmitry Vyukov 203*61a9b174SLi Zetao module_misc_device(miscdev); 204e624e6c3SBongsu Jeon 205e624e6c3SBongsu Jeon MODULE_LICENSE("GPL"); 206e624e6c3SBongsu Jeon MODULE_DESCRIPTION("Virtual NCI device simulation driver"); 207e624e6c3SBongsu Jeon MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>"); 208