aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth/hci_vhci.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/bluetooth/hci_vhci.c
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/bluetooth/hci_vhci.c')
-rw-r--r--drivers/bluetooth/hci_vhci.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
new file mode 100644
index 000000000000..3256192dcde8
--- /dev/null
+++ b/drivers/bluetooth/hci_vhci.c
@@ -0,0 +1,364 @@
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 * Bluetooth 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
57static int hci_vhci_open(struct hci_dev *hdev)
58{
59 set_bit(HCI_RUNNING, &hdev->flags);
60 return 0;
61}
62
63static 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
70static 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
79static 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
89static int hci_vhci_send_frame(struct sk_buff *skb)
90{
91 struct hci_dev* hdev = (struct hci_dev *) skb->dev;
92 struct hci_vhci_struct *hci_vhci;
93
94 if (!hdev) {
95 BT_ERR("Frame for uknown device (hdev=NULL)");
96 return -ENODEV;
97 }
98
99 if (!test_bit(HCI_RUNNING, &hdev->flags))
100 return -EBUSY;
101
102 hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
103
104 memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
105 skb_queue_tail(&hci_vhci->readq, skb);
106
107 if (hci_vhci->flags & VHCI_FASYNC)
108 kill_fasync(&hci_vhci->fasync, SIGIO, POLL_IN);
109 wake_up_interruptible(&hci_vhci->read_wait);
110
111 return 0;
112}
113
114/* Character device part */
115
116/* Poll */
117static unsigned int hci_vhci_chr_poll(struct file *file, poll_table * wait)
118{
119 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
120
121 poll_wait(file, &hci_vhci->read_wait, wait);
122
123 if (skb_queue_len(&hci_vhci->readq))
124 return POLLIN | POLLRDNORM;
125
126 return POLLOUT | POLLWRNORM;
127}
128
129/* Get packet from user space buffer(already verified) */
130static inline ssize_t hci_vhci_get_user(struct hci_vhci_struct *hci_vhci, const char __user *buf, size_t count)
131{
132 struct sk_buff *skb;
133
134 if (count > HCI_MAX_FRAME_SIZE)
135 return -EINVAL;
136
137 if (!(skb = bt_skb_alloc(count, GFP_KERNEL)))
138 return -ENOMEM;
139
140 if (copy_from_user(skb_put(skb, count), buf, count)) {
141 kfree_skb(skb);
142 return -EFAULT;
143 }
144
145 skb->dev = (void *) hci_vhci->hdev;
146 skb->pkt_type = *((__u8 *) skb->data);
147 skb_pull(skb, 1);
148
149 hci_recv_frame(skb);
150
151 return count;
152}
153
154/* Write */
155static ssize_t hci_vhci_chr_write(struct file * file, const char __user * buf,
156 size_t count, loff_t *pos)
157{
158 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
159
160 if (!access_ok(VERIFY_READ, buf, count))
161 return -EFAULT;
162
163 return hci_vhci_get_user(hci_vhci, buf, count);
164}
165
166/* Put packet to user space buffer(already verified) */
167static inline ssize_t hci_vhci_put_user(struct hci_vhci_struct *hci_vhci,
168 struct sk_buff *skb, char __user *buf,
169 int count)
170{
171 int len = count, total = 0;
172 char __user *ptr = buf;
173
174 len = min_t(unsigned int, skb->len, len);
175 if (copy_to_user(ptr, skb->data, len))
176 return -EFAULT;
177 total += len;
178
179 hci_vhci->hdev->stat.byte_tx += len;
180 switch (skb->pkt_type) {
181 case HCI_COMMAND_PKT:
182 hci_vhci->hdev->stat.cmd_tx++;
183 break;
184
185 case HCI_ACLDATA_PKT:
186 hci_vhci->hdev->stat.acl_tx++;
187 break;
188
189 case HCI_SCODATA_PKT:
190 hci_vhci->hdev->stat.cmd_tx++;
191 break;
192 };
193
194 return total;
195}
196
197/* Read */
198static ssize_t hci_vhci_chr_read(struct file * file, char __user * buf, size_t count, loff_t *pos)
199{
200 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
201 DECLARE_WAITQUEUE(wait, current);
202 struct sk_buff *skb;
203 ssize_t ret = 0;
204
205 add_wait_queue(&hci_vhci->read_wait, &wait);
206 while (count) {
207 set_current_state(TASK_INTERRUPTIBLE);
208
209 /* Read frames from device queue */
210 if (!(skb = skb_dequeue(&hci_vhci->readq))) {
211 if (file->f_flags & O_NONBLOCK) {
212 ret = -EAGAIN;
213 break;
214 }
215 if (signal_pending(current)) {
216 ret = -ERESTARTSYS;
217 break;
218 }
219
220 /* Nothing to read, let's sleep */
221 schedule();
222 continue;
223 }
224
225 if (access_ok(VERIFY_WRITE, buf, count))
226 ret = hci_vhci_put_user(hci_vhci, skb, buf, count);
227 else
228 ret = -EFAULT;
229
230 kfree_skb(skb);
231 break;
232 }
233 set_current_state(TASK_RUNNING);
234 remove_wait_queue(&hci_vhci->read_wait, &wait);
235
236 return ret;
237}
238
239static loff_t hci_vhci_chr_lseek(struct file * file, loff_t offset, int origin)
240{
241 return -ESPIPE;
242}
243
244static int hci_vhci_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
245{
246 return -EINVAL;
247}
248
249static int hci_vhci_chr_fasync(int fd, struct file *file, int on)
250{
251 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
252 int ret;
253
254 if ((ret = fasync_helper(fd, file, on, &hci_vhci->fasync)) < 0)
255 return ret;
256
257 if (on)
258 hci_vhci->flags |= VHCI_FASYNC;
259 else
260 hci_vhci->flags &= ~VHCI_FASYNC;
261
262 return 0;
263}
264
265static int hci_vhci_chr_open(struct inode *inode, struct file * file)
266{
267 struct hci_vhci_struct *hci_vhci = NULL;
268 struct hci_dev *hdev;
269
270 if (!(hci_vhci = kmalloc(sizeof(struct hci_vhci_struct), GFP_KERNEL)))
271 return -ENOMEM;
272
273 memset(hci_vhci, 0, sizeof(struct hci_vhci_struct));
274
275 skb_queue_head_init(&hci_vhci->readq);
276 init_waitqueue_head(&hci_vhci->read_wait);
277
278 /* Initialize and register HCI device */
279 hdev = hci_alloc_dev();
280 if (!hdev) {
281 kfree(hci_vhci);
282 return -ENOMEM;
283 }
284
285 hci_vhci->hdev = hdev;
286
287 hdev->type = HCI_VHCI;
288 hdev->driver_data = hci_vhci;
289
290 hdev->open = hci_vhci_open;
291 hdev->close = hci_vhci_close;
292 hdev->flush = hci_vhci_flush;
293 hdev->send = hci_vhci_send_frame;
294 hdev->destruct = hci_vhci_destruct;
295
296 hdev->owner = THIS_MODULE;
297
298 if (hci_register_dev(hdev) < 0) {
299 kfree(hci_vhci);
300 hci_free_dev(hdev);
301 return -EBUSY;
302 }
303
304 file->private_data = hci_vhci;
305 return nonseekable_open(inode, file);
306}
307
308static int hci_vhci_chr_close(struct inode *inode, struct file *file)
309{
310 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
311 struct hci_dev *hdev = hci_vhci->hdev;
312
313 if (hci_unregister_dev(hdev) < 0) {
314 BT_ERR("Can't unregister HCI device %s", hdev->name);
315 }
316
317 hci_free_dev(hdev);
318
319 file->private_data = NULL;
320 return 0;
321}
322
323static struct file_operations hci_vhci_fops = {
324 .owner = THIS_MODULE,
325 .llseek = hci_vhci_chr_lseek,
326 .read = hci_vhci_chr_read,
327 .write = hci_vhci_chr_write,
328 .poll = hci_vhci_chr_poll,
329 .ioctl = hci_vhci_chr_ioctl,
330 .open = hci_vhci_chr_open,
331 .release = hci_vhci_chr_close,
332 .fasync = hci_vhci_chr_fasync
333};
334
335static struct miscdevice hci_vhci_miscdev=
336{
337 VHCI_MINOR,
338 "hci_vhci",
339 &hci_vhci_fops
340};
341
342static int __init hci_vhci_init(void)
343{
344 BT_INFO("VHCI driver ver %s", VERSION);
345
346 if (misc_register(&hci_vhci_miscdev)) {
347 BT_ERR("Can't register misc device %d\n", VHCI_MINOR);
348 return -EIO;
349 }
350
351 return 0;
352}
353
354static void hci_vhci_cleanup(void)
355{
356 misc_deregister(&hci_vhci_miscdev);
357}
358
359module_init(hci_vhci_init);
360module_exit(hci_vhci_cleanup);
361
362MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
363MODULE_DESCRIPTION("Bluetooth VHCI driver ver " VERSION);
364MODULE_LICENSE("GPL");