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 | #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) |
23 | |
24 | struct virtual_nci_dev { |
25 | struct nci_dev *ndev; |
26 | struct mutex mtx; |
27 | struct sk_buff *send_buff; |
28 | struct wait_queue_head wq; |
29 | }; |
30 | |
31 | static int virtual_nci_open(struct nci_dev *ndev) |
32 | { |
33 | return 0; |
34 | } |
35 | |
36 | static int virtual_nci_close(struct nci_dev *ndev) |
37 | { |
38 | struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); |
39 | |
40 | mutex_lock(&vdev->mtx); |
41 | kfree_skb(skb: vdev->send_buff); |
42 | vdev->send_buff = NULL; |
43 | mutex_unlock(lock: &vdev->mtx); |
44 | |
45 | return 0; |
46 | } |
47 | |
48 | static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb) |
49 | { |
50 | struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); |
51 | |
52 | mutex_lock(&vdev->mtx); |
53 | if (vdev->send_buff) { |
54 | mutex_unlock(lock: &vdev->mtx); |
55 | kfree_skb(skb); |
56 | return -1; |
57 | } |
58 | vdev->send_buff = skb_copy(skb, GFP_KERNEL); |
59 | if (!vdev->send_buff) { |
60 | mutex_unlock(lock: &vdev->mtx); |
61 | kfree_skb(skb); |
62 | return -1; |
63 | } |
64 | mutex_unlock(lock: &vdev->mtx); |
65 | wake_up_interruptible(&vdev->wq); |
66 | consume_skb(skb); |
67 | |
68 | return 0; |
69 | } |
70 | |
71 | static const struct nci_ops virtual_nci_ops = { |
72 | .open = virtual_nci_open, |
73 | .close = virtual_nci_close, |
74 | .send = virtual_nci_send |
75 | }; |
76 | |
77 | static ssize_t virtual_ncidev_read(struct file *file, char __user *buf, |
78 | size_t count, loff_t *ppos) |
79 | { |
80 | struct virtual_nci_dev *vdev = file->private_data; |
81 | size_t actual_len; |
82 | |
83 | mutex_lock(&vdev->mtx); |
84 | while (!vdev->send_buff) { |
85 | mutex_unlock(lock: &vdev->mtx); |
86 | if (wait_event_interruptible(vdev->wq, vdev->send_buff)) |
87 | return -EFAULT; |
88 | mutex_lock(&vdev->mtx); |
89 | } |
90 | |
91 | actual_len = min_t(size_t, count, vdev->send_buff->len); |
92 | |
93 | if (copy_to_user(to: buf, from: vdev->send_buff->data, n: actual_len)) { |
94 | mutex_unlock(lock: &vdev->mtx); |
95 | return -EFAULT; |
96 | } |
97 | |
98 | skb_pull(skb: vdev->send_buff, len: actual_len); |
99 | if (vdev->send_buff->len == 0) { |
100 | consume_skb(skb: vdev->send_buff); |
101 | vdev->send_buff = NULL; |
102 | } |
103 | mutex_unlock(lock: &vdev->mtx); |
104 | |
105 | return actual_len; |
106 | } |
107 | |
108 | static ssize_t virtual_ncidev_write(struct file *file, |
109 | const char __user *buf, |
110 | size_t count, loff_t *ppos) |
111 | { |
112 | struct virtual_nci_dev *vdev = file->private_data; |
113 | struct sk_buff *skb; |
114 | |
115 | skb = alloc_skb(size: count, GFP_KERNEL); |
116 | if (!skb) |
117 | return -ENOMEM; |
118 | |
119 | if (copy_from_user(to: skb_put(skb, len: count), from: buf, n: count)) { |
120 | kfree_skb(skb); |
121 | return -EFAULT; |
122 | } |
123 | |
124 | nci_recv_frame(ndev: vdev->ndev, skb); |
125 | return count; |
126 | } |
127 | |
128 | static int virtual_ncidev_open(struct inode *inode, struct file *file) |
129 | { |
130 | int ret = 0; |
131 | struct virtual_nci_dev *vdev; |
132 | |
133 | vdev = kzalloc(size: sizeof(*vdev), GFP_KERNEL); |
134 | if (!vdev) |
135 | return -ENOMEM; |
136 | vdev->ndev = nci_allocate_device(ops: &virtual_nci_ops, |
137 | VIRTUAL_NFC_PROTOCOLS, tx_headroom: 0, tx_tailroom: 0); |
138 | if (!vdev->ndev) { |
139 | kfree(objp: vdev); |
140 | return -ENOMEM; |
141 | } |
142 | |
143 | mutex_init(&vdev->mtx); |
144 | init_waitqueue_head(&vdev->wq); |
145 | file->private_data = vdev; |
146 | nci_set_drvdata(ndev: vdev->ndev, data: vdev); |
147 | |
148 | ret = nci_register_device(ndev: vdev->ndev); |
149 | if (ret < 0) { |
150 | nci_free_device(ndev: vdev->ndev); |
151 | mutex_destroy(lock: &vdev->mtx); |
152 | kfree(objp: vdev); |
153 | return ret; |
154 | } |
155 | |
156 | return 0; |
157 | } |
158 | |
159 | static int virtual_ncidev_close(struct inode *inode, struct file *file) |
160 | { |
161 | struct virtual_nci_dev *vdev = file->private_data; |
162 | |
163 | nci_unregister_device(ndev: vdev->ndev); |
164 | nci_free_device(ndev: vdev->ndev); |
165 | mutex_destroy(lock: &vdev->mtx); |
166 | kfree(objp: vdev); |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | static long virtual_ncidev_ioctl(struct file *file, unsigned int cmd, |
172 | unsigned long arg) |
173 | { |
174 | struct virtual_nci_dev *vdev = file->private_data; |
175 | const struct nfc_dev *nfc_dev = vdev->ndev->nfc_dev; |
176 | void __user *p = (void __user *)arg; |
177 | |
178 | if (cmd != IOCTL_GET_NCIDEV_IDX) |
179 | return -ENOTTY; |
180 | |
181 | if (copy_to_user(to: p, from: &nfc_dev->idx, n: sizeof(nfc_dev->idx))) |
182 | return -EFAULT; |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | static const struct file_operations virtual_ncidev_fops = { |
188 | .owner = THIS_MODULE, |
189 | .read = virtual_ncidev_read, |
190 | .write = virtual_ncidev_write, |
191 | .open = virtual_ncidev_open, |
192 | .release = virtual_ncidev_close, |
193 | .unlocked_ioctl = virtual_ncidev_ioctl |
194 | }; |
195 | |
196 | static struct miscdevice miscdev = { |
197 | .minor = MISC_DYNAMIC_MINOR, |
198 | .name = "virtual_nci" , |
199 | .fops = &virtual_ncidev_fops, |
200 | .mode = 0600, |
201 | }; |
202 | |
203 | module_misc_device(miscdev); |
204 | |
205 | MODULE_LICENSE("GPL" ); |
206 | MODULE_DESCRIPTION("Virtual NCI device simulation driver" ); |
207 | MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>" ); |
208 | |