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 kfree_skb(skb); 58 return 0; 59 } 60 61 if (send_buff) { 62 mutex_unlock(&nci_mutex); 63 kfree_skb(skb); 64 return -1; 65 } 66 send_buff = skb_copy(skb, GFP_KERNEL); 67 mutex_unlock(&nci_mutex); 68 wake_up_interruptible(&wq); 69 consume_skb(skb); 70 71 return 0; 72 } 73 74 static const struct nci_ops virtual_nci_ops = { 75 .open = virtual_nci_open, 76 .close = virtual_nci_close, 77 .send = virtual_nci_send 78 }; 79 80 static ssize_t virtual_ncidev_read(struct file *file, char __user *buf, 81 size_t count, loff_t *ppos) 82 { 83 size_t actual_len; 84 85 mutex_lock(&nci_mutex); 86 while (!send_buff) { 87 mutex_unlock(&nci_mutex); 88 if (wait_event_interruptible(wq, send_buff)) 89 return -EFAULT; 90 mutex_lock(&nci_mutex); 91 } 92 93 actual_len = min_t(size_t, count, send_buff->len); 94 95 if (copy_to_user(buf, send_buff->data, actual_len)) { 96 mutex_unlock(&nci_mutex); 97 return -EFAULT; 98 } 99 100 skb_pull(send_buff, actual_len); 101 if (send_buff->len == 0) { 102 consume_skb(send_buff); 103 send_buff = NULL; 104 } 105 mutex_unlock(&nci_mutex); 106 107 return actual_len; 108 } 109 110 static ssize_t virtual_ncidev_write(struct file *file, 111 const char __user *buf, 112 size_t count, loff_t *ppos) 113 { 114 struct sk_buff *skb; 115 116 skb = alloc_skb(count, GFP_KERNEL); 117 if (!skb) 118 return -ENOMEM; 119 120 if (copy_from_user(skb_put(skb, count), buf, count)) { 121 kfree_skb(skb); 122 return -EFAULT; 123 } 124 125 nci_recv_frame(ndev, skb); 126 return count; 127 } 128 129 static int virtual_ncidev_open(struct inode *inode, struct file *file) 130 { 131 int ret = 0; 132 133 mutex_lock(&nci_mutex); 134 if (state != virtual_ncidev_disabled) { 135 mutex_unlock(&nci_mutex); 136 return -EBUSY; 137 } 138 139 ndev = nci_allocate_device(&virtual_nci_ops, VIRTUAL_NFC_PROTOCOLS, 140 0, 0); 141 if (!ndev) { 142 mutex_unlock(&nci_mutex); 143 return -ENOMEM; 144 } 145 146 ret = nci_register_device(ndev); 147 if (ret < 0) { 148 nci_free_device(ndev); 149 mutex_unlock(&nci_mutex); 150 return ret; 151 } 152 state = virtual_ncidev_enabled; 153 mutex_unlock(&nci_mutex); 154 155 return 0; 156 } 157 158 static int virtual_ncidev_close(struct inode *inode, struct file *file) 159 { 160 mutex_lock(&nci_mutex); 161 162 if (state == virtual_ncidev_enabled) { 163 state = virtual_ncidev_disabling; 164 mutex_unlock(&nci_mutex); 165 166 nci_unregister_device(ndev); 167 nci_free_device(ndev); 168 169 mutex_lock(&nci_mutex); 170 } 171 172 state = virtual_ncidev_disabled; 173 mutex_unlock(&nci_mutex); 174 175 return 0; 176 } 177 178 static long virtual_ncidev_ioctl(struct file *flip, unsigned int cmd, 179 unsigned long arg) 180 { 181 const struct nfc_dev *nfc_dev = ndev->nfc_dev; 182 void __user *p = (void __user *)arg; 183 184 if (cmd != IOCTL_GET_NCIDEV_IDX) 185 return -ENOTTY; 186 187 if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx))) 188 return -EFAULT; 189 190 return 0; 191 } 192 193 static const struct file_operations virtual_ncidev_fops = { 194 .owner = THIS_MODULE, 195 .read = virtual_ncidev_read, 196 .write = virtual_ncidev_write, 197 .open = virtual_ncidev_open, 198 .release = virtual_ncidev_close, 199 .unlocked_ioctl = virtual_ncidev_ioctl 200 }; 201 202 static int __init virtual_ncidev_init(void) 203 { 204 state = virtual_ncidev_disabled; 205 miscdev.minor = MISC_DYNAMIC_MINOR; 206 miscdev.name = "virtual_nci"; 207 miscdev.fops = &virtual_ncidev_fops; 208 miscdev.mode = 0600; 209 210 return misc_register(&miscdev); 211 } 212 213 static void __exit virtual_ncidev_exit(void) 214 { 215 misc_deregister(&miscdev); 216 } 217 218 module_init(virtual_ncidev_init); 219 module_exit(virtual_ncidev_exit); 220 221 MODULE_LICENSE("GPL"); 222 MODULE_DESCRIPTION("Virtual NCI device simulation driver"); 223 MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>"); 224