Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * | |
3 | * Digianswer Bluetooth USB driver | |
4 | * | |
e24b21ec | 5 | * Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org> |
1da177e4 LT |
6 | * |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | * | |
22 | */ | |
23 | ||
1da177e4 | 24 | #include <linux/kernel.h> |
e24b21ec | 25 | #include <linux/module.h> |
1da177e4 LT |
26 | #include <linux/init.h> |
27 | #include <linux/slab.h> | |
28 | #include <linux/types.h> | |
e24b21ec | 29 | #include <linux/sched.h> |
1da177e4 | 30 | #include <linux/errno.h> |
e24b21ec | 31 | #include <linux/skbuff.h> |
1da177e4 LT |
32 | |
33 | #include <linux/usb.h> | |
34 | ||
35 | #include <net/bluetooth/bluetooth.h> | |
36 | #include <net/bluetooth/hci_core.h> | |
37 | ||
38 | #ifndef CONFIG_BT_HCIBPA10X_DEBUG | |
39 | #undef BT_DBG | |
40 | #define BT_DBG(D...) | |
41 | #endif | |
42 | ||
943d56b0 | 43 | #define VERSION "0.10" |
1da177e4 LT |
44 | |
45 | static struct usb_device_id bpa10x_table[] = { | |
46 | /* Tektronix BPA 100/105 (Digianswer) */ | |
47 | { USB_DEVICE(0x08fd, 0x0002) }, | |
48 | ||
49 | { } /* Terminating entry */ | |
50 | }; | |
51 | ||
52 | MODULE_DEVICE_TABLE(usb, bpa10x_table); | |
53 | ||
1da177e4 | 54 | struct bpa10x_data { |
e24b21ec MH |
55 | struct hci_dev *hdev; |
56 | struct usb_device *udev; | |
1da177e4 | 57 | |
e24b21ec MH |
58 | struct usb_anchor tx_anchor; |
59 | struct usb_anchor rx_anchor; | |
1da177e4 | 60 | |
e24b21ec | 61 | struct sk_buff *rx_skb[2]; |
1da177e4 LT |
62 | }; |
63 | ||
e24b21ec | 64 | #define HCI_VENDOR_HDR_SIZE 5 |
1da177e4 LT |
65 | |
66 | struct hci_vendor_hdr { | |
e24b21ec MH |
67 | __u8 type; |
68 | __le16 snum; | |
69 | __le16 dlen; | |
1da177e4 LT |
70 | } __attribute__ ((packed)); |
71 | ||
e24b21ec | 72 | static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count) |
1da177e4 | 73 | { |
e24b21ec MH |
74 | struct bpa10x_data *data = hdev->driver_data; |
75 | ||
76 | BT_DBG("%s queue %d buffer %p count %d", hdev->name, | |
77 | queue, buf, count); | |
78 | ||
79 | if (queue < 0 || queue > 1) | |
80 | return -EILSEQ; | |
81 | ||
82 | hdev->stat.byte_rx += count; | |
1da177e4 LT |
83 | |
84 | while (count) { | |
e24b21ec MH |
85 | struct sk_buff *skb = data->rx_skb[queue]; |
86 | struct { __u8 type; int expect; } *scb; | |
87 | int type, len = 0; | |
1da177e4 | 88 | |
e24b21ec MH |
89 | if (!skb) { |
90 | /* Start of the frame */ | |
91 | ||
92 | type = *((__u8 *) buf); | |
93 | count--; buf++; | |
94 | ||
95 | switch (type) { | |
96 | case HCI_EVENT_PKT: | |
97 | if (count >= HCI_EVENT_HDR_SIZE) { | |
98 | struct hci_event_hdr *h = buf; | |
99 | len = HCI_EVENT_HDR_SIZE + h->plen; | |
100 | } else | |
101 | return -EILSEQ; | |
102 | break; | |
103 | ||
104 | case HCI_ACLDATA_PKT: | |
105 | if (count >= HCI_ACL_HDR_SIZE) { | |
106 | struct hci_acl_hdr *h = buf; | |
107 | len = HCI_ACL_HDR_SIZE + | |
108 | __le16_to_cpu(h->dlen); | |
109 | } else | |
110 | return -EILSEQ; | |
111 | break; | |
112 | ||
113 | case HCI_SCODATA_PKT: | |
114 | if (count >= HCI_SCO_HDR_SIZE) { | |
115 | struct hci_sco_hdr *h = buf; | |
116 | len = HCI_SCO_HDR_SIZE + h->dlen; | |
117 | } else | |
118 | return -EILSEQ; | |
119 | break; | |
120 | ||
121 | case HCI_VENDOR_PKT: | |
122 | if (count >= HCI_VENDOR_HDR_SIZE) { | |
123 | struct hci_vendor_hdr *h = buf; | |
124 | len = HCI_VENDOR_HDR_SIZE + | |
125 | __le16_to_cpu(h->dlen); | |
126 | } else | |
127 | return -EILSEQ; | |
128 | break; | |
1da177e4 | 129 | } |
1da177e4 | 130 | |
1da177e4 | 131 | skb = bt_skb_alloc(len, GFP_ATOMIC); |
e24b21ec MH |
132 | if (!skb) { |
133 | BT_ERR("%s no memory for packet", hdev->name); | |
134 | return -ENOMEM; | |
1da177e4 | 135 | } |
1da177e4 | 136 | |
e24b21ec | 137 | skb->dev = (void *) hdev; |
1da177e4 | 138 | |
e24b21ec | 139 | data->rx_skb[queue] = skb; |
1da177e4 | 140 | |
e24b21ec MH |
141 | scb = (void *) skb->cb; |
142 | scb->type = type; | |
143 | scb->expect = len; | |
144 | } else { | |
145 | /* Continuation */ | |
1da177e4 | 146 | |
e24b21ec MH |
147 | scb = (void *) skb->cb; |
148 | len = scb->expect; | |
1da177e4 LT |
149 | } |
150 | ||
e24b21ec | 151 | len = min(len, count); |
1da177e4 | 152 | |
e24b21ec | 153 | memcpy(skb_put(skb, len), buf, len); |
1da177e4 | 154 | |
e24b21ec | 155 | scb->expect -= len; |
1da177e4 | 156 | |
e24b21ec MH |
157 | if (scb->expect == 0) { |
158 | /* Complete frame */ | |
1da177e4 | 159 | |
e24b21ec | 160 | data->rx_skb[queue] = NULL; |
1da177e4 | 161 | |
e24b21ec | 162 | bt_cb(skb)->pkt_type = scb->type; |
1da177e4 | 163 | hci_recv_frame(skb); |
1da177e4 | 164 | } |
e24b21ec MH |
165 | |
166 | count -= len; buf += len; | |
1da177e4 LT |
167 | } |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
e24b21ec | 172 | static void bpa10x_tx_complete(struct urb *urb) |
1da177e4 | 173 | { |
e24b21ec MH |
174 | struct sk_buff *skb = urb->context; |
175 | struct hci_dev *hdev = (struct hci_dev *) skb->dev; | |
1da177e4 | 176 | |
e24b21ec MH |
177 | BT_DBG("%s urb %p status %d count %d", hdev->name, |
178 | urb, urb->status, urb->actual_length); | |
1da177e4 | 179 | |
e24b21ec MH |
180 | if (!test_bit(HCI_RUNNING, &hdev->flags)) |
181 | goto done; | |
182 | ||
183 | if (!urb->status) | |
184 | hdev->stat.byte_tx += urb->transfer_buffer_length; | |
1da177e4 | 185 | else |
e24b21ec | 186 | hdev->stat.err_tx++; |
1da177e4 | 187 | |
e24b21ec MH |
188 | done: |
189 | kfree(urb->setup_packet); | |
1da177e4 | 190 | |
e24b21ec MH |
191 | kfree_skb(skb); |
192 | } | |
193 | ||
194 | static void bpa10x_rx_complete(struct urb *urb) | |
195 | { | |
196 | struct hci_dev *hdev = urb->context; | |
197 | struct bpa10x_data *data = hdev->driver_data; | |
198 | int err; | |
1da177e4 | 199 | |
e24b21ec MH |
200 | BT_DBG("%s urb %p status %d count %d", hdev->name, |
201 | urb, urb->status, urb->actual_length); | |
1da177e4 | 202 | |
e24b21ec MH |
203 | if (!test_bit(HCI_RUNNING, &hdev->flags)) |
204 | return; | |
1da177e4 | 205 | |
e24b21ec MH |
206 | if (urb->status == 0) { |
207 | if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe), | |
208 | urb->transfer_buffer, | |
209 | urb->actual_length) < 0) { | |
210 | BT_ERR("%s corrupted event packet", hdev->name); | |
211 | hdev->stat.err_rx++; | |
212 | } | |
1da177e4 LT |
213 | } |
214 | ||
e24b21ec MH |
215 | usb_anchor_urb(urb, &data->rx_anchor); |
216 | ||
217 | err = usb_submit_urb(urb, GFP_ATOMIC); | |
218 | if (err < 0) { | |
219 | BT_ERR("%s urb %p failed to resubmit (%d)", | |
220 | hdev->name, urb, -err); | |
221 | usb_unanchor_urb(urb); | |
1da177e4 LT |
222 | } |
223 | } | |
224 | ||
e24b21ec | 225 | static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev) |
1da177e4 | 226 | { |
e24b21ec MH |
227 | struct bpa10x_data *data = hdev->driver_data; |
228 | struct urb *urb; | |
229 | unsigned char *buf; | |
230 | unsigned int pipe; | |
231 | int err, size = 16; | |
1da177e4 | 232 | |
e24b21ec | 233 | BT_DBG("%s", hdev->name); |
1da177e4 | 234 | |
e24b21ec MH |
235 | urb = usb_alloc_urb(0, GFP_KERNEL); |
236 | if (!urb) | |
237 | return -ENOMEM; | |
1da177e4 | 238 | |
e24b21ec MH |
239 | buf = kmalloc(size, GFP_KERNEL); |
240 | if (!buf) { | |
241 | usb_free_urb(urb); | |
242 | return -ENOMEM; | |
243 | } | |
1da177e4 | 244 | |
e24b21ec | 245 | pipe = usb_rcvintpipe(data->udev, 0x81); |
1da177e4 | 246 | |
e24b21ec MH |
247 | usb_fill_int_urb(urb, data->udev, pipe, buf, size, |
248 | bpa10x_rx_complete, hdev, 1); | |
1da177e4 | 249 | |
e24b21ec | 250 | urb->transfer_flags |= URB_FREE_BUFFER; |
1da177e4 | 251 | |
e24b21ec | 252 | usb_anchor_urb(urb, &data->rx_anchor); |
1da177e4 | 253 | |
e24b21ec MH |
254 | err = usb_submit_urb(urb, GFP_KERNEL); |
255 | if (err < 0) { | |
256 | BT_ERR("%s urb %p submission failed (%d)", | |
257 | hdev->name, urb, -err); | |
258 | usb_unanchor_urb(urb); | |
1da177e4 LT |
259 | } |
260 | ||
e24b21ec | 261 | usb_free_urb(urb); |
1da177e4 | 262 | |
e24b21ec | 263 | return err; |
1da177e4 LT |
264 | } |
265 | ||
e24b21ec | 266 | static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev) |
1da177e4 | 267 | { |
e24b21ec | 268 | struct bpa10x_data *data = hdev->driver_data; |
1da177e4 | 269 | struct urb *urb; |
1da177e4 | 270 | unsigned char *buf; |
e24b21ec MH |
271 | unsigned int pipe; |
272 | int err, size = 64; | |
1da177e4 | 273 | |
e24b21ec | 274 | BT_DBG("%s", hdev->name); |
1da177e4 | 275 | |
e24b21ec | 276 | urb = usb_alloc_urb(0, GFP_KERNEL); |
1da177e4 | 277 | if (!urb) |
e24b21ec | 278 | return -ENOMEM; |
1da177e4 | 279 | |
e24b21ec | 280 | buf = kmalloc(size, GFP_KERNEL); |
1da177e4 LT |
281 | if (!buf) { |
282 | usb_free_urb(urb); | |
e24b21ec | 283 | return -ENOMEM; |
1da177e4 LT |
284 | } |
285 | ||
e24b21ec | 286 | pipe = usb_rcvbulkpipe(data->udev, 0x82); |
1da177e4 | 287 | |
e24b21ec MH |
288 | usb_fill_bulk_urb(urb, data->udev, pipe, |
289 | buf, size, bpa10x_rx_complete, hdev); | |
1da177e4 | 290 | |
e24b21ec | 291 | urb->transfer_flags |= URB_FREE_BUFFER; |
1da177e4 | 292 | |
e24b21ec | 293 | usb_anchor_urb(urb, &data->rx_anchor); |
1da177e4 | 294 | |
e24b21ec MH |
295 | err = usb_submit_urb(urb, GFP_KERNEL); |
296 | if (err < 0) { | |
297 | BT_ERR("%s urb %p submission failed (%d)", | |
298 | hdev->name, urb, -err); | |
299 | usb_unanchor_urb(urb); | |
1da177e4 LT |
300 | } |
301 | ||
1da177e4 | 302 | usb_free_urb(urb); |
e24b21ec MH |
303 | |
304 | return err; | |
1da177e4 LT |
305 | } |
306 | ||
307 | static int bpa10x_open(struct hci_dev *hdev) | |
308 | { | |
309 | struct bpa10x_data *data = hdev->driver_data; | |
1da177e4 LT |
310 | int err; |
311 | ||
e24b21ec | 312 | BT_DBG("%s", hdev->name); |
1da177e4 LT |
313 | |
314 | if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) | |
315 | return 0; | |
316 | ||
e24b21ec MH |
317 | err = bpa10x_submit_intr_urb(hdev); |
318 | if (err < 0) | |
319 | goto error; | |
1da177e4 | 320 | |
e24b21ec MH |
321 | err = bpa10x_submit_bulk_urb(hdev); |
322 | if (err < 0) | |
323 | goto error; | |
1da177e4 | 324 | |
e24b21ec | 325 | return 0; |
1da177e4 | 326 | |
e24b21ec MH |
327 | error: |
328 | usb_kill_anchored_urbs(&data->rx_anchor); | |
1da177e4 | 329 | |
e24b21ec | 330 | clear_bit(HCI_RUNNING, &hdev->flags); |
1da177e4 LT |
331 | |
332 | return err; | |
333 | } | |
334 | ||
335 | static int bpa10x_close(struct hci_dev *hdev) | |
336 | { | |
337 | struct bpa10x_data *data = hdev->driver_data; | |
1da177e4 | 338 | |
e24b21ec | 339 | BT_DBG("%s", hdev->name); |
1da177e4 LT |
340 | |
341 | if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) | |
342 | return 0; | |
343 | ||
e24b21ec | 344 | usb_kill_anchored_urbs(&data->rx_anchor); |
1da177e4 LT |
345 | |
346 | return 0; | |
347 | } | |
348 | ||
349 | static int bpa10x_flush(struct hci_dev *hdev) | |
350 | { | |
351 | struct bpa10x_data *data = hdev->driver_data; | |
352 | ||
e24b21ec | 353 | BT_DBG("%s", hdev->name); |
1da177e4 | 354 | |
e24b21ec | 355 | usb_kill_anchored_urbs(&data->tx_anchor); |
1da177e4 LT |
356 | |
357 | return 0; | |
358 | } | |
359 | ||
360 | static int bpa10x_send_frame(struct sk_buff *skb) | |
361 | { | |
362 | struct hci_dev *hdev = (struct hci_dev *) skb->dev; | |
e24b21ec MH |
363 | struct bpa10x_data *data = hdev->driver_data; |
364 | struct usb_ctrlrequest *dr; | |
365 | struct urb *urb; | |
366 | unsigned int pipe; | |
367 | int err; | |
1da177e4 | 368 | |
e24b21ec | 369 | BT_DBG("%s", hdev->name); |
1da177e4 LT |
370 | |
371 | if (!test_bit(HCI_RUNNING, &hdev->flags)) | |
372 | return -EBUSY; | |
373 | ||
e24b21ec MH |
374 | urb = usb_alloc_urb(0, GFP_ATOMIC); |
375 | if (!urb) | |
376 | return -ENOMEM; | |
1da177e4 LT |
377 | |
378 | /* Prepend skb with frame type */ | |
e24b21ec | 379 | *skb_push(skb, 1) = bt_cb(skb)->pkt_type; |
1da177e4 | 380 | |
0d48d939 | 381 | switch (bt_cb(skb)->pkt_type) { |
1da177e4 | 382 | case HCI_COMMAND_PKT: |
e24b21ec MH |
383 | dr = kmalloc(sizeof(*dr), GFP_ATOMIC); |
384 | if (!dr) { | |
385 | usb_free_urb(urb); | |
386 | return -ENOMEM; | |
387 | } | |
388 | ||
389 | dr->bRequestType = USB_TYPE_VENDOR; | |
390 | dr->bRequest = 0; | |
391 | dr->wIndex = 0; | |
392 | dr->wValue = 0; | |
393 | dr->wLength = __cpu_to_le16(skb->len); | |
394 | ||
395 | pipe = usb_sndctrlpipe(data->udev, 0x00); | |
396 | ||
397 | usb_fill_control_urb(urb, data->udev, pipe, (void *) dr, | |
398 | skb->data, skb->len, bpa10x_tx_complete, skb); | |
399 | ||
1da177e4 | 400 | hdev->stat.cmd_tx++; |
1da177e4 LT |
401 | break; |
402 | ||
403 | case HCI_ACLDATA_PKT: | |
e24b21ec MH |
404 | pipe = usb_sndbulkpipe(data->udev, 0x02); |
405 | ||
406 | usb_fill_bulk_urb(urb, data->udev, pipe, | |
407 | skb->data, skb->len, bpa10x_tx_complete, skb); | |
408 | ||
1da177e4 | 409 | hdev->stat.acl_tx++; |
1da177e4 LT |
410 | break; |
411 | ||
412 | case HCI_SCODATA_PKT: | |
e24b21ec MH |
413 | pipe = usb_sndbulkpipe(data->udev, 0x02); |
414 | ||
415 | usb_fill_bulk_urb(urb, data->udev, pipe, | |
416 | skb->data, skb->len, bpa10x_tx_complete, skb); | |
417 | ||
1da177e4 | 418 | hdev->stat.sco_tx++; |
1da177e4 | 419 | break; |
1da177e4 | 420 | |
e24b21ec | 421 | default: |
cb7cd429 | 422 | usb_free_urb(urb); |
e24b21ec MH |
423 | return -EILSEQ; |
424 | } | |
425 | ||
426 | usb_anchor_urb(urb, &data->tx_anchor); | |
1da177e4 | 427 | |
e24b21ec MH |
428 | err = usb_submit_urb(urb, GFP_ATOMIC); |
429 | if (err < 0) { | |
430 | BT_ERR("%s urb %p submission failed", hdev->name, urb); | |
431 | kfree(urb->setup_packet); | |
432 | usb_unanchor_urb(urb); | |
433 | } | |
1da177e4 | 434 | |
e24b21ec | 435 | usb_free_urb(urb); |
1da177e4 LT |
436 | |
437 | return 0; | |
438 | } | |
439 | ||
440 | static void bpa10x_destruct(struct hci_dev *hdev) | |
441 | { | |
442 | struct bpa10x_data *data = hdev->driver_data; | |
443 | ||
e24b21ec | 444 | BT_DBG("%s", hdev->name); |
1da177e4 | 445 | |
cbafe312 IJ |
446 | kfree_skb(data->rx_skb[0]); |
447 | kfree_skb(data->rx_skb[1]); | |
1da177e4 LT |
448 | kfree(data); |
449 | } | |
450 | ||
451 | static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id) | |
452 | { | |
1da177e4 | 453 | struct bpa10x_data *data; |
e24b21ec | 454 | struct hci_dev *hdev; |
1da177e4 LT |
455 | int err; |
456 | ||
457 | BT_DBG("intf %p id %p", intf, id); | |
458 | ||
e24b21ec | 459 | if (intf->cur_altsetting->desc.bInterfaceNumber != 0) |
1da177e4 LT |
460 | return -ENODEV; |
461 | ||
089b1dbb | 462 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
e24b21ec | 463 | if (!data) |
1da177e4 | 464 | return -ENOMEM; |
1da177e4 | 465 | |
e24b21ec | 466 | data->udev = interface_to_usbdev(intf); |
1da177e4 | 467 | |
e24b21ec MH |
468 | init_usb_anchor(&data->tx_anchor); |
469 | init_usb_anchor(&data->rx_anchor); | |
1da177e4 LT |
470 | |
471 | hdev = hci_alloc_dev(); | |
472 | if (!hdev) { | |
1da177e4 LT |
473 | kfree(data); |
474 | return -ENOMEM; | |
475 | } | |
476 | ||
1da177e4 LT |
477 | hdev->type = HCI_USB; |
478 | hdev->driver_data = data; | |
e24b21ec MH |
479 | |
480 | data->hdev = hdev; | |
481 | ||
1da177e4 LT |
482 | SET_HCIDEV_DEV(hdev, &intf->dev); |
483 | ||
e24b21ec MH |
484 | hdev->open = bpa10x_open; |
485 | hdev->close = bpa10x_close; | |
486 | hdev->flush = bpa10x_flush; | |
487 | hdev->send = bpa10x_send_frame; | |
488 | hdev->destruct = bpa10x_destruct; | |
1da177e4 LT |
489 | |
490 | hdev->owner = THIS_MODULE; | |
491 | ||
492 | err = hci_register_dev(hdev); | |
493 | if (err < 0) { | |
1da177e4 | 494 | hci_free_dev(hdev); |
e24b21ec | 495 | kfree(data); |
1da177e4 LT |
496 | return err; |
497 | } | |
498 | ||
499 | usb_set_intfdata(intf, data); | |
500 | ||
501 | return 0; | |
502 | } | |
503 | ||
504 | static void bpa10x_disconnect(struct usb_interface *intf) | |
505 | { | |
506 | struct bpa10x_data *data = usb_get_intfdata(intf); | |
1da177e4 LT |
507 | |
508 | BT_DBG("intf %p", intf); | |
509 | ||
e24b21ec | 510 | if (!data) |
1da177e4 LT |
511 | return; |
512 | ||
513 | usb_set_intfdata(intf, NULL); | |
514 | ||
e24b21ec | 515 | hci_unregister_dev(data->hdev); |
1da177e4 | 516 | |
e24b21ec | 517 | hci_free_dev(data->hdev); |
1da177e4 LT |
518 | } |
519 | ||
520 | static struct usb_driver bpa10x_driver = { | |
1da177e4 LT |
521 | .name = "bpa10x", |
522 | .probe = bpa10x_probe, | |
523 | .disconnect = bpa10x_disconnect, | |
524 | .id_table = bpa10x_table, | |
525 | }; | |
526 | ||
527 | static int __init bpa10x_init(void) | |
528 | { | |
1da177e4 LT |
529 | BT_INFO("Digianswer Bluetooth USB driver ver %s", VERSION); |
530 | ||
e24b21ec | 531 | return usb_register(&bpa10x_driver); |
1da177e4 LT |
532 | } |
533 | ||
534 | static void __exit bpa10x_exit(void) | |
535 | { | |
536 | usb_deregister(&bpa10x_driver); | |
537 | } | |
538 | ||
539 | module_init(bpa10x_init); | |
540 | module_exit(bpa10x_exit); | |
541 | ||
1da177e4 LT |
542 | MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); |
543 | MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION); | |
544 | MODULE_VERSION(VERSION); | |
545 | MODULE_LICENSE("GPL"); |