1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Virtual NCI device simulation driver
5 * Copyright (C) 2020 Samsung Electrnoics
6 * Bongsu Jeon <bongsu.jeon@samsung.com>
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>
16 enum virtual_ncidev_mode {
17 virtual_ncidev_enabled,
18 virtual_ncidev_disabled,
19 virtual_ncidev_disabling,
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)
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);
37 static int virtual_nci_open(struct nci_dev *ndev)
42 static int virtual_nci_close(struct nci_dev *ndev)
44 mutex_lock(&nci_mutex);
47 mutex_unlock(&nci_mutex);
52 static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
54 mutex_lock(&nci_mutex);
55 if (state != virtual_ncidev_enabled) {
56 mutex_unlock(&nci_mutex);
61 mutex_unlock(&nci_mutex);
64 send_buff = skb_copy(skb, GFP_KERNEL);
65 mutex_unlock(&nci_mutex);
66 wake_up_interruptible(&wq);
71 static const struct nci_ops virtual_nci_ops = {
72 .open = virtual_nci_open,
73 .close = virtual_nci_close,
74 .send = virtual_nci_send
77 static ssize_t virtual_ncidev_read(struct file *file, char __user *buf,
78 size_t count, loff_t *ppos)
82 mutex_lock(&nci_mutex);
84 mutex_unlock(&nci_mutex);
85 if (wait_event_interruptible(wq, send_buff))
87 mutex_lock(&nci_mutex);
90 actual_len = min_t(size_t, count, send_buff->len);
92 if (copy_to_user(buf, send_buff->data, actual_len)) {
93 mutex_unlock(&nci_mutex);
97 skb_pull(send_buff, actual_len);
98 if (send_buff->len == 0) {
99 consume_skb(send_buff);
102 mutex_unlock(&nci_mutex);
107 static ssize_t virtual_ncidev_write(struct file *file,
108 const char __user *buf,
109 size_t count, loff_t *ppos)
113 skb = alloc_skb(count, GFP_KERNEL);
117 if (copy_from_user(skb_put(skb, count), buf, count)) {
122 nci_recv_frame(ndev, skb);
126 static int virtual_ncidev_open(struct inode *inode, struct file *file)
130 mutex_lock(&nci_mutex);
131 if (state != virtual_ncidev_disabled) {
132 mutex_unlock(&nci_mutex);
136 ndev = nci_allocate_device(&virtual_nci_ops, VIRTUAL_NFC_PROTOCOLS,
139 mutex_unlock(&nci_mutex);
143 ret = nci_register_device(ndev);
145 nci_free_device(ndev);
146 mutex_unlock(&nci_mutex);
149 state = virtual_ncidev_enabled;
150 mutex_unlock(&nci_mutex);
155 static int virtual_ncidev_close(struct inode *inode, struct file *file)
157 mutex_lock(&nci_mutex);
159 if (state == virtual_ncidev_enabled) {
160 state = virtual_ncidev_disabling;
161 mutex_unlock(&nci_mutex);
163 nci_unregister_device(ndev);
164 nci_free_device(ndev);
166 mutex_lock(&nci_mutex);
169 state = virtual_ncidev_disabled;
170 mutex_unlock(&nci_mutex);
175 static long virtual_ncidev_ioctl(struct file *flip, unsigned int cmd,
178 const struct nfc_dev *nfc_dev = ndev->nfc_dev;
179 void __user *p = (void __user *)arg;
181 if (cmd != IOCTL_GET_NCIDEV_IDX)
184 if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx)))
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
199 static int __init virtual_ncidev_init(void)
201 state = virtual_ncidev_disabled;
202 miscdev.minor = MISC_DYNAMIC_MINOR;
203 miscdev.name = "virtual_nci";
204 miscdev.fops = &virtual_ncidev_fops;
207 return misc_register(&miscdev);
210 static void __exit virtual_ncidev_exit(void)
212 misc_deregister(&miscdev);
215 module_init(virtual_ncidev_init);
216 module_exit(virtual_ncidev_exit);
218 MODULE_LICENSE("GPL");
219 MODULE_DESCRIPTION("Virtual NCI device simulation driver");
220 MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");