Merge tag 'mips_6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
[platform/kernel/linux-starfive.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 #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>");