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