Merge tag 'for-linus' of git://github.com/openrisc/linux
[linux-2.6-microblaze.git] / drivers / nfc / virtual_ncidev.c
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>");