Commit 130a7545 authored by Marcel Holtmann's avatar Marcel Holtmann

[Bluetooth] Add quirk for broken RTX Telecom based dongles

Some RTX Telecom based USB dongles offer SCO support, but their
implementation is broken. This patch disables the use of the ISOC
interface for these devices.
parent 8b553217
...@@ -29,9 +29,7 @@ ...@@ -29,9 +29,7 @@
* Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com> * Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu> * Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
* *
* $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $
*/ */
#define VERSION "2.5"
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -57,9 +55,9 @@ ...@@ -57,9 +55,9 @@
#ifndef CONFIG_BT_HCIUSB_DEBUG #ifndef CONFIG_BT_HCIUSB_DEBUG
#undef BT_DBG #undef BT_DBG
#define BT_DBG( A... ) #define BT_DBG(D...)
#undef BT_DMP #undef BT_DMP
#define BT_DMP( A... ) #define BT_DMP(D...)
#endif #endif
#ifndef CONFIG_BT_HCIUSB_ZERO_PACKET #ifndef CONFIG_BT_HCIUSB_ZERO_PACKET
...@@ -67,6 +65,8 @@ ...@@ -67,6 +65,8 @@
#define URB_ZERO_PACKET 0 #define URB_ZERO_PACKET 0
#endif #endif
#define VERSION "2.6"
static struct usb_driver hci_usb_driver; static struct usb_driver hci_usb_driver;
static struct usb_device_id bluetooth_ids[] = { static struct usb_device_id bluetooth_ids[] = {
...@@ -100,7 +100,10 @@ static struct usb_device_id blacklist_ids[] = { ...@@ -100,7 +100,10 @@ static struct usb_device_id blacklist_ids[] = {
/* Digianswer device */ /* Digianswer device */
{ USB_DEVICE(0x08fd, 0x0001), .driver_info = HCI_DIGIANSWER }, { USB_DEVICE(0x08fd, 0x0001), .driver_info = HCI_DIGIANSWER },
{ } /* Terminating entry */ /* RTX Telecom based adapter with buggy SCO support */
{ USB_DEVICE(0x0400, 0x0807), .driver_info = HCI_BROKEN_ISOC },
{ } /* Terminating entry */
}; };
struct _urb *_urb_alloc(int isoc, int gfp) struct _urb *_urb_alloc(int isoc, int gfp)
...@@ -393,7 +396,7 @@ static int hci_usb_close(struct hci_dev *hdev) ...@@ -393,7 +396,7 @@ static int hci_usb_close(struct hci_dev *hdev)
{ {
struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
unsigned long flags; unsigned long flags;
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0; return 0;
...@@ -402,7 +405,7 @@ static int hci_usb_close(struct hci_dev *hdev) ...@@ -402,7 +405,7 @@ static int hci_usb_close(struct hci_dev *hdev)
/* Synchronize with completion handlers */ /* Synchronize with completion handlers */
write_lock_irqsave(&husb->completion_lock, flags); write_lock_irqsave(&husb->completion_lock, flags);
write_unlock_irqrestore(&husb->completion_lock, flags); write_unlock_irqrestore(&husb->completion_lock, flags);
hci_usb_unlink_urbs(husb); hci_usb_unlink_urbs(husb);
hci_usb_flush(hdev); hci_usb_flush(hdev);
return 0; return 0;
...@@ -414,7 +417,7 @@ static int __tx_submit(struct hci_usb *husb, struct _urb *_urb) ...@@ -414,7 +417,7 @@ static int __tx_submit(struct hci_usb *husb, struct _urb *_urb)
int err; int err;
BT_DBG("%s urb %p type %d", husb->hdev->name, urb, _urb->type); BT_DBG("%s urb %p type %d", husb->hdev->name, urb, _urb->type);
_urb_queue_tail(__pending_q(husb, _urb->type), _urb); _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
err = usb_submit_urb(urb, GFP_ATOMIC); err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) { if (err) {
...@@ -551,7 +554,7 @@ static void hci_usb_tx_process(struct hci_usb *husb) ...@@ -551,7 +554,7 @@ static void hci_usb_tx_process(struct hci_usb *husb)
skb_queue_head(q, skb); skb_queue_head(q, skb);
} }
#endif #endif
/* Process ACL queue */ /* Process ACL queue */
q = __transmit_q(husb, HCI_ACLDATA_PKT); q = __transmit_q(husb, HCI_ACLDATA_PKT);
while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX && while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX &&
...@@ -656,7 +659,7 @@ static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int c ...@@ -656,7 +659,7 @@ static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int c
if (count >= HCI_SCO_HDR_SIZE) { if (count >= HCI_SCO_HDR_SIZE) {
struct hci_sco_hdr *h = data; struct hci_sco_hdr *h = data;
len = HCI_SCO_HDR_SIZE + h->dlen; len = HCI_SCO_HDR_SIZE + h->dlen;
} else } else
return -EILSEQ; return -EILSEQ;
break; break;
#endif #endif
...@@ -702,7 +705,7 @@ static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs) ...@@ -702,7 +705,7 @@ static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs)
struct _urb *_urb = container_of(urb, struct _urb, urb); struct _urb *_urb = container_of(urb, struct _urb, urb);
struct hci_usb *husb = (void *) urb->context; struct hci_usb *husb = (void *) urb->context;
struct hci_dev *hdev = husb->hdev; struct hci_dev *hdev = husb->hdev;
int err, count = urb->actual_length; int err, count = urb->actual_length;
BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb, BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
_urb->type, urb->status, count, urb->transfer_flags); _urb->type, urb->status, count, urb->transfer_flags);
...@@ -743,7 +746,7 @@ static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs) ...@@ -743,7 +746,7 @@ static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs)
resubmit: resubmit:
urb->dev = husb->udev; urb->dev = husb->udev;
err = usb_submit_urb(urb, GFP_ATOMIC); err = usb_submit_urb(urb, GFP_ATOMIC);
BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb, BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
_urb->type, err); _urb->type, err);
...@@ -779,7 +782,7 @@ static void hci_usb_tx_complete(struct urb *urb, struct pt_regs *regs) ...@@ -779,7 +782,7 @@ static void hci_usb_tx_complete(struct urb *urb, struct pt_regs *regs)
_urb_queue_tail(__completed_q(husb, _urb->type), _urb); _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
hci_usb_tx_wakeup(husb); hci_usb_tx_wakeup(husb);
read_unlock(&husb->completion_lock); read_unlock(&husb->completion_lock);
} }
...@@ -819,9 +822,8 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -819,9 +822,8 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (intf->cur_altsetting->desc.bInterfaceNumber > 0) if (intf->cur_altsetting->desc.bInterfaceNumber > 0)
return -ENODEV; return -ENODEV;
/* Find endpoints that we need */
/* Find endpoints that we need */
uif = intf->cur_altsetting; uif = intf->cur_altsetting;
for (e = 0; e < uif->desc.bNumEndpoints; e++) { for (e = 0; e < uif->desc.bNumEndpoints; e++) {
ep = &uif->endpoint[e]; ep = &uif->endpoint[e];
...@@ -862,16 +864,17 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -862,16 +864,17 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
husb->ctrl_req = HCI_DIGI_REQ; husb->ctrl_req = HCI_DIGI_REQ;
else else
husb->ctrl_req = HCI_CTRL_REQ; husb->ctrl_req = HCI_CTRL_REQ;
/* Find isochronous endpoints that we can use */
/* Find isochronous endpoints that we can use */
size = 0; size = 0;
isoc_iface = NULL; isoc_iface = NULL;
isoc_alts = 0; isoc_alts = 0;
isoc_ifnum = 1; isoc_ifnum = 1;
#ifdef CONFIG_BT_HCIUSB_SCO #ifdef CONFIG_BT_HCIUSB_SCO
isoc_iface = usb_ifnum_to_if(udev, isoc_ifnum); if (!(id->driver_info & HCI_BROKEN_ISOC))
isoc_iface = usb_ifnum_to_if(udev, isoc_ifnum);
if (isoc_iface) { if (isoc_iface) {
int a; int a;
struct usb_host_endpoint *isoc_out_ep = NULL; struct usb_host_endpoint *isoc_out_ep = NULL;
...@@ -917,10 +920,10 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -917,10 +920,10 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
} }
} }
#endif #endif
husb->completion_lock = RW_LOCK_UNLOCKED; husb->completion_lock = RW_LOCK_UNLOCKED;
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
skb_queue_head_init(&husb->transmit_q[i]); skb_queue_head_init(&husb->transmit_q[i]);
_urb_queue_init(&husb->pending_q[i]); _urb_queue_init(&husb->pending_q[i]);
_urb_queue_init(&husb->completed_q[i]); _urb_queue_init(&husb->completed_q[i]);
...@@ -939,10 +942,10 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -939,10 +942,10 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
hdev->driver_data = husb; hdev->driver_data = husb;
SET_HCIDEV_DEV(hdev, &intf->dev); SET_HCIDEV_DEV(hdev, &intf->dev);
hdev->open = hci_usb_open; hdev->open = hci_usb_open;
hdev->close = hci_usb_close; hdev->close = hci_usb_close;
hdev->flush = hci_usb_flush; hdev->flush = hci_usb_flush;
hdev->send = hci_usb_send_frame; hdev->send = hci_usb_send_frame;
hdev->destruct = hci_usb_destruct; hdev->destruct = hci_usb_destruct;
hdev->owner = THIS_MODULE; hdev->owner = THIS_MODULE;
...@@ -993,11 +996,11 @@ static void hci_usb_disconnect(struct usb_interface *intf) ...@@ -993,11 +996,11 @@ static void hci_usb_disconnect(struct usb_interface *intf)
} }
static struct usb_driver hci_usb_driver = { static struct usb_driver hci_usb_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "hci_usb", .name = "hci_usb",
.probe = hci_usb_probe, .probe = hci_usb_probe,
.disconnect = hci_usb_disconnect, .disconnect = hci_usb_disconnect,
.id_table = bluetooth_ids, .id_table = bluetooth_ids,
}; };
static int __init hci_usb_init(void) static int __init hci_usb_init(void)
......
...@@ -23,33 +23,28 @@ ...@@ -23,33 +23,28 @@
SOFTWARE IS DISCLAIMED. SOFTWARE IS DISCLAIMED.
*/ */
/*
* $Id: hci_usb.h,v 1.2 2002/03/18 19:10:04 maxk Exp $
*/
#ifdef __KERNEL__
/* Class, SubClass, and Protocol codes that describe a Bluetooth device */ /* Class, SubClass, and Protocol codes that describe a Bluetooth device */
#define HCI_DEV_CLASS 0xe0 /* Wireless class */ #define HCI_DEV_CLASS 0xe0 /* Wireless class */
#define HCI_DEV_SUBCLASS 0x01 /* RF subclass */ #define HCI_DEV_SUBCLASS 0x01 /* RF subclass */
#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */ #define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
#define HCI_CTRL_REQ 0x20 #define HCI_CTRL_REQ 0x20
#define HCI_DIGI_REQ 0x40 #define HCI_DIGI_REQ 0x40
#define HCI_IGNORE 0x01 #define HCI_IGNORE 0x01
#define HCI_RESET 0x02 #define HCI_RESET 0x02
#define HCI_DIGIANSWER 0x04 #define HCI_DIGIANSWER 0x04
#define HCI_BROKEN_ISOC 0x08
#define HCI_MAX_IFACE_NUM 3 #define HCI_MAX_IFACE_NUM 3
#define HCI_MAX_BULK_TX 4 #define HCI_MAX_BULK_TX 4
#define HCI_MAX_BULK_RX 1 #define HCI_MAX_BULK_RX 1
#define HCI_MAX_ISOC_RX 2 #define HCI_MAX_ISOC_RX 2
#define HCI_MAX_ISOC_TX 2 #define HCI_MAX_ISOC_TX 2
#define HCI_MAX_ISOC_FRAMES 10 #define HCI_MAX_ISOC_FRAMES 10
struct _urb_queue { struct _urb_queue {
struct list_head head; struct list_head head;
...@@ -79,16 +74,16 @@ static inline void _urb_queue_init(struct _urb_queue *q) ...@@ -79,16 +74,16 @@ static inline void _urb_queue_init(struct _urb_queue *q)
static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb) static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&q->lock, flags); spin_lock_irqsave(&q->lock, flags);
list_add(&_urb->list, &q->head); _urb->queue = q; list_add(&_urb->list, &q->head); _urb->queue = q;
spin_unlock_irqrestore(&q->lock, flags); spin_unlock_irqrestore(&q->lock, flags);
} }
static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb) static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&q->lock, flags); spin_lock_irqsave(&q->lock, flags);
list_add_tail(&_urb->list, &q->head); _urb->queue = q; list_add_tail(&_urb->list, &q->head); _urb->queue = q;
spin_unlock_irqrestore(&q->lock, flags); spin_unlock_irqrestore(&q->lock, flags);
} }
...@@ -96,9 +91,9 @@ static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb) ...@@ -96,9 +91,9 @@ static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb)
static inline void _urb_unlink(struct _urb *_urb) static inline void _urb_unlink(struct _urb *_urb)
{ {
struct _urb_queue *q = _urb->queue; struct _urb_queue *q = _urb->queue;
unsigned long flags; unsigned long flags;
if (q) { if (q) {
spin_lock_irqsave(&q->lock, flags); spin_lock_irqsave(&q->lock, flags);
list_del(&_urb->list); _urb->queue = NULL; list_del(&_urb->list); _urb->queue = NULL;
spin_unlock_irqrestore(&q->lock, flags); spin_unlock_irqrestore(&q->lock, flags);
} }
...@@ -106,41 +101,33 @@ static inline void _urb_unlink(struct _urb *_urb) ...@@ -106,41 +101,33 @@ static inline void _urb_unlink(struct _urb *_urb)
struct _urb *_urb_dequeue(struct _urb_queue *q); 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_usb {
struct hci_dev *hdev; struct hci_dev *hdev;
unsigned long state; unsigned long state;
struct usb_device *udev; struct usb_device *udev;
struct usb_host_endpoint *bulk_in_ep; struct usb_host_endpoint *bulk_in_ep;
struct usb_host_endpoint *bulk_out_ep; struct usb_host_endpoint *bulk_out_ep;
struct usb_host_endpoint *intr_in_ep; struct usb_host_endpoint *intr_in_ep;
struct usb_interface *isoc_iface; struct usb_interface *isoc_iface;
struct usb_host_endpoint *isoc_out_ep; struct usb_host_endpoint *isoc_out_ep;
struct usb_host_endpoint *isoc_in_ep; struct usb_host_endpoint *isoc_in_ep;
__u8 ctrl_req; __u8 ctrl_req;
struct sk_buff_head transmit_q[4]; struct sk_buff_head transmit_q[4];
struct sk_buff *reassembly[4]; // Reassembly buffers struct sk_buff *reassembly[4]; /* Reassembly buffers */
rwlock_t completion_lock; rwlock_t completion_lock;
atomic_t pending_tx[4]; // Number of pending requests atomic_t pending_tx[4]; /* Number of pending requests */
struct _urb_queue pending_q[4]; // Pending requests struct _urb_queue pending_q[4]; /* Pending requests */
struct _urb_queue completed_q[4]; // Completed requests struct _urb_queue completed_q[4]; /* Completed requests */
}; };
/* States */ /* States */
#define HCI_USB_TX_PROCESS 1 #define HCI_USB_TX_PROCESS 1
#define HCI_USB_TX_WAKEUP 2 #define HCI_USB_TX_WAKEUP 2
#endif /* __KERNEL__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment