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