1 /*
2 BlueZ - Bluetooth protocol stack for Linux
3 Copyright (C) 2000-2001 Qualcomm Incorporated
4
5 Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation;
10
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
12 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
14 IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
15 CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
16 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20 ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
21 COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
22 SOFTWARE IS DISCLAIMED.
23 */
24
25 /*
26 * BlueZ HCI virtual device driver.
27 *
28 * $Id: hci_vhci.c,v 1.3 2002/04/17 17:37:20 maxk Exp $
29 */
30 #define VERSION "1.1"
31
32 #include <linux/config.h>
33 #include <linux/module.h>
34
35 #include <linux/errno.h>
36 #include <linux/kernel.h>
37 #include <linux/major.h>
38 #include <linux/sched.h>
39 #include <linux/slab.h>
40 #include <linux/poll.h>
41 #include <linux/fcntl.h>
42 #include <linux/init.h>
43 #include <linux/random.h>
44
45 #include <linux/skbuff.h>
46 #include <linux/miscdevice.h>
47
48 #include <asm/system.h>
49 #include <asm/uaccess.h>
50
51 #include <net/bluetooth/bluetooth.h>
52 #include <net/bluetooth/hci_core.h>
53 #include "hci_vhci.h"
54
55 /* HCI device part */
56
hci_vhci_open(struct hci_dev * hdev)57 static int hci_vhci_open(struct hci_dev *hdev)
58 {
59 set_bit(HCI_RUNNING, &hdev->flags);
60 return 0;
61 }
62
hci_vhci_flush(struct hci_dev * hdev)63 static int hci_vhci_flush(struct hci_dev *hdev)
64 {
65 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
66 skb_queue_purge(&hci_vhci->readq);
67 return 0;
68 }
69
hci_vhci_close(struct hci_dev * hdev)70 static int hci_vhci_close(struct hci_dev *hdev)
71 {
72 if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
73 return 0;
74
75 hci_vhci_flush(hdev);
76 return 0;
77 }
78
hci_vhci_destruct(struct hci_dev * hdev)79 static void hci_vhci_destruct(struct hci_dev *hdev)
80 {
81 struct hci_vhci_struct *vhci;
82
83 if (!hdev) return;
84
85 vhci = (struct hci_vhci_struct *) hdev->driver_data;
86 kfree(vhci);
87
88 MOD_DEC_USE_COUNT;
89 }
90
hci_vhci_send_frame(struct sk_buff * skb)91 static int hci_vhci_send_frame(struct sk_buff *skb)
92 {
93 struct hci_dev* hdev = (struct hci_dev *) skb->dev;
94 struct hci_vhci_struct *hci_vhci;
95
96 if (!hdev) {
97 BT_ERR("Frame for uknown device (hdev=NULL)");
98 return -ENODEV;
99 }
100
101 if (!test_bit(HCI_RUNNING, &hdev->flags))
102 return -EBUSY;
103
104 hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
105
106 memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
107 skb_queue_tail(&hci_vhci->readq, skb);
108
109 if (hci_vhci->flags & VHCI_FASYNC)
110 kill_fasync(&hci_vhci->fasync, SIGIO, POLL_IN);
111 wake_up_interruptible(&hci_vhci->read_wait);
112
113 return 0;
114 }
115
116 /* Character device part */
117
118 /* Poll */
hci_vhci_chr_poll(struct file * file,poll_table * wait)119 static unsigned int hci_vhci_chr_poll(struct file *file, poll_table * wait)
120 {
121 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
122
123 poll_wait(file, &hci_vhci->read_wait, wait);
124
125 if (skb_queue_len(&hci_vhci->readq))
126 return POLLIN | POLLRDNORM;
127
128 return POLLOUT | POLLWRNORM;
129 }
130
131 /* Get packet from user space buffer(already verified) */
hci_vhci_get_user(struct hci_vhci_struct * hci_vhci,const char * buf,size_t count)132 static inline ssize_t hci_vhci_get_user(struct hci_vhci_struct *hci_vhci, const char *buf, size_t count)
133 {
134 struct sk_buff *skb;
135
136 if (count > HCI_MAX_FRAME_SIZE)
137 return -EINVAL;
138
139 if (!(skb = bluez_skb_alloc(count, GFP_KERNEL)))
140 return -ENOMEM;
141
142 copy_from_user(skb_put(skb, count), buf, count);
143
144 skb->dev = (void *) &hci_vhci->hdev;
145 skb->pkt_type = *((__u8 *) skb->data);
146 skb_pull(skb, 1);
147
148 hci_recv_frame(skb);
149
150 return count;
151 }
152
153 /* Write */
hci_vhci_chr_write(struct file * file,const char * buf,size_t count,loff_t * pos)154 static ssize_t hci_vhci_chr_write(struct file * file, const char * buf,
155 size_t count, loff_t *pos)
156 {
157 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
158
159 if (verify_area(VERIFY_READ, buf, count))
160 return -EFAULT;
161
162 return hci_vhci_get_user(hci_vhci, buf, count);
163 }
164
165 /* Put packet to user space buffer(already verified) */
hci_vhci_put_user(struct hci_vhci_struct * hci_vhci,struct sk_buff * skb,char * buf,int count)166 static inline ssize_t hci_vhci_put_user(struct hci_vhci_struct *hci_vhci,
167 struct sk_buff *skb, char *buf, int count)
168 {
169 int len = count, total = 0;
170 char *ptr = buf;
171
172 len = MIN(skb->len, len);
173 copy_to_user(ptr, skb->data, len);
174 total += len;
175
176 hci_vhci->hdev.stat.byte_tx += len;
177 switch (skb->pkt_type) {
178 case HCI_COMMAND_PKT:
179 hci_vhci->hdev.stat.cmd_tx++;
180 break;
181
182 case HCI_ACLDATA_PKT:
183 hci_vhci->hdev.stat.acl_tx++;
184 break;
185
186 case HCI_SCODATA_PKT:
187 hci_vhci->hdev.stat.cmd_tx++;
188 break;
189 };
190
191 return total;
192 }
193
194 /* Read */
hci_vhci_chr_read(struct file * file,char * buf,size_t count,loff_t * pos)195 static ssize_t hci_vhci_chr_read(struct file * file, char * buf, size_t count, loff_t *pos)
196 {
197 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
198 DECLARE_WAITQUEUE(wait, current);
199 struct sk_buff *skb;
200 ssize_t ret = 0;
201
202 add_wait_queue(&hci_vhci->read_wait, &wait);
203 while (count) {
204 set_current_state(TASK_INTERRUPTIBLE);
205
206 /* Read frames from device queue */
207 if (!(skb = skb_dequeue(&hci_vhci->readq))) {
208 if (file->f_flags & O_NONBLOCK) {
209 ret = -EAGAIN;
210 break;
211 }
212 if (signal_pending(current)) {
213 ret = -ERESTARTSYS;
214 break;
215 }
216
217 /* Nothing to read, let's sleep */
218 schedule();
219 continue;
220 }
221
222 if (!verify_area(VERIFY_WRITE, buf, count))
223 ret = hci_vhci_put_user(hci_vhci, skb, buf, count);
224 else
225 ret = -EFAULT;
226
227 kfree_skb(skb);
228 break;
229 }
230 set_current_state(TASK_RUNNING);
231 remove_wait_queue(&hci_vhci->read_wait, &wait);
232
233 return ret;
234 }
235
hci_vhci_chr_lseek(struct file * file,loff_t offset,int origin)236 static loff_t hci_vhci_chr_lseek(struct file * file, loff_t offset, int origin)
237 {
238 return -ESPIPE;
239 }
240
hci_vhci_chr_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)241 static int hci_vhci_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
242 {
243 return -EINVAL;
244 }
245
hci_vhci_chr_fasync(int fd,struct file * file,int on)246 static int hci_vhci_chr_fasync(int fd, struct file *file, int on)
247 {
248 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
249 int ret;
250
251 if ((ret = fasync_helper(fd, file, on, &hci_vhci->fasync)) < 0)
252 return ret;
253
254 if (on)
255 hci_vhci->flags |= VHCI_FASYNC;
256 else
257 hci_vhci->flags &= ~VHCI_FASYNC;
258
259 return 0;
260 }
261
hci_vhci_chr_open(struct inode * inode,struct file * file)262 static int hci_vhci_chr_open(struct inode *inode, struct file * file)
263 {
264 struct hci_vhci_struct *hci_vhci = NULL;
265 struct hci_dev *hdev;
266
267 if (!(hci_vhci = kmalloc(sizeof(struct hci_vhci_struct), GFP_KERNEL)))
268 return -ENOMEM;
269
270 memset(hci_vhci, 0, sizeof(struct hci_vhci_struct));
271
272 skb_queue_head_init(&hci_vhci->readq);
273 init_waitqueue_head(&hci_vhci->read_wait);
274
275 /* Initialize and register HCI device */
276 hdev = &hci_vhci->hdev;
277
278 hdev->type = HCI_VHCI;
279 hdev->driver_data = hci_vhci;
280
281 hdev->open = hci_vhci_open;
282 hdev->close = hci_vhci_close;
283 hdev->flush = hci_vhci_flush;
284 hdev->send = hci_vhci_send_frame;
285 hdev->destruct = hci_vhci_destruct;
286
287 if (hci_register_dev(hdev) < 0) {
288 kfree(hci_vhci);
289 return -EBUSY;
290 }
291 MOD_INC_USE_COUNT;
292
293 file->private_data = hci_vhci;
294 return 0;
295 }
296
hci_vhci_chr_close(struct inode * inode,struct file * file)297 static int hci_vhci_chr_close(struct inode *inode, struct file *file)
298 {
299 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
300
301 if (hci_unregister_dev(&hci_vhci->hdev) < 0) {
302 BT_ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
303 }
304
305 file->private_data = NULL;
306 return 0;
307 }
308
309 static struct file_operations hci_vhci_fops = {
310 owner: THIS_MODULE,
311 llseek: hci_vhci_chr_lseek,
312 read: hci_vhci_chr_read,
313 write: hci_vhci_chr_write,
314 poll: hci_vhci_chr_poll,
315 ioctl: hci_vhci_chr_ioctl,
316 open: hci_vhci_chr_open,
317 release:hci_vhci_chr_close,
318 fasync: hci_vhci_chr_fasync
319 };
320
321 static struct miscdevice hci_vhci_miscdev=
322 {
323 VHCI_MINOR,
324 "hci_vhci",
325 &hci_vhci_fops
326 };
327
hci_vhci_init(void)328 int __init hci_vhci_init(void)
329 {
330 BT_INFO("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
331 VERSION);
332 BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
333
334 if (misc_register(&hci_vhci_miscdev)) {
335 BT_ERR("Can't register misc device %d\n", VHCI_MINOR);
336 return -EIO;
337 }
338
339 return 0;
340 }
341
hci_vhci_cleanup(void)342 void hci_vhci_cleanup(void)
343 {
344 misc_deregister(&hci_vhci_miscdev);
345 }
346
347 module_init(hci_vhci_init);
348 module_exit(hci_vhci_cleanup);
349
350 MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
351 MODULE_DESCRIPTION("BlueZ VHCI driver ver " VERSION);
352 MODULE_LICENSE("GPL");
353