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
;
205 miscdev
.mode
= S_IALLUGO
;
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>");