diff options
| author | Maksim Krasnyanskiy <maxk@qualcomm.com> | 2003-04-26 03:53:19 -0700 |
|---|---|---|
| committer | Maksim Krasnyanskiy <maxk@qualcomm.com> | 2003-04-26 03:53:19 -0700 |
| commit | bba207eb70301e10fb2e66d20e132d5f68a6b8b6 (patch) | |
| tree | 2ffcb3dcbff9929660dcc6d6ae4a58d529959ebb | |
| parent | fc53e6ecf04000814266b3b042f73daf0ca5dd72 (diff) | |
| parent | 4081c352aa59797ea69b37ce82e3465520534563 (diff) | |
Merge bk://linux.bkbits.net/linux-2.5
into qualcomm.com:/home/kernel/bt-2.5
| -rw-r--r-- | drivers/bluetooth/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/bluetooth/bluecard_cs.c | 35 | ||||
| -rw-r--r-- | drivers/bluetooth/bt3c_cs.c | 35 | ||||
| -rw-r--r-- | drivers/bluetooth/btuart_cs.c | 35 | ||||
| -rw-r--r-- | drivers/bluetooth/dtl1_cs.c | 35 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_usb.c | 813 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_usb.h | 99 | ||||
| -rw-r--r-- | include/net/bluetooth/hci_core.h | 10 | ||||
| -rw-r--r-- | include/net/bluetooth/rfcomm.h | 5 | ||||
| -rw-r--r-- | net/bluetooth/hci_conn.c | 1 | ||||
| -rw-r--r-- | net/bluetooth/l2cap.c | 2 | ||||
| -rw-r--r-- | net/bluetooth/rfcomm/core.c | 51 |
12 files changed, 689 insertions, 441 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index c047c90251ea..30d099c33aee 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -13,11 +13,18 @@ config BT_HCIUSB Say Y here to compile support for Bluetooth USB devices into the kernel or say M to compile it as module (hci_usb). +config BT_USB_SCO + bool "SCO over HCI USB support" + depends on BT_HCIUSB + help + This option enables the SCO support in the HCI USB driver. You need this + to transmit voice data with your Bluetooth USB device. + Say Y here to compile support for SCO over HCI USB. + config BT_USB_ZERO_PACKET bool "USB zero packet support" depends on BT_HCIUSB help - Support for USB zero packets. This option is provided only as a work around for buggy Bluetooth USB devices. Do _not_ enable it unless you know for sure that your device requires zero packets. diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index f2dbb950c8ce..39e2cd68c48e 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -1075,36 +1075,29 @@ int bluecard_event(event_t event, int priority, event_callback_args_t *args) return 0; } - - -/* ======================== Module initialization ======================== */ - - -int __init init_bluecard_cs(void) +static struct pcmcia_driver bluecard_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "bluecard_cs", + }, + .attach = bluecard_attach, + .detach = bluecard_detach, +}; + +static int __init init_bluecard_cs(void) { - servinfo_t serv; - int err; - - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "bluecard_cs: Card Services release does not match!\n"); - return -1; - } - - err = register_pccard_driver(&dev_info, &bluecard_attach, &bluecard_detach); - - return err; + return pcmcia_register_driver(&bluecard_driver); } -void __exit exit_bluecard_cs(void) +static void __exit exit_bluecard_cs(void) { - unregister_pccard_driver(&dev_info); + pcmcia_unregister_driver(&bluecard_driver); + /* XXX: this really needs to move into generic code.. */ while (dev_list != NULL) bluecard_detach(dev_list); } - module_init(init_bluecard_cs); module_exit(exit_bluecard_cs); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 6fdeb612da0d..bf7c41a41e4d 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -861,36 +861,29 @@ int bt3c_event(event_t event, int priority, event_callback_args_t *args) return 0; } - - -/* ======================== Module initialization ======================== */ - - -int __init init_bt3c_cs(void) +static struct pcmcia_driver bt3c_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "bt3c_cs", + }, + .attach = bt3c_attach, + .detach = bt3c_detach, +}; + +static int __init init_bt3c_cs(void) { - servinfo_t serv; - int err; - - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "bt3c_cs: Card Services release does not match!\n"); - return -1; - } - - err = register_pccard_driver(&dev_info, &bt3c_attach, &bt3c_detach); - - return err; + return pcmcia_register_driver(&bt3c_driver); } -void __exit exit_bt3c_cs(void) +static void __exit exit_bt3c_cs(void) { - unregister_pccard_driver(&dev_info); + pcmcia_unregister_driver(&bt3c_driver); + /* XXX: this really needs to move into generic code.. */ while (dev_list != NULL) bt3c_detach(dev_list); } - module_init(init_bt3c_cs); module_exit(exit_bt3c_cs); diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 8bd487fb9a09..b37ca4817005 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -868,36 +868,29 @@ int btuart_event(event_t event, int priority, event_callback_args_t *args) return 0; } - - -/* ======================== Module initialization ======================== */ - - -int __init init_btuart_cs(void) +static struct pcmcia_driver btuart_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "btuart_cs", + }, + .attach = btuart_attach, + .detach = btuart_detach, +}; + +static int __init init_btuart_cs(void) { - servinfo_t serv; - int err; - - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "btuart_cs: Card Services release does not match!\n"); - return -1; - } - - err = register_pccard_driver(&dev_info, &btuart_attach, &btuart_detach); - - return err; + return pcmcia_register_driver(&btuart_driver); } -void __exit exit_btuart_cs(void) +static void __exit exit_btuart_cs(void) { - unregister_pccard_driver(&dev_info); + pcmcia_unregister_driver(&btuart_driver); + /* XXX: this really needs to move into generic code.. */ while (dev_list != NULL) btuart_detach(dev_list); } - module_init(init_btuart_cs); module_exit(exit_btuart_cs); diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 9b9a3720d179..766fcdd31a78 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -820,36 +820,29 @@ int dtl1_event(event_t event, int priority, event_callback_args_t *args) return 0; } - - -/* ======================== Module initialization ======================== */ - - -int __init init_dtl1_cs(void) +static struct pcmcia_driver dtl1_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "dtl1_cs", + }, + .attach = dtl1_attach, + .detach = dtl1_detach, +}; + +static int __init init_dtl1_cs(void) { - servinfo_t serv; - int err; - - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "dtl1_cs: Card Services release does not match!\n"); - return -1; - } - - err = register_pccard_driver(&dev_info, &dtl1_attach, &dtl1_detach); - - return err; + return pcmcia_register_driver(&dtl1_driver); } -void __exit exit_dtl1_cs(void) +static void __exit exit_dtl1_cs(void) { - unregister_pccard_driver(&dev_info); + pcmcia_unregister_driver(&dtl1_driver); + /* XXX: this really needs to move into generic code.. */ while (dev_list != NULL) dtl1_detach(dev_list); } - module_init(init_dtl1_cs); module_exit(exit_dtl1_cs); diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c index 4f8cec4fedee..2b173dfe5f9e 100644 --- a/drivers/bluetooth/hci_usb.c +++ b/drivers/bluetooth/hci_usb.c @@ -1,9 +1,10 @@ /* - BlueZ - Bluetooth protocol stack for Linux + HCI USB driver for Linux Bluetooth protocol stack (BlueZ) Copyright (C) 2000-2001 Qualcomm Incorporated - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> + Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com> + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; @@ -30,13 +31,11 @@ * * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $ */ -#define VERSION "2.1" +#define VERSION "2.4" #include <linux/config.h> #include <linux/module.h> -#define __KERNEL_SYSCALLS__ - #include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> @@ -49,17 +48,15 @@ #include <linux/errno.h> #include <linux/string.h> #include <linux/skbuff.h> -#include <linux/kmod.h> #include <linux/usb.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> -#include "hci_usb.h" -#define HCI_MAX_PENDING (HCI_MAX_BULK_RX + HCI_MAX_BULK_TX + 1) +#include "hci_usb.h" -#ifndef CONFIG_BT_HCIUSB_DEBUG +#ifndef HCI_USB_DEBUG #undef BT_DBG #define BT_DBG( A... ) #undef BT_DMP @@ -67,8 +64,8 @@ #endif #ifndef CONFIG_BT_USB_ZERO_PACKET -#undef URB_ZERO_PACKET -#define URB_ZERO_PACKET 0 +#undef USB_ZERO_PACKET +#define USB_ZERO_PACKET 0 #endif static struct usb_driver hci_usb_driver; @@ -80,6 +77,9 @@ static struct usb_device_id bluetooth_ids[] = { /* Ericsson with non-standard id */ { USB_DEVICE(0x0bdb, 0x1002) }, + /* Bluetooth Ultraport Module from IBM */ + { USB_DEVICE(0x04bf, 0x030a) }, + { } /* Terminating entry */ }; @@ -92,108 +92,196 @@ static struct usb_device_id ignore_ids[] = { { } /* Terminating entry */ }; -static void hci_usb_interrupt(struct urb *urb, struct pt_regs *regs); +struct _urb *_urb_alloc(int isoc, int gfp) +{ + struct _urb *_urb = kmalloc(sizeof(struct _urb) + + sizeof(struct usb_iso_packet_descriptor) * isoc, gfp); + if (_urb) { + memset(_urb, 0, sizeof(*_urb)); + _urb->urb.count = (atomic_t)ATOMIC_INIT(1); + spin_lock_init(&_urb->urb.lock); + } + return _urb; +} + +struct _urb *_urb_dequeue(struct _urb_queue *q) +{ + struct _urb *_urb = NULL; + unsigned long flags; + spin_lock_irqsave(&q->lock, flags); + { + struct list_head *head = &q->head; + struct list_head *next = head->next; + if (next != head) { + _urb = list_entry(next, struct _urb, list); + list_del(next); _urb->queue = NULL; + } + } + spin_unlock_irqrestore(&q->lock, flags); + return _urb; +} + static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs); static void hci_usb_tx_complete(struct urb *urb, struct pt_regs *regs); -static struct urb *hci_usb_get_completed(struct hci_usb *husb) +#define __pending_tx(husb, type) (&husb->pending_tx[type-1]) +#define __pending_q(husb, type) (&husb->pending_q[type-1]) +#define __completed_q(husb, type) (&husb->completed_q[type-1]) +#define __transmit_q(husb, type) (&husb->transmit_q[type-1]) +#define __reassembly(husb, type) (husb->reassembly[type-1]) + +static inline struct _urb *__get_completed(struct hci_usb *husb, int type) { - struct sk_buff *skb; - struct urb *urb = NULL; + return _urb_dequeue(__completed_q(husb, type)); +} - skb = skb_dequeue(&husb->completed_q); - if (skb) { - urb = ((struct hci_usb_scb *) skb->cb)->urb; - kfree_skb(skb); - } +#ifdef CONFIG_BT_USB_SCO +static void __fill_isoc_desc(struct urb *urb, int len, int mtu) +{ + int offset = 0, i; - BT_DBG("%s urb %p", husb->hdev.name, urb); - return urb; + BT_DBG("len %d mtu %d", len, mtu); + + for (i=0; i < HCI_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) { + urb->iso_frame_desc[i].offset = offset; + urb->iso_frame_desc[i].length = mtu; + BT_DBG("desc %d offset %d len %d", i, offset, mtu); + } + if (len && i < HCI_MAX_ISOC_FRAMES) { + urb->iso_frame_desc[i].offset = offset; + urb->iso_frame_desc[i].length = len; + BT_DBG("desc %d offset %d len %d", i, offset, len); + i++; + } + urb->number_of_packets = i; } +#endif -static int hci_usb_enable_intr(struct hci_usb *husb) +static int hci_usb_intr_rx_submit(struct hci_usb *husb) { + struct _urb *_urb; struct urb *urb; - int pipe, size; + int err, pipe, interval, size; void *buf; BT_DBG("%s", husb->hdev.name); - if (!(urb = usb_alloc_urb(0, GFP_KERNEL))) + size = husb->intr_in_ep->desc.wMaxPacketSize; + + buf = kmalloc(size, GFP_ATOMIC); + if (!buf) return -ENOMEM; - if (!(buf = kmalloc(HCI_MAX_EVENT_SIZE, GFP_KERNEL))) { - usb_free_urb(urb); + _urb = _urb_alloc(0, GFP_ATOMIC); + if (!_urb) { + kfree(buf); return -ENOMEM; } + _urb->type = HCI_EVENT_PKT; + _urb_queue_tail(__pending_q(husb, _urb->type), _urb); - husb->intr_urb = urb; - - pipe = usb_rcvintpipe(husb->udev, husb->intr_ep); - size = usb_maxpacket(husb->udev, pipe, usb_pipeout(pipe)); - usb_fill_int_urb(urb, husb->udev, pipe, buf, size, - hci_usb_interrupt, husb, husb->intr_interval); + urb = &_urb->urb; + pipe = usb_rcvintpipe(husb->udev, husb->intr_in_ep->desc.bEndpointAddress); + interval = husb->intr_in_ep->desc.bInterval; + usb_fill_int_urb(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb, interval); - return usb_submit_urb(urb, GFP_KERNEL); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + BT_ERR("%s intr rx submit failed urb %p err %d", + husb->hdev.name, urb, err); + _urb_unlink(_urb); + _urb_free(_urb); + kfree(buf); + } + return err; } -static int hci_usb_disable_intr(struct hci_usb *husb) +static int hci_usb_bulk_rx_submit(struct hci_usb *husb) { - struct urb *urb = husb->intr_urb; - struct sk_buff *skb; - - BT_DBG("%s", husb->hdev.name); + struct _urb *_urb; + struct urb *urb; + int err, pipe, size = HCI_MAX_FRAME_SIZE; + void *buf; - usb_unlink_urb(urb); usb_free_urb(urb); - husb->intr_urb = NULL; + buf = kmalloc(size, GFP_ATOMIC); + if (!buf) + return -ENOMEM; - skb = husb->intr_skb; - if (skb) { - husb->intr_skb = NULL; - kfree_skb(skb); + _urb = _urb_alloc(0, GFP_ATOMIC); + if (!_urb) { + kfree(buf); + return -ENOMEM; } + _urb->type = HCI_ACLDATA_PKT; + _urb_queue_tail(__pending_q(husb, _urb->type), _urb); - return 0; + urb = &_urb->urb; + pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep->desc.bEndpointAddress); + usb_fill_bulk_urb(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb); + urb->transfer_flags = 0; + + BT_DBG("%s urb %p", husb->hdev.name, urb); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + BT_ERR("%s bulk rx submit failed urb %p err %d", + husb->hdev.name, urb, err); + _urb_unlink(_urb); + _urb_free(_urb); + kfree(buf); + } + return err; } -static int hci_usb_rx_submit(struct hci_usb *husb, struct urb *urb) +#ifdef CONFIG_BT_USB_SCO +static int hci_usb_isoc_rx_submit(struct hci_usb *husb) { - struct hci_usb_scb *scb; - struct sk_buff *skb; - int pipe, size, err; + struct _urb *_urb; + struct urb *urb; + int err, mtu, size; + void *buf; - if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC))) - return -ENOMEM; + mtu = husb->isoc_in_ep->desc.wMaxPacketSize; + size = mtu * HCI_MAX_ISOC_FRAMES; - size = HCI_MAX_FRAME_SIZE; + buf = kmalloc(size, GFP_ATOMIC); + if (!buf) + return -ENOMEM; - if (!(skb = bt_skb_alloc(size, GFP_ATOMIC))) { - usb_free_urb(urb); + _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC); + if (!_urb) { + kfree(buf); return -ENOMEM; } - - BT_DBG("%s urb %p", husb->hdev.name, urb); + _urb->type = HCI_SCODATA_PKT; + _urb_queue_tail(__pending_q(husb, _urb->type), _urb); - skb->dev = (void *) &husb->hdev; - skb->pkt_type = HCI_ACLDATA_PKT; + urb = &_urb->urb; - scb = (struct hci_usb_scb *) skb->cb; - scb->urb = urb; + urb->context = husb; + urb->dev = husb->udev; + urb->pipe = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->desc.bEndpointAddress); + urb->complete = hci_usb_rx_complete; - pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep); + urb->transfer_buffer_length = size; + urb->transfer_buffer = buf; + urb->transfer_flags = URB_ISO_ASAP; - usb_fill_bulk_urb(urb, husb->udev, pipe, skb->data, size, hci_usb_rx_complete, skb); + __fill_isoc_desc(urb, size, mtu); + + BT_DBG("%s urb %p", husb->hdev.name, urb); - skb_queue_tail(&husb->pending_q, skb); err = usb_submit_urb(urb, GFP_ATOMIC); if (err) { - BT_ERR("%s bulk rx submit failed urb %p err %d", + BT_ERR("%s isoc rx submit failed urb %p err %d", husb->hdev.name, urb, err); - skb_unlink(skb); - usb_free_urb(urb); + _urb_unlink(_urb); + _urb_free(_urb); + kfree(buf); } return err; } +#endif /* Initialize device */ static int hci_usb_open(struct hci_dev *hdev) @@ -209,13 +297,17 @@ static int hci_usb_open(struct hci_dev *hdev) write_lock_irqsave(&husb->completion_lock, flags); - err = hci_usb_enable_intr(husb); + err = hci_usb_intr_rx_submit(husb); if (!err) { for (i = 0; i < HCI_MAX_BULK_RX; i++) - hci_usb_rx_submit(husb, NULL); - } else + hci_usb_bulk_rx_submit(husb); + +#ifdef CONFIG_BT_USB_SCO + hci_usb_isoc_rx_submit(husb); +#endif + } else { clear_bit(HCI_RUNNING, &hdev->flags); - + } write_unlock_irqrestore(&husb->completion_lock, flags); return err; @@ -225,29 +317,52 @@ static int hci_usb_open(struct hci_dev *hdev) static int hci_usb_flush(struct hci_dev *hdev) { struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; + int i; BT_DBG("%s", hdev->name); - skb_queue_purge(&husb->cmd_q); - skb_queue_purge(&husb->acl_q); + for (i=0; i < 4; i++) + skb_queue_purge(&husb->transmit_q[i]); return 0; } -static inline void hci_usb_unlink_urbs(struct hci_usb *husb) +static void hci_usb_unlink_urbs(struct hci_usb *husb) { - struct sk_buff *skb; - struct urb *urb; + int i; BT_DBG("%s", husb->hdev.name); - while ((skb = skb_dequeue(&husb->pending_q))) { - urb = ((struct hci_usb_scb *) skb->cb)->urb; - usb_unlink_urb(urb); - kfree_skb(skb); - } + for (i=0; i < 4; i++) { + struct _urb *_urb; + struct urb *urb; + + /* Kill pending requests */ + while ((_urb = _urb_dequeue(&husb->pending_q[i]))) { + urb = &_urb->urb; + BT_DBG("%s unlinking _urb %p type %d urb %p", + husb->hdev.name, _urb, _urb->type, urb); + usb_unlink_urb(urb); + _urb_queue_tail(__completed_q(husb, _urb->type), _urb); + } + + /* Release completed requests */ + while ((_urb = _urb_dequeue(&husb->completed_q[i]))) { + urb = &_urb->urb; + BT_DBG("%s freeing _urb %p type %d urb %p", + husb->hdev.name, _urb, _urb->type, urb); + if (urb->setup_packet) + kfree(urb->setup_packet); + if (urb->transfer_buffer) + kfree(urb->transfer_buffer); + _urb_free(_urb); + } - while ((urb = hci_usb_get_completed(husb))) - usb_free_urb(urb); + /* Release reassembly buffers */ + if (husb->reassembly[i]) { + kfree_skb(husb->reassembly[i]); + husb->reassembly[i] = NULL; + } + } } /* Close device */ @@ -263,7 +378,6 @@ static int hci_usb_close(struct hci_dev *hdev) write_lock_irqsave(&husb->completion_lock, flags); - hci_usb_disable_intr(husb); hci_usb_unlink_urbs(husb); hci_usb_flush(hdev); @@ -271,104 +385,157 @@ static int hci_usb_close(struct hci_dev *hdev) return 0; } -static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb) +static int __tx_submit(struct hci_usb *husb, struct _urb *_urb) { - struct hci_usb_scb *scb = (void *) skb->cb; - struct urb *urb = hci_usb_get_completed(husb); - struct usb_ctrlrequest *cr; - int pipe, err; - - if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC))) - return -ENOMEM; + struct urb *urb = &_urb->urb; + int err; - if (!(cr = kmalloc(sizeof(*cr), GFP_ATOMIC))) { - usb_free_urb(urb); - return -ENOMEM; - } + BT_DBG("%s urb %p type %d", husb->hdev.name, urb, _urb->type); - pipe = usb_sndctrlpipe(husb->udev, 0); + _urb_queue_tail(__pending_q(husb, _urb->type), _urb); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + BT_ERR("%s tx submit failed urb %p type %d err %d", + husb->hdev.name, urb, _urb->type, err); + _urb_unlink(_urb); + _urb_queue_tail(__completed_q(husb, _urb->type), _urb); + } else + atomic_inc(__pending_tx(husb, _urb->type)); - cr->bRequestType = HCI_CTRL_REQ; - cr->bRequest = 0; - cr->wIndex = 0; - cr->wValue = 0; - cr->wLength = __cpu_to_le16(skb->len); + return err; +} - usb_fill_control_urb(urb, husb->udev, pipe, (void *) cr, - skb->data, skb->len, hci_usb_tx_complete, skb); +static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb) +{ + struct _urb *_urb = __get_completed(husb, skb->pkt_type); + struct usb_ctrlrequest *dr; + struct urb *urb; - BT_DBG("%s urb %p len %d", husb->hdev.name, urb, skb->len); + if (!_urb) { + _urb = _urb_alloc(0, GFP_ATOMIC); + if (!_urb) + return -ENOMEM; + _urb->type = skb->pkt_type; - scb->urb = urb; + dr = kmalloc(sizeof(*dr), GFP_ATOMIC); + if (!dr) { + _urb_free(_urb); + return -ENOMEM; + } + } else + dr = (void *) _urb->urb.setup_packet; - skb_queue_tail(&husb->pending_q, skb); - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err) { - BT_ERR("%s ctrl tx submit failed urb %p err %d", - husb->hdev.name, urb, err); - skb_unlink(skb); - usb_free_urb(urb); kfree(cr); - } - return err; + dr->bRequestType = HCI_CTRL_REQ; + dr->bRequest = 0; + dr->wIndex = 0; + dr->wValue = 0; + dr->wLength = __cpu_to_le16(skb->len); + + urb = &_urb->urb; + usb_fill_control_urb(urb, husb->udev, usb_sndctrlpipe(husb->udev, 0), + (void *) dr, skb->data, skb->len, hci_usb_tx_complete, husb); + + BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len); + + _urb->priv = skb; + return __tx_submit(husb, _urb); } static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb) { - struct hci_usb_scb *scb = (void *) skb->cb; - struct urb *urb = hci_usb_get_completed(husb); - int pipe, err; + struct _urb *_urb = __get_completed(husb, skb->pkt_type); + struct urb *urb; + int pipe; - if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC))) - return -ENOMEM; + if (!_urb) { + _urb = _urb_alloc(0, GFP_ATOMIC); + if (!_urb) + return -ENOMEM; + _urb->type = skb->pkt_type; + } - pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep); - - usb_fill_bulk_urb(urb, husb->udev, pipe, skb->data, skb->len, - hci_usb_tx_complete, skb); - urb->transfer_flags = URB_ZERO_PACKET; + urb = &_urb->urb; + pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep->desc.bEndpointAddress); + usb_fill_bulk_urb(urb, husb->udev, pipe, skb->data, skb->len, + hci_usb_tx_complete, husb); + urb->transfer_flags = USB_ZERO_PACKET; - BT_DBG("%s urb %p len %d", husb->hdev.name, urb, skb->len); + BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len); - scb->urb = urb; + _urb->priv = skb; + return __tx_submit(husb, _urb); +} - skb_queue_tail(&husb->pending_q, skb); - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err) { - BT_ERR("%s bulk tx submit failed urb %p err %d", - husb->hdev.name, urb, err); - skb_unlink(skb); - usb_free_urb(urb); +#ifdef CONFIG_BT_USB_SCO +static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb) +{ + struct _urb *_urb = __get_completed(husb, skb->pkt_type); + struct urb *urb; + + if (!_urb) { + _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC); + if (!_urb) + return -ENOMEM; + _urb->type = skb->pkt_type; } - return err; + + BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len); + + urb = &_urb->urb; + + urb->context = husb; + urb->dev = husb->udev; + urb->pipe = usb_sndisocpipe(husb->udev, husb->isoc_out_ep->desc.bEndpointAddress); + urb->complete = hci_usb_tx_complete; + urb->transfer_flags = URB_ISO_ASAP; + + urb->transfer_buffer = skb->data; + urb->transfer_buffer_length = skb->len; + + __fill_isoc_desc(urb, skb->len, husb->isoc_out_ep->desc.wMaxPacketSize); + + _urb->priv = skb; + return __tx_submit(husb, _urb); } +#endif static void hci_usb_tx_process(struct hci_usb *husb) { + struct sk_buff_head *q; struct sk_buff *skb; BT_DBG("%s", husb->hdev.name); do { clear_bit(HCI_USB_TX_WAKEUP, &husb->state); + + /* Process command queue */ + q = __transmit_q(husb, HCI_COMMAND_PKT); + if (!atomic_read(__pending_tx(husb, HCI_COMMAND_PKT)) && + (skb = skb_dequeue(q))) { + if (hci_usb_send_ctrl(husb, skb) < 0) + skb_queue_head(q, skb); + } + +#ifdef CONFIG_BT_USB_SCO + /* Process SCO queue */ + q = __transmit_q(husb, HCI_SCODATA_PKT); + if (!atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) && + (skb = skb_dequeue(q))) { + if (hci_usb_send_isoc(husb, skb) < 0) + skb_queue_head(q, skb); + } +#endif /* Process ACL queue */ - while (skb_queue_len(&husb->pending_q) < HCI_MAX_PENDING && - (skb = skb_dequeue(&husb->acl_q))) { + q = __transmit_q(husb, HCI_ACLDATA_PKT); + while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX && + (skb = skb_dequeue(q))) { if (hci_usb_send_bulk(husb, skb) < 0) { - skb_queue_head(&husb->acl_q, skb); + skb_queue_head(q, skb); break; } } - - /* Process command queue */ - if (!test_bit(HCI_USB_CTRL_TX, &husb->state) && - (skb = skb_dequeue(&husb->cmd_q)) != NULL) { - set_bit(HCI_USB_CTRL_TX, &husb->state); - if (hci_usb_send_ctrl(husb, skb) < 0) { - skb_queue_head(&husb->cmd_q, skb); - clear_bit(HCI_USB_CTRL_TX, &husb->state); - } - } } while(test_bit(HCI_USB_TX_WAKEUP, &husb->state)); } @@ -383,7 +550,7 @@ static inline void hci_usb_tx_wakeup(struct hci_usb *husb) } /* Send frames from HCI layer */ -int hci_usb_send_frame(struct sk_buff *skb) +static int hci_usb_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct hci_usb *husb; @@ -396,247 +563,227 @@ int hci_usb_send_frame(struct sk_buff *skb) if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - husb = (struct hci_usb *) hdev->driver_data; - BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); - read_lock(&husb->completion_lock); + husb = (struct hci_usb *) hdev->driver_data; switch (skb->pkt_type) { case HCI_COMMAND_PKT: - skb_queue_tail(&husb->cmd_q, skb); hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: - skb_queue_tail(&husb->acl_q, skb); hdev->stat.acl_tx++; break; +#ifdef CONFIG_BT_USB_SCO case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; +#endif + default: kfree_skb(skb); - break; + return 0; } + + read_lock(&husb->completion_lock); + + skb_queue_tail(__transmit_q(husb, skb->pkt_type), skb); hci_usb_tx_wakeup(husb); read_unlock(&husb->completion_lock); return 0; } -static void hci_usb_interrupt(struct urb *urb, struct pt_regs *regs) +static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int count) { - struct hci_usb *husb = (void *) urb->context; - struct hci_usb_scb *scb; - struct sk_buff *skb; - struct hci_event_hdr *eh; - __u8 *data = urb->transfer_buffer; - int count = urb->actual_length; - int len = HCI_EVENT_HDR_SIZE; - int status; + BT_DBG("%s type %d data %p count %d", husb->hdev.name, type, data, count); - BT_DBG("%s urb %p count %d", husb->hdev.name, urb, count); + husb->hdev.stat.byte_rx += count; - if (!test_bit(HCI_RUNNING, &husb->hdev.flags)) - return; + while (count) { + struct sk_buff *skb = __reassembly(husb, type); + struct { int expect; } *scb; + int len = 0; + + if (!skb) { + /* Start of the frame */ + + switch (type) { + case HCI_EVENT_PKT: + if (count >= HCI_EVENT_HDR_SIZE) { + struct hci_event_hdr *h = data; + len = HCI_EVENT_HDR_SIZE + h->plen; + } else + return -EILSEQ; + break; - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - BT_DBG("%s urb shutting down with status: %d", - husb->hdev.name, urb->status); - return; - default: - BT_ERR("%s nonzero urb status received: %d", - husb->hdev.name, urb->status); - goto exit; - } + case HCI_ACLDATA_PKT: + if (count >= HCI_ACL_HDR_SIZE) { + struct hci_acl_hdr *h = data; + len = HCI_ACL_HDR_SIZE + __le16_to_cpu(h->dlen); + } else + return -EILSEQ; + break; +#ifdef CONFIG_BT_USB_SCO + case HCI_SCODATA_PKT: + if (count >= HCI_SCO_HDR_SIZE) { + struct hci_sco_hdr *h = data; + len = HCI_SCO_HDR_SIZE + h->dlen; + } else + return -EILSEQ; + break; +#endif + } + BT_DBG("new packet len %d", len); + + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) { + BT_ERR("%s no memory for the packet", husb->hdev.name); + return -ENOMEM; + } + skb->dev = (void *) &husb->hdev; + skb->pkt_type = type; + + __reassembly(husb, type) = skb; + + scb = (void *) skb->cb; + scb->expect = len; + } else { + /* Continuation */ + scb = (void *) skb->cb; + len = scb->expect; + } - if (!count) { - BT_DBG("%s intr status %d, count %d", - husb->hdev.name, urb->status, count); - goto exit; - } + len = min(len, count); + + memcpy(skb_put(skb, len), data, len); - read_lock(&husb->completion_lock); - - husb->hdev.stat.byte_rx += count; + scb->expect -= len; + if (!scb->expect) { + /* Complete frame */ + __reassembly(husb, type) = NULL; + hci_recv_frame(skb); + } - if (!(skb = husb->intr_skb)) { - /* Start of the frame */ - if (count < HCI_EVENT_HDR_SIZE) - goto bad_len; + count -= len; data += len; + } + return 0; +} + +static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs) +{ + struct _urb *_urb = container_of(urb, struct _urb, urb); + struct hci_usb *husb = (void *) urb->context; + struct hci_dev *hdev = &husb->hdev; + int err, count = urb->actual_length; - eh = (struct hci_event_hdr *) data; - len = eh->plen + HCI_EVENT_HDR_SIZE; + BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb, + _urb->type, urb->status, count, urb->transfer_flags); - if (count > len) - goto bad_len; + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return; - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) { - BT_ERR("%s no memory for event packet", husb->hdev.name); - goto done; - } - scb = (void *) skb->cb; + read_lock(&husb->completion_lock); - skb->dev = (void *) &husb->hdev; - skb->pkt_type = HCI_EVENT_PKT; + if (urb->status || !count) + goto resubmit; - husb->intr_skb = skb; - scb->intr_len = len; + if (_urb->type == HCI_SCODATA_PKT) { +#ifdef CONFIG_BT_USB_SCO + int i; + for (i=0; i < urb->number_of_packets; i++) { + BT_DBG("desc %d status %d offset %d len %d", i, + urb->iso_frame_desc[i].status, + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + + if (!urb->iso_frame_desc[i].status) + __recv_frame(husb, _urb->type, + urb->transfer_buffer + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } +#else + ; +#endif } else { - /* Continuation */ - scb = (void *) skb->cb; - len = scb->intr_len; - if (count > len) { - husb->intr_skb = NULL; - kfree_skb(skb); - goto bad_len; + err = __recv_frame(husb, _urb->type, urb->transfer_buffer, count); + if (err < 0) { + BT_ERR("%s corrupted packet: type %d count %d", + husb->hdev.name, _urb->type, count); + hdev->stat.err_rx++; } } - memcpy(skb_put(skb, count), data, count); - scb->intr_len -= count; - - if (!scb->intr_len) { - /* Complete frame */ - husb->intr_skb = NULL; - hci_recv_frame(skb); - } - -done: - read_unlock(&husb->completion_lock); - goto exit; +resubmit: + urb->dev = husb->udev; + err = usb_submit_urb(urb, GFP_ATOMIC); + BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb, + _urb->type, err); -bad_len: - BT_ERR("%s bad frame len %d expected %d", husb->hdev.name, count, len); - husb->hdev.stat.err_rx++; read_unlock(&husb->completion_lock); - -exit: - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) - BT_ERR ("%s usb_submit_urb failed with result %d", - husb->hdev.name, status); } static void hci_usb_tx_complete(struct urb *urb, struct pt_regs *regs) { - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; + struct _urb *_urb = container_of(urb, struct _urb, urb); + struct hci_usb *husb = (void *) urb->context; + struct hci_dev *hdev = &husb->hdev; - BT_DBG("%s urb %p status %d flags %x", husb->hdev.name, urb, + BT_DBG("%s urb %p status %d flags %x", hdev->name, urb, urb->status, urb->transfer_flags); - if (urb->pipe == usb_sndctrlpipe(husb->udev, 0)) { - kfree(urb->setup_packet); - clear_bit(HCI_USB_CTRL_TX, &husb->state); - } + atomic_dec(__pending_tx(husb, _urb->type)); + + urb->transfer_buffer = NULL; + kfree_skb((struct sk_buff *) _urb->priv); if (!test_bit(HCI_RUNNING, &hdev->flags)) return; - read_lock(&husb->completion_lock); - if (!urb->status) - husb->hdev.stat.byte_tx += skb->len; + hdev->stat.byte_tx += urb->transfer_buffer_length; else - husb->hdev.stat.err_tx++; - - skb_unlink(skb); - skb_queue_tail(&husb->completed_q, skb); - hci_usb_tx_wakeup(husb); - - read_unlock(&husb->completion_lock); - return; -} - -static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs) -{ - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; - int status, count = urb->actual_length; - struct hci_acl_hdr *ah; - int dlen, size; - - BT_DBG("%s urb %p status %d count %d flags %x", husb->hdev.name, urb, - urb->status, count, urb->transfer_flags); - - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return; + hdev->stat.err_tx++; read_lock(&husb->completion_lock); - if (urb->status || !count) - goto resubmit; - - husb->hdev.stat.byte_rx += count; - - ah = (struct hci_acl_hdr *) skb->data; - dlen = __le16_to_cpu(ah->dlen); - size = HCI_ACL_HDR_SIZE + dlen; + _urb_unlink(_urb); + _urb_queue_tail(__completed_q(husb, _urb->type), _urb); - /* Verify frame len and completeness */ - if (count != size) { - BT_ERR("%s corrupted ACL packet: count %d, dlen %d", - husb->hdev.name, count, dlen); - bt_dump("hci_usb", skb->data, count); - husb->hdev.stat.err_rx++; - goto resubmit; - } - - skb_unlink(skb); - skb_put(skb, count); - hci_recv_frame(skb); - - hci_usb_rx_submit(husb, urb); - - read_unlock(&husb->completion_lock); - return; - -resubmit: - urb->dev = husb->udev; - status = usb_submit_urb(urb, GFP_ATOMIC); - BT_DBG("%s URB resubmit status %d", husb->hdev.name, status); + hci_usb_tx_wakeup(husb); + read_unlock(&husb->completion_lock); } static void hci_usb_destruct(struct hci_dev *hdev) { - struct hci_usb *husb; - - if (!hdev) return; + struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; BT_DBG("%s", hdev->name); - husb = (struct hci_usb *) hdev->driver_data; kfree(husb); } int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev(intf); + struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_endpoint *bulk_out_ep[HCI_MAX_IFACE_NUM]; struct usb_host_endpoint *isoc_out_ep[HCI_MAX_IFACE_NUM]; struct usb_host_endpoint *bulk_in_ep[HCI_MAX_IFACE_NUM]; struct usb_host_endpoint *isoc_in_ep[HCI_MAX_IFACE_NUM]; struct usb_host_endpoint *intr_in_ep[HCI_MAX_IFACE_NUM]; + struct usb_host_endpoint *ep; struct usb_host_interface *uif; - struct usb_host_endpoint *ep; struct usb_interface *iface, *isoc_iface; struct hci_usb *husb; struct hci_dev *hdev; int i, a, e, size, ifn, isoc_ifnum, isoc_alts; - BT_DBG("intf %p", intf); + BT_DBG("udev %p ifnum %d", udev, ifnum); + + iface = &udev->actconfig->interface[0]; /* Check our black list */ if (usb_match_id(intf, ignore_ids)) @@ -679,6 +826,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) bulk_out_ep[i] = ep; break; +#ifdef CONFIG_BT_USB_SCO case USB_ENDPOINT_XFER_ISOC: if (ep->desc.wMaxPacketSize < size) break; @@ -693,6 +841,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) else isoc_out_ep[i] = ep; break; +#endif } } } @@ -703,10 +852,12 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) goto done; } +#ifdef CONFIG_BT_USB_SCO if (!isoc_in_ep[1] || !isoc_out_ep[1]) { BT_DBG("Isoc endpoints not found"); isoc_iface = NULL; } +#endif if (!(husb = kmalloc(sizeof(struct hci_usb), GFP_KERNEL))) { BT_ERR("Can't allocate: control structure"); @@ -716,35 +867,36 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) memset(husb, 0, sizeof(struct hci_usb)); husb->udev = udev; - husb->bulk_out_ep = bulk_out_ep[0]->desc.bEndpointAddress; - husb->bulk_in_ep = bulk_in_ep[0]->desc.bEndpointAddress; - - husb->intr_ep = intr_in_ep[0]->desc.bEndpointAddress; - husb->intr_interval = intr_in_ep[0]->desc.bInterval; + husb->bulk_out_ep = bulk_out_ep[0]; + husb->bulk_in_ep = bulk_in_ep[0]; + husb->intr_in_ep = intr_in_ep[0]; +#ifdef CONFIG_BT_USB_SCO if (isoc_iface) { + BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts); if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) { BT_ERR("Can't set isoc interface settings"); isoc_iface = NULL; } usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb); husb->isoc_iface = isoc_iface; - - husb->isoc_in_ep = isoc_in_ep[1]->desc.bEndpointAddress; - husb->isoc_out_ep = isoc_in_ep[1]->desc.bEndpointAddress; + husb->isoc_in_ep = isoc_in_ep[isoc_ifnum]; + husb->isoc_out_ep = isoc_out_ep[isoc_ifnum]; } - - husb->completion_lock = RW_LOCK_UNLOCKED; +#endif - skb_queue_head_init(&husb->acl_q); - skb_queue_head_init(&husb->cmd_q); - skb_queue_head_init(&husb->pending_q); - skb_queue_head_init(&husb->completed_q); + husb->completion_lock = RW_LOCK_UNLOCKED; + + for (i = 0; i < 4; i++) { + skb_queue_head_init(&husb->transmit_q[i]); + _urb_queue_init(&husb->pending_q[i]); + _urb_queue_init(&husb->completed_q[i]); + } /* Initialize and register HCI device */ hdev = &husb->hdev; - hdev->type = HCI_USB; + hdev->type = HCI_USB; hdev->driver_data = husb; hdev->open = hci_usb_open; @@ -754,7 +906,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) hdev->destruct = hci_usb_destruct; hdev->owner = THIS_MODULE; - + if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); goto probe_error; @@ -773,13 +925,12 @@ done: static void hci_usb_disconnect(struct usb_interface *intf) { struct hci_usb *husb = usb_get_intfdata(intf); - struct hci_dev *hdev; + struct hci_dev *hdev = &husb->hdev; if (!husb) return; usb_set_intfdata(intf, NULL); - hdev = &husb->hdev; BT_DBG("%s", hdev->name); hci_usb_close(hdev); @@ -792,10 +943,10 @@ static void hci_usb_disconnect(struct usb_interface *intf) } static struct usb_driver hci_usb_driver = { - .name = "hci_usb", - .probe = hci_usb_probe, - .disconnect = hci_usb_disconnect, - .id_table = bluetooth_ids + .name = "hci_usb", + .probe = hci_usb_probe, + .disconnect = hci_usb_disconnect, + .id_table = bluetooth_ids, }; int hci_usb_init(void) diff --git a/drivers/bluetooth/hci_usb.h b/drivers/bluetooth/hci_usb.h index 4e7e80d08196..1e7306286e56 100644 --- a/drivers/bluetooth/hci_usb.h +++ b/drivers/bluetooth/hci_usb.h @@ -1,9 +1,10 @@ /* - BlueZ - Bluetooth protocol stack for Linux + HCI USB driver for Linux Bluetooth protocol stack (BlueZ) Copyright (C) 2000-2001 Qualcomm Incorporated - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> + Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com> + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; @@ -40,40 +41,96 @@ #define HCI_MAX_BULK_TX 4 #define HCI_MAX_BULK_RX 1 +#define HCI_MAX_ISOC_FRAMES 10 + +struct _urb_queue { + struct list_head head; + spinlock_t lock; +}; + +struct _urb { + struct list_head list; + struct _urb_queue *queue; + int type; + void *priv; + struct urb urb; +}; + +struct _urb *_urb_alloc(int isoc, int gfp); + +static inline void _urb_free(struct _urb *_urb) +{ + kfree(_urb); +} + +static inline void _urb_queue_init(struct _urb_queue *q) +{ + INIT_LIST_HEAD(&q->head); + spin_lock_init(&q->lock); +} + +static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb) +{ + unsigned long flags; + spin_lock_irqsave(&q->lock, flags); + list_add(&_urb->list, &q->head); _urb->queue = q; + spin_unlock_irqrestore(&q->lock, flags); +} + +static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb) +{ + unsigned long flags; + spin_lock_irqsave(&q->lock, flags); + list_add_tail(&_urb->list, &q->head); _urb->queue = q; + spin_unlock_irqrestore(&q->lock, flags); +} + +static inline void _urb_unlink(struct _urb *_urb) +{ + struct _urb_queue *q = _urb->queue; + unsigned long flags; + if (q) { + spin_lock_irqsave(&q->lock, flags); + list_del(&_urb->list); _urb->queue = NULL; + spin_unlock_irqrestore(&q->lock, flags); + } +} + +struct _urb *_urb_dequeue(struct _urb_queue *q); + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + struct hci_usb { struct hci_dev hdev; unsigned long state; struct usb_device *udev; - struct usb_interface *isoc_iface; - __u8 bulk_out_ep; - __u8 bulk_in_ep; - __u8 isoc_out_ep; - __u8 isoc_in_ep; + struct usb_host_endpoint *bulk_in_ep; + struct usb_host_endpoint *bulk_out_ep; + struct usb_host_endpoint *intr_in_ep; + + struct usb_interface *isoc_iface; + struct usb_host_endpoint *isoc_out_ep; + struct usb_host_endpoint *isoc_in_ep; - __u8 intr_ep; - __u8 intr_interval; - struct urb *intr_urb; - struct sk_buff *intr_skb; + struct sk_buff_head transmit_q[4]; + struct sk_buff *reassembly[4]; // Reassembly buffers rwlock_t completion_lock; - - struct sk_buff_head cmd_q; // TX Commands - struct sk_buff_head acl_q; // TX ACLs - struct sk_buff_head pending_q; // Pending requests - struct sk_buff_head completed_q; // Completed requests -}; -struct hci_usb_scb { - struct urb *urb; - int intr_len; + atomic_t pending_tx[4]; // Number of pending requests + struct _urb_queue pending_q[4]; // Pending requests + struct _urb_queue completed_q[4]; // Completed requests }; /* States */ #define HCI_USB_TX_PROCESS 1 #define HCI_USB_TX_WAKEUP 2 -#define HCI_USB_CTRL_TX 3 #endif /* __KERNEL__ */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 044df634d3bd..5bf98447775c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -262,7 +262,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src); int hci_conn_auth(struct hci_conn *conn); int hci_conn_encrypt(struct hci_conn *conn); -static inline void hci_conn_set_timer(struct hci_conn *conn, long timeout) +static inline void hci_conn_set_timer(struct hci_conn *conn, unsigned long timeout) { mod_timer(&conn->timer, jiffies + timeout); } @@ -280,8 +280,12 @@ static inline void hci_conn_hold(struct hci_conn *conn) static inline void hci_conn_put(struct hci_conn *conn) { - if (atomic_dec_and_test(&conn->refcnt) && conn->out) - hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT); + if (atomic_dec_and_test(&conn->refcnt)) { + if (conn->type == SCO_LINK) + hci_conn_set_timer(conn, HZ / 100); + else if (conn->out) + hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT); + } } /* ----- HCI tasks ----- */ diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 73d85aae16f9..3393e95c7e0f 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -146,6 +146,11 @@ struct rfcomm_rpn { u16 param_mask; } __attribute__ ((packed)); +struct rfcomm_rls { + u8 dlci; + u8 status; +} __attribute__ ((packed)); + struct rfcomm_msc { u8 dlci; u8 v24_sig; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 416b164db547..dca73e34a16f 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -71,6 +71,7 @@ void hci_acl_connect(struct hci_conn *conn) memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, &conn->dst); + cp.pscan_rep_mode = 0x01; if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) && inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 3b29b9aca99d..7036ab2ebb5f 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1788,7 +1788,7 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) if (sk->state != BT_LISTEN) continue; - if (!bacmp(&bt_sk(sk)->src, bdaddr)) { + if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); exact++; } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 413d02561309..5795735a4f08 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -798,6 +798,33 @@ static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, return rfcomm_send_frame(s, buf, ptr - buf); } +static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) +{ + struct rfcomm_hdr *hdr; + struct rfcomm_mcc *mcc; + struct rfcomm_rls *rls; + u8 buf[16], *ptr = buf; + + BT_DBG("%p cr %d status 0x%x", s, cr, status); + + hdr = (void *) ptr; ptr += sizeof(*hdr); + hdr->addr = __addr(s->initiator, 0); + hdr->ctrl = __ctrl(RFCOMM_UIH, 0); + hdr->len = __len8(sizeof(*mcc) + sizeof(*rls)); + + mcc = (void *) ptr; ptr += sizeof(*mcc); + mcc->type = __mcc_type(cr, RFCOMM_RLS); + mcc->len = __len8(sizeof(*rls)); + + rls = (void *) ptr; ptr += sizeof(*rls); + rls->dlci = __addr(1, dlci); + rls->status = status; + + *ptr = __fcs(buf); ptr++; + + return rfcomm_send_frame(s, buf, ptr - buf); +} + static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) { struct rfcomm_hdr *hdr; @@ -1229,6 +1256,26 @@ rpn_out: return 0; } +static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb) +{ + struct rfcomm_rls *rls = (void *) skb->data; + u8 dlci = __get_dlci(rls->dlci); + + BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status); + + if (!cr) + return 0; + + /* FIXME: We should probably do something with this + information here. But for now it's sufficient just + to reply -- Bluetooth 1.1 says it's mandatory to + recognise and respond to RLS */ + + rfcomm_send_rls(s, 0, dlci, rls->status); + + return 0; +} + static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb) { struct rfcomm_msc *msc = (void *) skb->data; @@ -1279,6 +1326,10 @@ static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb) rfcomm_recv_rpn(s, cr, len, skb); break; + case RFCOMM_RLS: + rfcomm_recv_rls(s, cr, skb); + break; + case RFCOMM_MSC: rfcomm_recv_msc(s, cr, skb); break; |
