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 #define IOCTL_GET_NCIDEV_IDX 0
17 #define VIRTUAL_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
18 NFC_PROTO_MIFARE_MASK | \
19 NFC_PROTO_FELICA_MASK | \
20 NFC_PROTO_ISO14443_MASK | \
21 NFC_PROTO_ISO14443_B_MASK | \
22 NFC_PROTO_ISO15693_MASK)
24 struct virtual_nci_dev
{
27 struct sk_buff
*send_buff
;
28 struct wait_queue_head wq
;
32 static int virtual_nci_open(struct nci_dev
*ndev
)
34 struct virtual_nci_dev
*vdev
= nci_get_drvdata(ndev
);
40 static int virtual_nci_close(struct nci_dev
*ndev
)
42 struct virtual_nci_dev
*vdev
= nci_get_drvdata(ndev
);
44 mutex_lock(&vdev
->mtx
);
45 kfree_skb(vdev
->send_buff
);
46 vdev
->send_buff
= NULL
;
47 vdev
->running
= false;
48 mutex_unlock(&vdev
->mtx
);
53 static int virtual_nci_send(struct nci_dev
*ndev
, struct sk_buff
*skb
)
55 struct virtual_nci_dev
*vdev
= nci_get_drvdata(ndev
);
57 mutex_lock(&vdev
->mtx
);
58 if (vdev
->send_buff
|| !vdev
->running
) {
59 mutex_unlock(&vdev
->mtx
);
63 vdev
->send_buff
= skb_copy(skb
, GFP_KERNEL
);
64 if (!vdev
->send_buff
) {
65 mutex_unlock(&vdev
->mtx
);
69 mutex_unlock(&vdev
->mtx
);
70 wake_up_interruptible(&vdev
->wq
);
76 static const struct nci_ops virtual_nci_ops
= {
77 .open
= virtual_nci_open
,
78 .close
= virtual_nci_close
,
79 .send
= virtual_nci_send
82 static ssize_t
virtual_ncidev_read(struct file
*file
, char __user
*buf
,
83 size_t count
, loff_t
*ppos
)
85 struct virtual_nci_dev
*vdev
= file
->private_data
;
88 mutex_lock(&vdev
->mtx
);
89 while (!vdev
->send_buff
) {
90 mutex_unlock(&vdev
->mtx
);
91 if (wait_event_interruptible(vdev
->wq
, vdev
->send_buff
))
93 mutex_lock(&vdev
->mtx
);
96 actual_len
= min_t(size_t, count
, vdev
->send_buff
->len
);
98 if (copy_to_user(buf
, vdev
->send_buff
->data
, actual_len
)) {
99 mutex_unlock(&vdev
->mtx
);
103 skb_pull(vdev
->send_buff
, actual_len
);
104 if (vdev
->send_buff
->len
== 0) {
105 consume_skb(vdev
->send_buff
);
106 vdev
->send_buff
= NULL
;
108 mutex_unlock(&vdev
->mtx
);
113 static ssize_t
virtual_ncidev_write(struct file
*file
,
114 const char __user
*buf
,
115 size_t count
, loff_t
*ppos
)
117 struct virtual_nci_dev
*vdev
= file
->private_data
;
120 skb
= alloc_skb(count
, GFP_KERNEL
);
124 if (copy_from_user(skb_put(skb
, count
), buf
, count
)) {
128 if (strnlen(skb
->data
, count
) != count
) {
133 nci_recv_frame(vdev
->ndev
, skb
);
137 static int virtual_ncidev_open(struct inode
*inode
, struct file
*file
)
140 struct virtual_nci_dev
*vdev
;
142 vdev
= kzalloc(sizeof(*vdev
), GFP_KERNEL
);
145 vdev
->ndev
= nci_allocate_device(&virtual_nci_ops
,
146 VIRTUAL_NFC_PROTOCOLS
, 0, 0);
152 mutex_init(&vdev
->mtx
);
153 init_waitqueue_head(&vdev
->wq
);
154 file
->private_data
= vdev
;
155 nci_set_drvdata(vdev
->ndev
, vdev
);
157 ret
= nci_register_device(vdev
->ndev
);
159 nci_free_device(vdev
->ndev
);
160 mutex_destroy(&vdev
->mtx
);
168 static int virtual_ncidev_close(struct inode
*inode
, struct file
*file
)
170 struct virtual_nci_dev
*vdev
= file
->private_data
;
172 nci_unregister_device(vdev
->ndev
);
173 nci_free_device(vdev
->ndev
);
174 mutex_destroy(&vdev
->mtx
);
180 static long virtual_ncidev_ioctl(struct file
*file
, unsigned int cmd
,
183 struct virtual_nci_dev
*vdev
= file
->private_data
;
184 const struct nfc_dev
*nfc_dev
= vdev
->ndev
->nfc_dev
;
185 void __user
*p
= (void __user
*)arg
;
187 if (cmd
!= IOCTL_GET_NCIDEV_IDX
)
190 if (copy_to_user(p
, &nfc_dev
->idx
, sizeof(nfc_dev
->idx
)))
196 static const struct file_operations virtual_ncidev_fops
= {
197 .owner
= THIS_MODULE
,
198 .read
= virtual_ncidev_read
,
199 .write
= virtual_ncidev_write
,
200 .open
= virtual_ncidev_open
,
201 .release
= virtual_ncidev_close
,
202 .unlocked_ioctl
= virtual_ncidev_ioctl
205 static struct miscdevice miscdev
= {
206 .minor
= MISC_DYNAMIC_MINOR
,
207 .name
= "virtual_nci",
208 .fops
= &virtual_ncidev_fops
,
212 module_misc_device(miscdev
);
214 MODULE_LICENSE("GPL");
215 MODULE_DESCRIPTION("Virtual NCI device simulation driver");
216 MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");