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