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; 29*84d2db91SNguyen Dinh Phi bool running; 30b2e44aacSDmitry Vyukov }; 31e624e6c3SBongsu Jeon 32e624e6c3SBongsu Jeon static int virtual_nci_open(struct nci_dev *ndev) 33e624e6c3SBongsu Jeon { 34*84d2db91SNguyen Dinh Phi struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); 35*84d2db91SNguyen Dinh Phi 36*84d2db91SNguyen 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; 47*84d2db91SNguyen 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); 58*84d2db91SNguyen 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 } 128e624e6c3SBongsu Jeon 129b2e44aacSDmitry Vyukov nci_recv_frame(vdev->ndev, skb); 130e624e6c3SBongsu Jeon return count; 131e624e6c3SBongsu Jeon } 132e624e6c3SBongsu Jeon 133e624e6c3SBongsu Jeon static int virtual_ncidev_open(struct inode *inode, struct file *file) 134e624e6c3SBongsu Jeon { 135e624e6c3SBongsu Jeon int ret = 0; 136b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev; 137e624e6c3SBongsu Jeon 138b2e44aacSDmitry Vyukov vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); 139b2e44aacSDmitry Vyukov if (!vdev) 140b2e44aacSDmitry Vyukov return -ENOMEM; 141b2e44aacSDmitry Vyukov vdev->ndev = nci_allocate_device(&virtual_nci_ops, 142b2e44aacSDmitry Vyukov VIRTUAL_NFC_PROTOCOLS, 0, 0); 143b2e44aacSDmitry Vyukov if (!vdev->ndev) { 144b2e44aacSDmitry Vyukov kfree(vdev); 145e624e6c3SBongsu Jeon return -ENOMEM; 146e624e6c3SBongsu Jeon } 147e624e6c3SBongsu Jeon 148b2e44aacSDmitry Vyukov mutex_init(&vdev->mtx); 149b2e44aacSDmitry Vyukov init_waitqueue_head(&vdev->wq); 150b2e44aacSDmitry Vyukov file->private_data = vdev; 151b2e44aacSDmitry Vyukov nci_set_drvdata(vdev->ndev, vdev); 152b2e44aacSDmitry Vyukov 153b2e44aacSDmitry Vyukov ret = nci_register_device(vdev->ndev); 154e624e6c3SBongsu Jeon if (ret < 0) { 155b2e44aacSDmitry Vyukov nci_free_device(vdev->ndev); 156b2e44aacSDmitry Vyukov mutex_destroy(&vdev->mtx); 157b2e44aacSDmitry Vyukov kfree(vdev); 158e624e6c3SBongsu Jeon return ret; 159e624e6c3SBongsu Jeon } 160e624e6c3SBongsu Jeon 161e624e6c3SBongsu Jeon return 0; 162e624e6c3SBongsu Jeon } 163e624e6c3SBongsu Jeon 164e624e6c3SBongsu Jeon static int virtual_ncidev_close(struct inode *inode, struct file *file) 165e624e6c3SBongsu Jeon { 166b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 167e624e6c3SBongsu Jeon 168b2e44aacSDmitry Vyukov nci_unregister_device(vdev->ndev); 169b2e44aacSDmitry Vyukov nci_free_device(vdev->ndev); 170b2e44aacSDmitry Vyukov mutex_destroy(&vdev->mtx); 171b2e44aacSDmitry Vyukov kfree(vdev); 172e624e6c3SBongsu Jeon 173e624e6c3SBongsu Jeon return 0; 174e624e6c3SBongsu Jeon } 175e624e6c3SBongsu Jeon 176b2e44aacSDmitry Vyukov static long virtual_ncidev_ioctl(struct file *file, unsigned int cmd, 177e624e6c3SBongsu Jeon unsigned long arg) 178e624e6c3SBongsu Jeon { 179b2e44aacSDmitry Vyukov struct virtual_nci_dev *vdev = file->private_data; 180b2e44aacSDmitry Vyukov const struct nfc_dev *nfc_dev = vdev->ndev->nfc_dev; 181e624e6c3SBongsu Jeon void __user *p = (void __user *)arg; 182e624e6c3SBongsu Jeon 183e624e6c3SBongsu Jeon if (cmd != IOCTL_GET_NCIDEV_IDX) 184e624e6c3SBongsu Jeon return -ENOTTY; 185e624e6c3SBongsu Jeon 186e624e6c3SBongsu Jeon if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx))) 187e624e6c3SBongsu Jeon return -EFAULT; 188e624e6c3SBongsu Jeon 189e624e6c3SBongsu Jeon return 0; 190e624e6c3SBongsu Jeon } 191e624e6c3SBongsu Jeon 192e624e6c3SBongsu Jeon static const struct file_operations virtual_ncidev_fops = { 193e624e6c3SBongsu Jeon .owner = THIS_MODULE, 194e624e6c3SBongsu Jeon .read = virtual_ncidev_read, 195e624e6c3SBongsu Jeon .write = virtual_ncidev_write, 196e624e6c3SBongsu Jeon .open = virtual_ncidev_open, 197e624e6c3SBongsu Jeon .release = virtual_ncidev_close, 198e624e6c3SBongsu Jeon .unlocked_ioctl = virtual_ncidev_ioctl 199e624e6c3SBongsu Jeon }; 200e624e6c3SBongsu Jeon 201b2e44aacSDmitry Vyukov static struct miscdevice miscdev = { 202b2e44aacSDmitry Vyukov .minor = MISC_DYNAMIC_MINOR, 203b2e44aacSDmitry Vyukov .name = "virtual_nci", 204b2e44aacSDmitry Vyukov .fops = &virtual_ncidev_fops, 205b2e44aacSDmitry Vyukov .mode = 0600, 206b2e44aacSDmitry Vyukov }; 207b2e44aacSDmitry Vyukov 20861a9b174SLi Zetao module_misc_device(miscdev); 209e624e6c3SBongsu Jeon 210e624e6c3SBongsu Jeon MODULE_LICENSE("GPL"); 211e624e6c3SBongsu Jeon MODULE_DESCRIPTION("Virtual NCI device simulation driver"); 212e624e6c3SBongsu Jeon MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>"); 213