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