xref: /openbmc/linux/drivers/nfc/virtual_ncidev.c (revision 3fc7c707)
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(vdev->send_buff);
42 	vdev->send_buff = NULL;
43 	mutex_unlock(&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(&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(&vdev->mtx);
61 		kfree_skb(skb);
62 		return -1;
63 	}
64 	mutex_unlock(&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(&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(buf, vdev->send_buff->data, actual_len)) {
94 		mutex_unlock(&vdev->mtx);
95 		return -EFAULT;
96 	}
97 
98 	skb_pull(vdev->send_buff, actual_len);
99 	if (vdev->send_buff->len == 0) {
100 		consume_skb(vdev->send_buff);
101 		vdev->send_buff = NULL;
102 	}
103 	mutex_unlock(&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(count, GFP_KERNEL);
116 	if (!skb)
117 		return -ENOMEM;
118 
119 	if (copy_from_user(skb_put(skb, count), buf, count)) {
120 		kfree_skb(skb);
121 		return -EFAULT;
122 	}
123 
124 	nci_recv_frame(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(sizeof(*vdev), GFP_KERNEL);
134 	if (!vdev)
135 		return -ENOMEM;
136 	vdev->ndev = nci_allocate_device(&virtual_nci_ops,
137 		VIRTUAL_NFC_PROTOCOLS, 0, 0);
138 	if (!vdev->ndev) {
139 		kfree(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(vdev->ndev, vdev);
147 
148 	ret = nci_register_device(vdev->ndev);
149 	if (ret < 0) {
150 		nci_free_device(vdev->ndev);
151 		mutex_destroy(&vdev->mtx);
152 		kfree(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(vdev->ndev);
164 	nci_free_device(vdev->ndev);
165 	mutex_destroy(&vdev->mtx);
166 	kfree(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(p, &nfc_dev->idx, 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