aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth/hci_vhci.c
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2005-08-09 23:27:37 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2005-08-29 18:54:42 -0400
commit4aa769b99724953a6f322c648c0cfbe8c6616382 (patch)
tree464078550df33ac4f9c8eaf286e95a4f5f46a890 /drivers/bluetooth/hci_vhci.c
parentf6ccf55419c4f0021e7382f000f2fd14a29f3d3c (diff)
[Bluetooth]: Update and cleanup of the virtual HCI driver
This patch cleans up the virtual HCI driver. It also adds support for the dynamic minor device number allocation. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/bluetooth/hci_vhci.c')
-rw-r--r--drivers/bluetooth/hci_vhci.c368
1 files changed, 194 insertions, 174 deletions
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index f9b956fb2b8b..4aa5dfff12be 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -1,229 +1,220 @@
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/* 1/*
26 * Bluetooth HCI virtual device driver.
27 * 2 *
28 * $Id: hci_vhci.c,v 1.3 2002/04/17 17:37:20 maxk Exp $ 3 * Bluetooth virtual HCI driver
4 *
5 * Copyright (C) 2000-2001 Qualcomm Incorporated
6 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
7 * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
8 *
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
29 */ 24 */
30#define VERSION "1.1"
31 25
32#include <linux/config.h> 26#include <linux/config.h>
33#include <linux/module.h> 27#include <linux/module.h>
34 28
35#include <linux/errno.h>
36#include <linux/kernel.h> 29#include <linux/kernel.h>
37#include <linux/major.h> 30#include <linux/init.h>
38#include <linux/sched.h>
39#include <linux/slab.h> 31#include <linux/slab.h>
32#include <linux/types.h>
33#include <linux/errno.h>
34#include <linux/sched.h>
40#include <linux/poll.h> 35#include <linux/poll.h>
41#include <linux/fcntl.h>
42#include <linux/init.h>
43#include <linux/random.h>
44 36
45#include <linux/skbuff.h> 37#include <linux/skbuff.h>
46#include <linux/miscdevice.h> 38#include <linux/miscdevice.h>
47 39
48#include <asm/system.h>
49#include <asm/uaccess.h>
50
51#include <net/bluetooth/bluetooth.h> 40#include <net/bluetooth/bluetooth.h>
52#include <net/bluetooth/hci_core.h> 41#include <net/bluetooth/hci_core.h>
53#include "hci_vhci.h"
54 42
55/* HCI device part */ 43#ifndef CONFIG_BT_HCIVHCI_DEBUG
44#undef BT_DBG
45#define BT_DBG(D...)
46#endif
47
48#define VERSION "1.2"
49
50static int minor = MISC_DYNAMIC_MINOR;
56 51
57static int hci_vhci_open(struct hci_dev *hdev) 52struct vhci_data {
53 struct hci_dev *hdev;
54
55 unsigned long flags;
56
57 wait_queue_head_t read_wait;
58 struct sk_buff_head readq;
59
60 struct fasync_struct *fasync;
61};
62
63#define VHCI_FASYNC 0x0010
64
65static struct miscdevice vhci_miscdev;
66
67static int vhci_open_dev(struct hci_dev *hdev)
58{ 68{
59 set_bit(HCI_RUNNING, &hdev->flags); 69 set_bit(HCI_RUNNING, &hdev->flags);
60 return 0;
61}
62 70
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; 71 return 0;
68} 72}
69 73
70static int hci_vhci_close(struct hci_dev *hdev) 74static int vhci_close_dev(struct hci_dev *hdev)
71{ 75{
76 struct vhci_data *vhci = hdev->driver_data;
77
72 if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) 78 if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
73 return 0; 79 return 0;
74 80
75 hci_vhci_flush(hdev); 81 skb_queue_purge(&vhci->readq);
82
76 return 0; 83 return 0;
77} 84}
78 85
79static void hci_vhci_destruct(struct hci_dev *hdev) 86static int vhci_flush(struct hci_dev *hdev)
80{ 87{
81 struct hci_vhci_struct *vhci; 88 struct vhci_data *vhci = hdev->driver_data;
82 89
83 if (!hdev) return; 90 skb_queue_purge(&vhci->readq);
84 91
85 vhci = (struct hci_vhci_struct *) hdev->driver_data; 92 return 0;
86 kfree(vhci);
87} 93}
88 94
89static int hci_vhci_send_frame(struct sk_buff *skb) 95static int vhci_send_frame(struct sk_buff *skb)
90{ 96{
91 struct hci_dev* hdev = (struct hci_dev *) skb->dev; 97 struct hci_dev* hdev = (struct hci_dev *) skb->dev;
92 struct hci_vhci_struct *hci_vhci; 98 struct vhci_data *vhci;
93 99
94 if (!hdev) { 100 if (!hdev) {
95 BT_ERR("Frame for uknown device (hdev=NULL)"); 101 BT_ERR("Frame for unknown HCI device (hdev=NULL)");
96 return -ENODEV; 102 return -ENODEV;
97 } 103 }
98 104
99 if (!test_bit(HCI_RUNNING, &hdev->flags)) 105 if (!test_bit(HCI_RUNNING, &hdev->flags))
100 return -EBUSY; 106 return -EBUSY;
101 107
102 hci_vhci = (struct hci_vhci_struct *) hdev->driver_data; 108 vhci = hdev->driver_data;
103 109
104 memcpy(skb_push(skb, 1), &skb->pkt_type, 1); 110 memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
105 skb_queue_tail(&hci_vhci->readq, skb); 111 skb_queue_tail(&vhci->readq, skb);
112
113 if (vhci->flags & VHCI_FASYNC)
114 kill_fasync(&vhci->fasync, SIGIO, POLL_IN);
106 115
107 if (hci_vhci->flags & VHCI_FASYNC) 116 wake_up_interruptible(&vhci->read_wait);
108 kill_fasync(&hci_vhci->fasync, SIGIO, POLL_IN);
109 wake_up_interruptible(&hci_vhci->read_wait);
110 117
111 return 0; 118 return 0;
112} 119}
113 120
114/* Character device part */ 121static void vhci_destruct(struct hci_dev *hdev)
115 122{
116/* Poll */ 123 kfree(hdev->driver_data);
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_empty(&hci_vhci->readq))
124 return POLLIN | POLLRDNORM;
125
126 return POLLOUT | POLLWRNORM;
127} 124}
128 125
129/* Get packet from user space buffer(already verified) */ 126static inline ssize_t vhci_get_user(struct vhci_data *vhci,
130static inline ssize_t hci_vhci_get_user(struct hci_vhci_struct *hci_vhci, const char __user *buf, size_t count) 127 const char __user *buf, size_t count)
131{ 128{
132 struct sk_buff *skb; 129 struct sk_buff *skb;
133 130
134 if (count > HCI_MAX_FRAME_SIZE) 131 if (count > HCI_MAX_FRAME_SIZE)
135 return -EINVAL; 132 return -EINVAL;
136 133
137 if (!(skb = bt_skb_alloc(count, GFP_KERNEL))) 134 skb = bt_skb_alloc(count, GFP_KERNEL);
135 if (!skb)
138 return -ENOMEM; 136 return -ENOMEM;
139 137
140 if (copy_from_user(skb_put(skb, count), buf, count)) { 138 if (copy_from_user(skb_put(skb, count), buf, count)) {
141 kfree_skb(skb); 139 kfree_skb(skb);
142 return -EFAULT; 140 return -EFAULT;
143 } 141 }
144 142
145 skb->dev = (void *) hci_vhci->hdev; 143 skb->dev = (void *) vhci->hdev;
146 skb->pkt_type = *((__u8 *) skb->data); 144 skb->pkt_type = *((__u8 *) skb->data);
147 skb_pull(skb, 1); 145 skb_pull(skb, 1);
148 146
149 hci_recv_frame(skb); 147 hci_recv_frame(skb);
150 148
151 return count; 149 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} 150}
165 151
166/* Put packet to user space buffer(already verified) */ 152static inline ssize_t vhci_put_user(struct vhci_data *vhci,
167static inline ssize_t hci_vhci_put_user(struct hci_vhci_struct *hci_vhci, 153 struct sk_buff *skb, char __user *buf, int count)
168 struct sk_buff *skb, char __user *buf,
169 int count)
170{ 154{
171 int len = count, total = 0;
172 char __user *ptr = buf; 155 char __user *ptr = buf;
156 int len, total = 0;
157
158 len = min_t(unsigned int, skb->len, count);
173 159
174 len = min_t(unsigned int, skb->len, len);
175 if (copy_to_user(ptr, skb->data, len)) 160 if (copy_to_user(ptr, skb->data, len))
176 return -EFAULT; 161 return -EFAULT;
162
177 total += len; 163 total += len;
178 164
179 hci_vhci->hdev->stat.byte_tx += len; 165 vhci->hdev->stat.byte_tx += len;
166
180 switch (skb->pkt_type) { 167 switch (skb->pkt_type) {
181 case HCI_COMMAND_PKT: 168 case HCI_COMMAND_PKT:
182 hci_vhci->hdev->stat.cmd_tx++; 169 vhci->hdev->stat.cmd_tx++;
183 break; 170 break;
184 171
185 case HCI_ACLDATA_PKT: 172 case HCI_ACLDATA_PKT:
186 hci_vhci->hdev->stat.acl_tx++; 173 vhci->hdev->stat.acl_tx++;
187 break; 174 break;
188 175
189 case HCI_SCODATA_PKT: 176 case HCI_SCODATA_PKT:
190 hci_vhci->hdev->stat.cmd_tx++; 177 vhci->hdev->stat.cmd_tx++;
191 break; 178 break;
192 }; 179 };
193 180
194 return total; 181 return total;
195} 182}
196 183
197/* Read */ 184static loff_t vhci_llseek(struct file * file, loff_t offset, int origin)
198static ssize_t hci_vhci_chr_read(struct file * file, char __user * buf, size_t count, loff_t *pos) 185{
186 return -ESPIPE;
187}
188
189static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, loff_t *pos)
199{ 190{
200 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
201 DECLARE_WAITQUEUE(wait, current); 191 DECLARE_WAITQUEUE(wait, current);
192 struct vhci_data *vhci = file->private_data;
202 struct sk_buff *skb; 193 struct sk_buff *skb;
203 ssize_t ret = 0; 194 ssize_t ret = 0;
204 195
205 add_wait_queue(&hci_vhci->read_wait, &wait); 196 add_wait_queue(&vhci->read_wait, &wait);
206 while (count) { 197 while (count) {
207 set_current_state(TASK_INTERRUPTIBLE); 198 set_current_state(TASK_INTERRUPTIBLE);
208 199
209 /* Read frames from device queue */ 200 skb = skb_dequeue(&vhci->readq);
210 if (!(skb = skb_dequeue(&hci_vhci->readq))) { 201 if (!skb) {
211 if (file->f_flags & O_NONBLOCK) { 202 if (file->f_flags & O_NONBLOCK) {
212 ret = -EAGAIN; 203 ret = -EAGAIN;
213 break; 204 break;
214 } 205 }
206
215 if (signal_pending(current)) { 207 if (signal_pending(current)) {
216 ret = -ERESTARTSYS; 208 ret = -ERESTARTSYS;
217 break; 209 break;
218 } 210 }
219 211
220 /* Nothing to read, let's sleep */
221 schedule(); 212 schedule();
222 continue; 213 continue;
223 } 214 }
224 215
225 if (access_ok(VERIFY_WRITE, buf, count)) 216 if (access_ok(VERIFY_WRITE, buf, count))
226 ret = hci_vhci_put_user(hci_vhci, skb, buf, count); 217 ret = vhci_put_user(vhci, skb, buf, count);
227 else 218 else
228 ret = -EFAULT; 219 ret = -EFAULT;
229 220
@@ -231,84 +222,90 @@ static ssize_t hci_vhci_chr_read(struct file * file, char __user * buf, size_t c
231 break; 222 break;
232 } 223 }
233 set_current_state(TASK_RUNNING); 224 set_current_state(TASK_RUNNING);
234 remove_wait_queue(&hci_vhci->read_wait, &wait); 225 remove_wait_queue(&vhci->read_wait, &wait);
235 226
236 return ret; 227 return ret;
237} 228}
238 229
239static loff_t hci_vhci_chr_lseek(struct file * file, loff_t offset, int origin) 230static ssize_t vhci_write(struct file *file,
231 const char __user *buf, size_t count, loff_t *pos)
240{ 232{
241 return -ESPIPE; 233 struct vhci_data *vhci = file->private_data;
242}
243 234
244static int hci_vhci_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 235 if (!access_ok(VERIFY_READ, buf, count))
245{ 236 return -EFAULT;
246 return -EINVAL; 237
238 return vhci_get_user(vhci, buf, count);
247} 239}
248 240
249static int hci_vhci_chr_fasync(int fd, struct file *file, int on) 241static unsigned int vhci_poll(struct file *file, poll_table *wait)
250{ 242{
251 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data; 243 struct vhci_data *vhci = file->private_data;
252 int ret;
253 244
254 if ((ret = fasync_helper(fd, file, on, &hci_vhci->fasync)) < 0) 245 poll_wait(file, &vhci->read_wait, wait);
255 return ret;
256
257 if (on)
258 hci_vhci->flags |= VHCI_FASYNC;
259 else
260 hci_vhci->flags &= ~VHCI_FASYNC;
261 246
262 return 0; 247 if (!skb_queue_empty(&vhci->readq))
248 return POLLIN | POLLRDNORM;
249
250 return POLLOUT | POLLWRNORM;
251}
252
253static int vhci_ioctl(struct inode *inode, struct file *file,
254 unsigned int cmd, unsigned long arg)
255{
256 return -EINVAL;
263} 257}
264 258
265static int hci_vhci_chr_open(struct inode *inode, struct file * file) 259static int vhci_open(struct inode *inode, struct file *file)
266{ 260{
267 struct hci_vhci_struct *hci_vhci = NULL; 261 struct vhci_data *vhci;
268 struct hci_dev *hdev; 262 struct hci_dev *hdev;
269 263
270 if (!(hci_vhci = kmalloc(sizeof(struct hci_vhci_struct), GFP_KERNEL))) 264 vhci = kmalloc(sizeof(struct vhci_data), GFP_KERNEL);
265 if (!vhci)
271 return -ENOMEM; 266 return -ENOMEM;
272 267
273 memset(hci_vhci, 0, sizeof(struct hci_vhci_struct)); 268 memset(vhci, 0, sizeof(struct vhci_data));
274 269
275 skb_queue_head_init(&hci_vhci->readq); 270 skb_queue_head_init(&vhci->readq);
276 init_waitqueue_head(&hci_vhci->read_wait); 271 init_waitqueue_head(&vhci->read_wait);
277 272
278 /* Initialize and register HCI device */
279 hdev = hci_alloc_dev(); 273 hdev = hci_alloc_dev();
280 if (!hdev) { 274 if (!hdev) {
281 kfree(hci_vhci); 275 kfree(vhci);
282 return -ENOMEM; 276 return -ENOMEM;
283 } 277 }
284 278
285 hci_vhci->hdev = hdev; 279 vhci->hdev = hdev;
286 280
287 hdev->type = HCI_VHCI; 281 hdev->type = HCI_VHCI;
288 hdev->driver_data = hci_vhci; 282 hdev->driver_data = vhci;
283 SET_HCIDEV_DEV(hdev, vhci_miscdev.dev);
289 284
290 hdev->open = hci_vhci_open; 285 hdev->open = vhci_open_dev;
291 hdev->close = hci_vhci_close; 286 hdev->close = vhci_close_dev;
292 hdev->flush = hci_vhci_flush; 287 hdev->flush = vhci_flush;
293 hdev->send = hci_vhci_send_frame; 288 hdev->send = vhci_send_frame;
294 hdev->destruct = hci_vhci_destruct; 289 hdev->destruct = vhci_destruct;
295 290
296 hdev->owner = THIS_MODULE; 291 hdev->owner = THIS_MODULE;
297 292
298 if (hci_register_dev(hdev) < 0) { 293 if (hci_register_dev(hdev) < 0) {
299 kfree(hci_vhci); 294 BT_ERR("Can't register HCI device");
295 kfree(vhci);
300 hci_free_dev(hdev); 296 hci_free_dev(hdev);
301 return -EBUSY; 297 return -EBUSY;
302 } 298 }
303 299
304 file->private_data = hci_vhci; 300 file->private_data = vhci;
305 return nonseekable_open(inode, file); 301
302 return nonseekable_open(inode, file);
306} 303}
307 304
308static int hci_vhci_chr_close(struct inode *inode, struct file *file) 305static int vhci_release(struct inode *inode, struct file *file)
309{ 306{
310 struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data; 307 struct vhci_data *vhci = file->private_data;
311 struct hci_dev *hdev = hci_vhci->hdev; 308 struct hci_dev *hdev = vhci->hdev;
312 309
313 if (hci_unregister_dev(hdev) < 0) { 310 if (hci_unregister_dev(hdev) < 0) {
314 BT_ERR("Can't unregister HCI device %s", hdev->name); 311 BT_ERR("Can't unregister HCI device %s", hdev->name);
@@ -317,48 +314,71 @@ static int hci_vhci_chr_close(struct inode *inode, struct file *file)
317 hci_free_dev(hdev); 314 hci_free_dev(hdev);
318 315
319 file->private_data = NULL; 316 file->private_data = NULL;
317
318 return 0;
319}
320
321static int vhci_fasync(int fd, struct file *file, int on)
322{
323 struct vhci_data *vhci = file->private_data;
324 int err;
325
326 err = fasync_helper(fd, file, on, &vhci->fasync);
327 if (err < 0)
328 return err;
329
330 if (on)
331 vhci->flags |= VHCI_FASYNC;
332 else
333 vhci->flags &= ~VHCI_FASYNC;
334
320 return 0; 335 return 0;
321} 336}
322 337
323static struct file_operations hci_vhci_fops = { 338static struct file_operations vhci_fops = {
324 .owner = THIS_MODULE, 339 .owner = THIS_MODULE,
325 .llseek = hci_vhci_chr_lseek, 340 .llseek = vhci_llseek,
326 .read = hci_vhci_chr_read, 341 .read = vhci_read,
327 .write = hci_vhci_chr_write, 342 .write = vhci_write,
328 .poll = hci_vhci_chr_poll, 343 .poll = vhci_poll,
329 .ioctl = hci_vhci_chr_ioctl, 344 .ioctl = vhci_ioctl,
330 .open = hci_vhci_chr_open, 345 .open = vhci_open,
331 .release = hci_vhci_chr_close, 346 .release = vhci_release,
332 .fasync = hci_vhci_chr_fasync 347 .fasync = vhci_fasync,
333}; 348};
334 349
335static struct miscdevice hci_vhci_miscdev= 350static struct miscdevice vhci_miscdev= {
336{ 351 .name = "vhci",
337 VHCI_MINOR, 352 .fops = &vhci_fops,
338 "hci_vhci",
339 &hci_vhci_fops
340}; 353};
341 354
342static int __init hci_vhci_init(void) 355static int __init vhci_init(void)
343{ 356{
344 BT_INFO("VHCI driver ver %s", VERSION); 357 BT_INFO("Virtual HCI driver ver %s", VERSION);
345 358
346 if (misc_register(&hci_vhci_miscdev)) { 359 vhci_miscdev.minor = minor;
347 BT_ERR("Can't register misc device %d\n", VHCI_MINOR); 360
361 if (misc_register(&vhci_miscdev) < 0) {
362 BT_ERR("Can't register misc device with minor %d", minor);
348 return -EIO; 363 return -EIO;
349 } 364 }
350 365
351 return 0; 366 return 0;
352} 367}
353 368
354static void hci_vhci_cleanup(void) 369static void __exit vhci_exit(void)
355{ 370{
356 misc_deregister(&hci_vhci_miscdev); 371 if (misc_deregister(&vhci_miscdev) < 0)
372 BT_ERR("Can't unregister misc device with minor %d", minor);
357} 373}
358 374
359module_init(hci_vhci_init); 375module_init(vhci_init);
360module_exit(hci_vhci_cleanup); 376module_exit(vhci_exit);
377
378module_param(minor, int, 0444);
379MODULE_PARM_DESC(minor, "Miscellaneous minor device number");
361 380
362MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); 381MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
363MODULE_DESCRIPTION("Bluetooth VHCI driver ver " VERSION); 382MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
364MODULE_LICENSE("GPL"); 383MODULE_VERSION(VERSION);
384MODULE_LICENSE("GPL");