xref: /openbmc/linux/drivers/nfc/virtual_ncidev.c (revision ee7da21a)
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 		return 0;
58 	}
59 
60 	if (send_buff) {
61 		mutex_unlock(&nci_mutex);
62 		return -1;
63 	}
64 	send_buff = skb_copy(skb, GFP_KERNEL);
65 	mutex_unlock(&nci_mutex);
66 	wake_up_interruptible(&wq);
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 	size_t actual_len;
81 
82 	mutex_lock(&nci_mutex);
83 	while (!send_buff) {
84 		mutex_unlock(&nci_mutex);
85 		if (wait_event_interruptible(wq, send_buff))
86 			return -EFAULT;
87 		mutex_lock(&nci_mutex);
88 	}
89 
90 	actual_len = min_t(size_t, count, send_buff->len);
91 
92 	if (copy_to_user(buf, send_buff->data, actual_len)) {
93 		mutex_unlock(&nci_mutex);
94 		return -EFAULT;
95 	}
96 
97 	skb_pull(send_buff, actual_len);
98 	if (send_buff->len == 0) {
99 		consume_skb(send_buff);
100 		send_buff = NULL;
101 	}
102 	mutex_unlock(&nci_mutex);
103 
104 	return actual_len;
105 }
106 
107 static ssize_t virtual_ncidev_write(struct file *file,
108 				    const char __user *buf,
109 				    size_t count, loff_t *ppos)
110 {
111 	struct sk_buff *skb;
112 
113 	skb = alloc_skb(count, GFP_KERNEL);
114 	if (!skb)
115 		return -ENOMEM;
116 
117 	if (copy_from_user(skb_put(skb, count), buf, count)) {
118 		kfree_skb(skb);
119 		return -EFAULT;
120 	}
121 
122 	nci_recv_frame(ndev, skb);
123 	return count;
124 }
125 
126 static int virtual_ncidev_open(struct inode *inode, struct file *file)
127 {
128 	int ret = 0;
129 
130 	mutex_lock(&nci_mutex);
131 	if (state != virtual_ncidev_disabled) {
132 		mutex_unlock(&nci_mutex);
133 		return -EBUSY;
134 	}
135 
136 	ndev = nci_allocate_device(&virtual_nci_ops, VIRTUAL_NFC_PROTOCOLS,
137 				   0, 0);
138 	if (!ndev) {
139 		mutex_unlock(&nci_mutex);
140 		return -ENOMEM;
141 	}
142 
143 	ret = nci_register_device(ndev);
144 	if (ret < 0) {
145 		nci_free_device(ndev);
146 		mutex_unlock(&nci_mutex);
147 		return ret;
148 	}
149 	state = virtual_ncidev_enabled;
150 	mutex_unlock(&nci_mutex);
151 
152 	return 0;
153 }
154 
155 static int virtual_ncidev_close(struct inode *inode, struct file *file)
156 {
157 	mutex_lock(&nci_mutex);
158 
159 	if (state == virtual_ncidev_enabled) {
160 		state = virtual_ncidev_disabling;
161 		mutex_unlock(&nci_mutex);
162 
163 		nci_unregister_device(ndev);
164 		nci_free_device(ndev);
165 
166 		mutex_lock(&nci_mutex);
167 	}
168 
169 	state = virtual_ncidev_disabled;
170 	mutex_unlock(&nci_mutex);
171 
172 	return 0;
173 }
174 
175 static long virtual_ncidev_ioctl(struct file *flip, unsigned int cmd,
176 				 unsigned long arg)
177 {
178 	const struct nfc_dev *nfc_dev = ndev->nfc_dev;
179 	void __user *p = (void __user *)arg;
180 
181 	if (cmd != IOCTL_GET_NCIDEV_IDX)
182 		return -ENOTTY;
183 
184 	if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx)))
185 		return -EFAULT;
186 
187 	return 0;
188 }
189 
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
197 };
198 
199 static int __init virtual_ncidev_init(void)
200 {
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;
206 
207 	return misc_register(&miscdev);
208 }
209 
210 static void __exit virtual_ncidev_exit(void)
211 {
212 	misc_deregister(&miscdev);
213 }
214 
215 module_init(virtual_ncidev_init);
216 module_exit(virtual_ncidev_exit);
217 
218 MODULE_LICENSE("GPL");
219 MODULE_DESCRIPTION("Virtual NCI device simulation driver");
220 MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");
221