Commit d2805395 authored by David S. Miller's avatar David S. Miller
parents f3b9605d 63fbd24e
...@@ -936,94 +936,19 @@ M: joern@lazybastard.org ...@@ -936,94 +936,19 @@ M: joern@lazybastard.org
L: linux-mtd@lists.infradead.org L: linux-mtd@lists.infradead.org
S: Maintained S: Maintained
BLUETOOTH SUBSYSTEM BLUETOOTH DRIVERS
P: Marcel Holtmann P: Marcel Holtmann
M: marcel@holtmann.org M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
L: linux-bluetooth@vger.kernel.org L: linux-bluetooth@vger.kernel.org
W: http://bluez.sf.net W: http://www.bluez.org/
W: http://www.bluez.org
W: http://www.holtmann.org/linux/bluetooth/
T: git kernel.org:/pub/scm/linux/kernel/git/holtmann/bluetooth-2.6.git
S: Maintained
BLUETOOTH RFCOMM LAYER
P: Marcel Holtmann
M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
S: Maintained
BLUETOOTH BNEP LAYER
P: Marcel Holtmann
M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
S: Maintained
BLUETOOTH CMTP LAYER
P: Marcel Holtmann
M: marcel@holtmann.org
S: Maintained S: Maintained
BLUETOOTH HIDP LAYER BLUETOOTH SUBSYSTEM
P: Marcel Holtmann
M: marcel@holtmann.org
S: Maintained
BLUETOOTH HCI UART DRIVER
P: Marcel Holtmann
M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
S: Maintained
BLUETOOTH HCI USB DRIVER
P: Marcel Holtmann
M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
S: Maintained
BLUETOOTH HCI BCM203X DRIVER
P: Marcel Holtmann
M: marcel@holtmann.org
S: Maintained
BLUETOOTH HCI BPA10X DRIVER
P: Marcel Holtmann
M: marcel@holtmann.org
S: Maintained
BLUETOOTH HCI BFUSB DRIVER
P: Marcel Holtmann
M: marcel@holtmann.org
S: Maintained
BLUETOOTH HCI DTL1 DRIVER
P: Marcel Holtmann
M: marcel@holtmann.org
S: Maintained
BLUETOOTH HCI BLUECARD DRIVER
P: Marcel Holtmann
M: marcel@holtmann.org
S: Maintained
BLUETOOTH HCI BT3C DRIVER
P: Marcel Holtmann
M: marcel@holtmann.org
S: Maintained
BLUETOOTH HCI BTUART DRIVER
P: Marcel Holtmann P: Marcel Holtmann
M: marcel@holtmann.org M: marcel@holtmann.org
S: Maintained L: linux-bluetooth@vger.kernel.org
W: http://www.bluez.org/
BLUETOOTH HCI VHCI DRIVER T: git kernel.org:/pub/scm/linux/kernel/git/holtmann/bluetooth-2.6.git
P: Maxim Krasnyansky
M: maxk@qualcomm.com
S: Maintained S: Maintained
BONDING DRIVER BONDING DRIVER
......
...@@ -3,8 +3,8 @@ menu "Bluetooth device drivers" ...@@ -3,8 +3,8 @@ menu "Bluetooth device drivers"
depends on BT depends on BT
config BT_HCIUSB config BT_HCIUSB
tristate "HCI USB driver" tristate "HCI USB driver (old version)"
depends on USB depends on USB && BT_HCIBTUSB=n
help help
Bluetooth HCI USB driver. Bluetooth HCI USB driver.
This driver is required if you want to use Bluetooth devices with This driver is required if you want to use Bluetooth devices with
...@@ -23,15 +23,13 @@ config BT_HCIUSB_SCO ...@@ -23,15 +23,13 @@ config BT_HCIUSB_SCO
Say Y here to compile support for SCO over HCI USB. Say Y here to compile support for SCO over HCI USB.
config BT_HCIBTUSB config BT_HCIBTUSB
tristate "HCI USB driver (alternate version)" tristate "HCI USB driver"
depends on USB && EXPERIMENTAL && BT_HCIUSB=n depends on USB
help help
Bluetooth HCI USB driver. Bluetooth HCI USB driver.
This driver is required if you want to use Bluetooth devices with This driver is required if you want to use Bluetooth devices with
USB interface. USB interface.
This driver is still experimental and has no SCO support.
Say Y here to compile support for Bluetooth USB devices into the Say Y here to compile support for Bluetooth USB devices into the
kernel or say M to compile it as module (btusb). kernel or say M to compile it as module (btusb).
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
/* ======================== Module parameters ======================== */ /* ======================== Module parameters ======================== */
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Jose Orlando Pereira <jop@di.uminho.pt>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card"); MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_FIRMWARE("BT3CPCC.bin"); MODULE_FIRMWARE("BT3CPCC.bin");
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* *
* Generic Bluetooth USB driver * Generic Bluetooth USB driver
* *
* Copyright (C) 2005-2007 Marcel Holtmann <marcel@holtmann.org> * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
* *
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
#define BT_DBG(D...) #define BT_DBG(D...)
#endif #endif
#define VERSION "0.2" #define VERSION "0.3"
static int ignore_dga; static int ignore_dga;
static int ignore_csr; static int ignore_csr;
...@@ -160,12 +160,16 @@ static struct usb_device_id blacklist_table[] = { ...@@ -160,12 +160,16 @@ static struct usb_device_id blacklist_table[] = {
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
#define BTUSB_MAX_ISOC_FRAMES 10
#define BTUSB_INTR_RUNNING 0 #define BTUSB_INTR_RUNNING 0
#define BTUSB_BULK_RUNNING 1 #define BTUSB_BULK_RUNNING 1
#define BTUSB_ISOC_RUNNING 2
struct btusb_data { struct btusb_data {
struct hci_dev *hdev; struct hci_dev *hdev;
struct usb_device *udev; struct usb_device *udev;
struct usb_interface *isoc;
spinlock_t lock; spinlock_t lock;
...@@ -176,10 +180,15 @@ struct btusb_data { ...@@ -176,10 +180,15 @@ struct btusb_data {
struct usb_anchor tx_anchor; struct usb_anchor tx_anchor;
struct usb_anchor intr_anchor; struct usb_anchor intr_anchor;
struct usb_anchor bulk_anchor; struct usb_anchor bulk_anchor;
struct usb_anchor isoc_anchor;
struct usb_endpoint_descriptor *intr_ep; struct usb_endpoint_descriptor *intr_ep;
struct usb_endpoint_descriptor *bulk_tx_ep; struct usb_endpoint_descriptor *bulk_tx_ep;
struct usb_endpoint_descriptor *bulk_rx_ep; struct usb_endpoint_descriptor *bulk_rx_ep;
struct usb_endpoint_descriptor *isoc_tx_ep;
struct usb_endpoint_descriptor *isoc_rx_ep;
int isoc_altsetting;
}; };
static void btusb_intr_complete(struct urb *urb) static void btusb_intr_complete(struct urb *urb)
...@@ -195,6 +204,8 @@ static void btusb_intr_complete(struct urb *urb) ...@@ -195,6 +204,8 @@ static void btusb_intr_complete(struct urb *urb)
return; return;
if (urb->status == 0) { if (urb->status == 0) {
hdev->stat.byte_rx += urb->actual_length;
if (hci_recv_fragment(hdev, HCI_EVENT_PKT, if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
urb->transfer_buffer, urb->transfer_buffer,
urb->actual_length) < 0) { urb->actual_length) < 0) {
...@@ -216,7 +227,7 @@ static void btusb_intr_complete(struct urb *urb) ...@@ -216,7 +227,7 @@ static void btusb_intr_complete(struct urb *urb)
} }
} }
static inline int btusb_submit_intr_urb(struct hci_dev *hdev) static int btusb_submit_intr_urb(struct hci_dev *hdev)
{ {
struct btusb_data *data = hdev->driver_data; struct btusb_data *data = hdev->driver_data;
struct urb *urb; struct urb *urb;
...@@ -226,6 +237,9 @@ static inline int btusb_submit_intr_urb(struct hci_dev *hdev) ...@@ -226,6 +237,9 @@ static inline int btusb_submit_intr_urb(struct hci_dev *hdev)
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
if (!data->intr_ep)
return -ENODEV;
urb = usb_alloc_urb(0, GFP_ATOMIC); urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) if (!urb)
return -ENOMEM; return -ENOMEM;
...@@ -274,6 +288,8 @@ static void btusb_bulk_complete(struct urb *urb) ...@@ -274,6 +288,8 @@ static void btusb_bulk_complete(struct urb *urb)
return; return;
if (urb->status == 0) { if (urb->status == 0) {
hdev->stat.byte_rx += urb->actual_length;
if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT, if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
urb->transfer_buffer, urb->transfer_buffer,
urb->actual_length) < 0) { urb->actual_length) < 0) {
...@@ -295,7 +311,7 @@ static void btusb_bulk_complete(struct urb *urb) ...@@ -295,7 +311,7 @@ static void btusb_bulk_complete(struct urb *urb)
} }
} }
static inline int btusb_submit_bulk_urb(struct hci_dev *hdev) static int btusb_submit_bulk_urb(struct hci_dev *hdev)
{ {
struct btusb_data *data = hdev->driver_data; struct btusb_data *data = hdev->driver_data;
struct urb *urb; struct urb *urb;
...@@ -305,6 +321,9 @@ static inline int btusb_submit_bulk_urb(struct hci_dev *hdev) ...@@ -305,6 +321,9 @@ static inline int btusb_submit_bulk_urb(struct hci_dev *hdev)
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
if (!data->bulk_rx_ep)
return -ENODEV;
urb = usb_alloc_urb(0, GFP_KERNEL); urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) if (!urb)
return -ENOMEM; return -ENOMEM;
...@@ -339,6 +358,127 @@ static inline int btusb_submit_bulk_urb(struct hci_dev *hdev) ...@@ -339,6 +358,127 @@ static inline int btusb_submit_bulk_urb(struct hci_dev *hdev)
return err; return err;
} }
static void btusb_isoc_complete(struct urb *urb)
{
struct hci_dev *hdev = urb->context;
struct btusb_data *data = hdev->driver_data;
int i, err;
BT_DBG("%s urb %p status %d count %d", hdev->name,
urb, urb->status, urb->actual_length);
if (!test_bit(HCI_RUNNING, &hdev->flags))
return;
if (urb->status == 0) {
for (i = 0; i < urb->number_of_packets; i++) {
unsigned int offset = urb->iso_frame_desc[i].offset;
unsigned int length = urb->iso_frame_desc[i].actual_length;
if (urb->iso_frame_desc[i].status)
continue;
hdev->stat.byte_rx += length;
if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
urb->transfer_buffer + offset,
length) < 0) {
BT_ERR("%s corrupted SCO packet", hdev->name);
hdev->stat.err_rx++;
}
}
}
if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
return;
usb_anchor_urb(urb, &data->isoc_anchor);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
BT_ERR("%s urb %p failed to resubmit (%d)",
hdev->name, urb, -err);
usb_unanchor_urb(urb);
}
}
static void inline __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
{
int i, offset = 0;
BT_DBG("len %d mtu %d", len, mtu);
for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
i++, offset += mtu, len -= mtu) {
urb->iso_frame_desc[i].offset = offset;
urb->iso_frame_desc[i].length = mtu;
}
if (len && i < BTUSB_MAX_ISOC_FRAMES) {
urb->iso_frame_desc[i].offset = offset;
urb->iso_frame_desc[i].length = len;
i++;
}
urb->number_of_packets = i;
}
static int btusb_submit_isoc_urb(struct hci_dev *hdev)
{
struct btusb_data *data = hdev->driver_data;
struct urb *urb;
unsigned char *buf;
unsigned int pipe;
int err, size;
BT_DBG("%s", hdev->name);
if (!data->isoc_rx_ep)
return -ENODEV;
urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
if (!urb)
return -ENOMEM;
size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
BTUSB_MAX_ISOC_FRAMES;
buf = kmalloc(size, GFP_KERNEL);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
urb->dev = data->udev;
urb->pipe = pipe;
urb->context = hdev;
urb->complete = btusb_isoc_complete;
urb->interval = data->isoc_rx_ep->bInterval;
urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
urb->transfer_buffer = buf;
urb->transfer_buffer_length = size;
__fill_isoc_descriptor(urb, size,
le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
usb_anchor_urb(urb, &data->isoc_anchor);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err < 0) {
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err);
usb_unanchor_urb(urb);
kfree(buf);
}
usb_free_urb(urb);
return err;
}
static void btusb_tx_complete(struct urb *urb) static void btusb_tx_complete(struct urb *urb)
{ {
struct sk_buff *skb = urb->context; struct sk_buff *skb = urb->context;
...@@ -392,6 +532,9 @@ static int btusb_close(struct hci_dev *hdev) ...@@ -392,6 +532,9 @@ static int btusb_close(struct hci_dev *hdev)
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0; return 0;
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->intr_anchor);
clear_bit(BTUSB_BULK_RUNNING, &data->flags); clear_bit(BTUSB_BULK_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->bulk_anchor); usb_kill_anchored_urbs(&data->bulk_anchor);
...@@ -453,6 +596,9 @@ static int btusb_send_frame(struct sk_buff *skb) ...@@ -453,6 +596,9 @@ static int btusb_send_frame(struct sk_buff *skb)
break; break;
case HCI_ACLDATA_PKT: case HCI_ACLDATA_PKT:
if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
return -ENODEV;
urb = usb_alloc_urb(0, GFP_ATOMIC); urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) if (!urb)
return -ENOMEM; return -ENOMEM;
...@@ -467,9 +613,31 @@ static int btusb_send_frame(struct sk_buff *skb) ...@@ -467,9 +613,31 @@ static int btusb_send_frame(struct sk_buff *skb)
break; break;
case HCI_SCODATA_PKT: case HCI_SCODATA_PKT:
if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
return -ENODEV;
urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
if (!urb)
return -ENOMEM;
pipe = usb_sndisocpipe(data->udev,
data->isoc_tx_ep->bEndpointAddress);
urb->dev = data->udev;
urb->pipe = pipe;
urb->context = skb;
urb->complete = btusb_tx_complete;
urb->interval = data->isoc_tx_ep->bInterval;
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = skb->data;
urb->transfer_buffer_length = skb->len;
__fill_isoc_descriptor(urb, skb->len,
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
hdev->stat.sco_tx++; hdev->stat.sco_tx++;
kfree_skb(skb); break;
return 0;
default: default:
return -EILSEQ; return -EILSEQ;
...@@ -508,22 +676,86 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt) ...@@ -508,22 +676,86 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
schedule_work(&data->work); schedule_work(&data->work);
} }
static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
{
struct btusb_data *data = hdev->driver_data;
struct usb_interface *intf = data->isoc;
struct usb_endpoint_descriptor *ep_desc;
int i, err;
if (!data->isoc)
return -ENODEV;
err = usb_set_interface(data->udev, 1, altsetting);
if (err < 0) {
BT_ERR("%s setting interface failed (%d)", hdev->name, -err);
return err;
}
data->isoc_altsetting = altsetting;
data->isoc_tx_ep = NULL;
data->isoc_rx_ep = NULL;
for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
ep_desc = &intf->cur_altsetting->endpoint[i].desc;
if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
data->isoc_tx_ep = ep_desc;
continue;
}
if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
data->isoc_rx_ep = ep_desc;
continue;
}
}
if (!data->isoc_tx_ep || !data->isoc_rx_ep) {
BT_ERR("%s invalid SCO descriptors", hdev->name);
return -ENODEV;
}
return 0;
}
static void btusb_work(struct work_struct *work) static void btusb_work(struct work_struct *work)
{ {
struct btusb_data *data = container_of(work, struct btusb_data, work); struct btusb_data *data = container_of(work, struct btusb_data, work);
struct hci_dev *hdev = data->hdev; struct hci_dev *hdev = data->hdev;
if (hdev->conn_hash.acl_num == 0) { if (hdev->conn_hash.acl_num > 0) {
if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
if (btusb_submit_bulk_urb(hdev) < 0)
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
else
btusb_submit_bulk_urb(hdev);
}
} else {
clear_bit(BTUSB_BULK_RUNNING, &data->flags); clear_bit(BTUSB_BULK_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->bulk_anchor); usb_kill_anchored_urbs(&data->bulk_anchor);
return;
} }
if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) { if (hdev->conn_hash.sco_num > 0) {
if (btusb_submit_bulk_urb(hdev) < 0) if (data->isoc_altsetting != 2) {
clear_bit(BTUSB_BULK_RUNNING, &data->flags); clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
else usb_kill_anchored_urbs(&data->isoc_anchor);
btusb_submit_bulk_urb(hdev);
if (__set_isoc_interface(hdev, 2) < 0)
return;
}
if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
if (btusb_submit_isoc_urb(hdev) < 0)
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
else
btusb_submit_isoc_urb(hdev);
}
} else {
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->isoc_anchor);
__set_isoc_interface(hdev, 0);
} }
} }
...@@ -597,6 +829,7 @@ static int btusb_probe(struct usb_interface *intf, ...@@ -597,6 +829,7 @@ static int btusb_probe(struct usb_interface *intf,
init_usb_anchor(&data->tx_anchor); init_usb_anchor(&data->tx_anchor);
init_usb_anchor(&data->intr_anchor); init_usb_anchor(&data->intr_anchor);
init_usb_anchor(&data->bulk_anchor); init_usb_anchor(&data->bulk_anchor);
init_usb_anchor(&data->isoc_anchor);
hdev = hci_alloc_dev(); hdev = hci_alloc_dev();
if (!hdev) { if (!hdev) {
...@@ -620,6 +853,9 @@ static int btusb_probe(struct usb_interface *intf, ...@@ -620,6 +853,9 @@ static int btusb_probe(struct usb_interface *intf,
hdev->owner = THIS_MODULE; hdev->owner = THIS_MODULE;
/* interface numbers are hardcoded in the spec */
data->isoc = usb_ifnum_to_if(data->udev, 1);
if (reset || id->driver_info & BTUSB_RESET) if (reset || id->driver_info & BTUSB_RESET)
set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks); set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
...@@ -628,11 +864,16 @@ static int btusb_probe(struct usb_interface *intf, ...@@ -628,11 +864,16 @@ static int btusb_probe(struct usb_interface *intf,
set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks); set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
} }
if (id->driver_info & BTUSB_BROKEN_ISOC)
data->isoc = NULL;
if (id->driver_info & BTUSB_SNIFFER) { if (id->driver_info & BTUSB_SNIFFER) {
struct usb_device *udev = interface_to_usbdev(intf); struct usb_device *udev = data->udev;
if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997) if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
data->isoc = NULL;
} }
if (id->driver_info & BTUSB_BCM92035) { if (id->driver_info & BTUSB_BCM92035) {
...@@ -646,6 +887,16 @@ static int btusb_probe(struct usb_interface *intf, ...@@ -646,6 +887,16 @@ static int btusb_probe(struct usb_interface *intf,
} }
} }
if (data->isoc) {
err = usb_driver_claim_interface(&btusb_driver,
data->isoc, NULL);
if (err < 0) {
hci_free_dev(hdev);
kfree(data);
return err;
}
}
err = hci_register_dev(hdev); err = hci_register_dev(hdev);
if (err < 0) { if (err < 0) {
hci_free_dev(hdev); hci_free_dev(hdev);
...@@ -670,6 +921,9 @@ static void btusb_disconnect(struct usb_interface *intf) ...@@ -670,6 +921,9 @@ static void btusb_disconnect(struct usb_interface *intf)
hdev = data->hdev; hdev = data->hdev;
if (data->isoc)
usb_driver_release_interface(&btusb_driver, data->isoc);
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
hci_unregister_dev(hdev); hci_unregister_dev(hdev);
......
...@@ -577,7 +577,7 @@ module_exit(hci_uart_exit); ...@@ -577,7 +577,7 @@ module_exit(hci_uart_exit);
module_param(reset, bool, 0644); module_param(reset, bool, 0644);
MODULE_PARM_DESC(reset, "Send HCI reset command on initialization"); MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION); MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION);
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -1130,7 +1130,7 @@ module_param(isoc, int, 0644); ...@@ -1130,7 +1130,7 @@ module_param(isoc, int, 0644);
MODULE_PARM_DESC(isoc, "Set isochronous transfers for SCO over HCI support"); MODULE_PARM_DESC(isoc, "Set isochronous transfers for SCO over HCI support");
#endif #endif
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth HCI USB driver ver " VERSION); MODULE_DESCRIPTION("Bluetooth HCI USB driver ver " VERSION);
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -377,7 +377,7 @@ module_exit(vhci_exit); ...@@ -377,7 +377,7 @@ module_exit(vhci_exit);
module_param(minor, int, 0444); module_param(minor, int, 0444);
MODULE_PARM_DESC(minor, "Miscellaneous minor device number"); MODULE_PARM_DESC(minor, "Miscellaneous minor device number");
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION); MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -456,7 +456,7 @@ static void __exit bt_exit(void) ...@@ -456,7 +456,7 @@ static void __exit bt_exit(void)
subsys_initcall(bt_init); subsys_initcall(bt_init);
module_exit(bt_exit); module_exit(bt_exit);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth Core ver " VERSION); MODULE_DESCRIPTION("Bluetooth Core ver " VERSION);
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -736,7 +736,7 @@ MODULE_PARM_DESC(compress_src, "Compress sources headers"); ...@@ -736,7 +736,7 @@ MODULE_PARM_DESC(compress_src, "Compress sources headers");
module_param(compress_dst, bool, 0644); module_param(compress_dst, bool, 0644);
MODULE_PARM_DESC(compress_dst, "Compress destination headers"); MODULE_PARM_DESC(compress_dst, "Compress destination headers");
MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyansky <maxk@qualcomm.com>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION); MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
...@@ -12,10 +10,164 @@ ...@@ -12,10 +10,164 @@
#undef BT_DBG #undef BT_DBG
#define BT_DBG(D...) #define BT_DBG(D...)
#endif #endif
struct class *bt_class = NULL;
EXPORT_SYMBOL_GPL(bt_class);
static struct workqueue_struct *btaddconn; static struct workqueue_struct *btaddconn;
static struct workqueue_struct *btdelconn; static struct workqueue_struct *btdelconn;
static inline char *typetostr(int type) static inline char *link_typetostr(int type)
{
switch (type) {
case ACL_LINK:
return "ACL";
case SCO_LINK:
return "SCO";
case ESCO_LINK:
return "eSCO";
default:
return "UNKNOWN";
}
}
static ssize_t show_link_type(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hci_conn *conn = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", link_typetostr(conn->type));
}
static ssize_t show_link_address(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hci_conn *conn = dev_get_drvdata(dev);
bdaddr_t bdaddr;
baswap(&bdaddr, &conn->dst);
return sprintf(buf, "%s\n", batostr(&bdaddr));
}
static ssize_t show_link_features(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hci_conn *conn = dev_get_drvdata(dev);
return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
conn->features[0], conn->features[1],
conn->features[2], conn->features[3],
conn->features[4], conn->features[5],
conn->features[6], conn->features[7]);
}
#define LINK_ATTR(_name,_mode,_show,_store) \
struct device_attribute link_attr_##_name = __ATTR(_name,_mode,_show,_store)
static LINK_ATTR(type, S_IRUGO, show_link_type, NULL);
static LINK_ATTR(address, S_IRUGO, show_link_address, NULL);
static LINK_ATTR(features, S_IRUGO, show_link_features, NULL);
static struct attribute *bt_link_attrs[] = {
&link_attr_type.attr,
&link_attr_address.attr,
&link_attr_features.attr,
NULL
};
static struct attribute_group bt_link_group = {
.attrs = bt_link_attrs,
};
static struct attribute_group *bt_link_groups[] = {
&bt_link_group,
NULL
};
static void bt_link_release(struct device *dev)
{
void *data = dev_get_drvdata(dev);
kfree(data);
}
static struct device_type bt_link = {
.name = "link",
.groups = bt_link_groups,
.release = bt_link_release,
};
static void add_conn(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn, work);
flush_workqueue(btdelconn);
if (device_add(&conn->dev) < 0) {
BT_ERR("Failed to register connection device");
return;
}
}
void hci_conn_add_sysfs(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p", conn);
conn->dev.type = &bt_link;
conn->dev.class = bt_class;
conn->dev.parent = &hdev->dev;
snprintf(conn->dev.bus_id, BUS_ID_SIZE, "%s:%d",
hdev->name, conn->handle);
dev_set_drvdata(&conn->dev, conn);
device_initialize(&conn->dev);
INIT_WORK(&conn->work, add_conn);
queue_work(btaddconn, &conn->work);
}
/*
* The rfcomm tty device will possibly retain even when conn
* is down, and sysfs doesn't support move zombie device,
* so we should move the device before conn device is destroyed.
*/
static int __match_tty(struct device *dev, void *data)
{
return !strncmp(dev->bus_id, "rfcomm", 6);
}
static void del_conn(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn, work);
struct hci_dev *hdev = conn->hdev;
while (1) {
struct device *dev;
dev = device_find_child(&conn->dev, NULL, __match_tty);
if (!dev)
break;
device_move(dev, NULL);
put_device(dev);
}
device_del(&conn->dev);
put_device(&conn->dev);
hci_dev_put(hdev);
}
void hci_conn_del_sysfs(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
if (!device_is_registered(&conn->dev))
return;
INIT_WORK(&conn->work, del_conn);
queue_work(btdelconn, &conn->work);
}
static inline char *host_typetostr(int type)
{ {
switch (type) { switch (type) {
case HCI_VIRTUAL: case HCI_VIRTUAL:
...@@ -40,7 +192,7 @@ static inline char *typetostr(int type) ...@@ -40,7 +192,7 @@ static inline char *typetostr(int type)
static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf)
{ {
struct hci_dev *hdev = dev_get_drvdata(dev); struct hci_dev *hdev = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", typetostr(hdev->type)); return sprintf(buf, "%s\n", host_typetostr(hdev->type));
} }
static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf)
...@@ -221,183 +373,62 @@ static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR, ...@@ -221,183 +373,62 @@ static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR,
static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR, static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR,
show_sniff_min_interval, store_sniff_min_interval); show_sniff_min_interval, store_sniff_min_interval);
static struct device_attribute *bt_attrs[] = { static struct attribute *bt_host_attrs[] = {
&dev_attr_type, &dev_attr_type.attr,
&dev_attr_name, &dev_attr_name.attr,
&dev_attr_class, &dev_attr_class.attr,
&dev_attr_address, &dev_attr_address.attr,
&dev_attr_features, &dev_attr_features.attr,
&dev_attr_manufacturer, &dev_attr_manufacturer.attr,
&dev_attr_hci_version, &dev_attr_hci_version.attr,
&dev_attr_hci_revision, &dev_attr_hci_revision.attr,
&dev_attr_inquiry_cache, &dev_attr_inquiry_cache.attr,
&dev_attr_idle_timeout, &dev_attr_idle_timeout.attr,
&dev_attr_sniff_max_interval, &dev_attr_sniff_max_interval.attr,
&dev_attr_sniff_min_interval, &dev_attr_sniff_min_interval.attr,
NULL NULL
}; };
static ssize_t show_conn_type(struct device *dev, struct device_attribute *attr, char *buf) static struct attribute_group bt_host_group = {
{ .attrs = bt_host_attrs,
struct hci_conn *conn = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", conn->type == ACL_LINK ? "ACL" : "SCO");
}
static ssize_t show_conn_address(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hci_conn *conn = dev_get_drvdata(dev);
bdaddr_t bdaddr;
baswap(&bdaddr, &conn->dst);
return sprintf(buf, "%s\n", batostr(&bdaddr));
}
static ssize_t show_conn_features(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hci_conn *conn = dev_get_drvdata(dev);
return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
conn->features[0], conn->features[1],
conn->features[2], conn->features[3],
conn->features[4], conn->features[5],
conn->features[6], conn->features[7]);
}
#define CONN_ATTR(_name,_mode,_show,_store) \
struct device_attribute conn_attr_##_name = __ATTR(_name,_mode,_show,_store)
static CONN_ATTR(type, S_IRUGO, show_conn_type, NULL);
static CONN_ATTR(address, S_IRUGO, show_conn_address, NULL);
static CONN_ATTR(features, S_IRUGO, show_conn_features, NULL);
static struct device_attribute *conn_attrs[] = {
&conn_attr_type,
&conn_attr_address,
&conn_attr_features,
NULL
}; };
struct class *bt_class = NULL; static struct attribute_group *bt_host_groups[] = {
EXPORT_SYMBOL_GPL(bt_class); &bt_host_group,
NULL
static struct bus_type bt_bus = {
.name = "bluetooth",
}; };
static struct platform_device *bt_platform; static void bt_host_release(struct device *dev)
static void bt_release(struct device *dev)
{ {
void *data = dev_get_drvdata(dev); void *data = dev_get_drvdata(dev);
kfree(data); kfree(data);
} }
static void add_conn(struct work_struct *work) static struct device_type bt_host = {
{ .name = "host",
struct hci_conn *conn = container_of(work, struct hci_conn, work); .groups = bt_host_groups,
int i; .release = bt_host_release,
};
flush_workqueue(btdelconn);
if (device_add(&conn->dev) < 0) {
BT_ERR("Failed to register connection device");
return;
}
for (i = 0; conn_attrs[i]; i++)
if (device_create_file(&conn->dev, conn_attrs[i]) < 0)
BT_ERR("Failed to create connection attribute");
}
void hci_conn_add_sysfs(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p", conn);
conn->dev.bus = &bt_bus;
conn->dev.parent = &hdev->dev;
conn->dev.release = bt_release;
snprintf(conn->dev.bus_id, BUS_ID_SIZE, "%s:%d",
hdev->name, conn->handle);
dev_set_drvdata(&conn->dev, conn);
device_initialize(&conn->dev);
INIT_WORK(&conn->work, add_conn);
queue_work(btaddconn, &conn->work);
}
/*
* The rfcomm tty device will possibly retain even when conn
* is down, and sysfs doesn't support move zombie device,
* so we should move the device before conn device is destroyed.
*/
static int __match_tty(struct device *dev, void *data)
{
return !strncmp(dev->bus_id, "rfcomm", 6);
}
static void del_conn(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn, work);
struct hci_dev *hdev = conn->hdev;
while (1) {
struct device *dev;
dev = device_find_child(&conn->dev, NULL, __match_tty);
if (!dev)
break;
device_move(dev, NULL);
put_device(dev);
}
device_del(&conn->dev);
put_device(&conn->dev);
hci_dev_put(hdev);
}
void hci_conn_del_sysfs(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
if (!device_is_registered(&conn->dev))
return;
INIT_WORK(&conn->work, del_conn);
queue_work(btdelconn, &conn->work);
}
int hci_register_sysfs(struct hci_dev *hdev) int hci_register_sysfs(struct hci_dev *hdev)
{ {
struct device *dev = &hdev->dev; struct device *dev = &hdev->dev;
unsigned int i;
int err; int err;
BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
dev->bus = &bt_bus; dev->type = &bt_host;
dev->class = bt_class;
dev->parent = hdev->parent; dev->parent = hdev->parent;
strlcpy(dev->bus_id, hdev->name, BUS_ID_SIZE); strlcpy(dev->bus_id, hdev->name, BUS_ID_SIZE);
dev->release = bt_release;
dev_set_drvdata(dev, hdev); dev_set_drvdata(dev, hdev);
err = device_register(dev); err = device_register(dev);
if (err < 0) if (err < 0)
return err; return err;
for (i = 0; bt_attrs[i]; i++)
if (device_create_file(dev, bt_attrs[i]) < 0)
BT_ERR("Failed to create device attribute");
return 0; return 0;
} }
...@@ -410,59 +441,30 @@ void hci_unregister_sysfs(struct hci_dev *hdev) ...@@ -410,59 +441,30 @@ void hci_unregister_sysfs(struct hci_dev *hdev)
int __init bt_sysfs_init(void) int __init bt_sysfs_init(void)
{ {
int err;
btaddconn = create_singlethread_workqueue("btaddconn"); btaddconn = create_singlethread_workqueue("btaddconn");
if (!btaddconn) { if (!btaddconn)
err = -ENOMEM; return -ENOMEM;
goto out;
}
btdelconn = create_singlethread_workqueue("btdelconn"); btdelconn = create_singlethread_workqueue("btdelconn");
if (!btdelconn) { if (!btdelconn) {
err = -ENOMEM; destroy_workqueue(btaddconn);
goto out_del; return -ENOMEM;
}
bt_platform = platform_device_register_simple("bluetooth", -1, NULL, 0);
if (IS_ERR(bt_platform)) {
err = PTR_ERR(bt_platform);
goto out_platform;
} }
err = bus_register(&bt_bus);
if (err < 0)
goto out_bus;
bt_class = class_create(THIS_MODULE, "bluetooth"); bt_class = class_create(THIS_MODULE, "bluetooth");
if (IS_ERR(bt_class)) { if (IS_ERR(bt_class)) {
err = PTR_ERR(bt_class); destroy_workqueue(btdelconn);
goto out_class; destroy_workqueue(btaddconn);
return PTR_ERR(bt_class);
} }
return 0; return 0;
out_class:
bus_unregister(&bt_bus);
out_bus:
platform_device_unregister(bt_platform);
out_platform:
destroy_workqueue(btdelconn);
out_del:
destroy_workqueue(btaddconn);
out:
return err;
} }
void bt_sysfs_cleanup(void) void bt_sysfs_cleanup(void)
{ {
destroy_workqueue(btaddconn); destroy_workqueue(btaddconn);
destroy_workqueue(btdelconn); destroy_workqueue(btdelconn);
class_destroy(bt_class); class_destroy(bt_class);
bus_unregister(&bt_bus);
platform_device_unregister(bt_platform);
} }
...@@ -2516,7 +2516,7 @@ EXPORT_SYMBOL(l2cap_load); ...@@ -2516,7 +2516,7 @@ EXPORT_SYMBOL(l2cap_load);
module_init(l2cap_init); module_init(l2cap_init);
module_exit(l2cap_exit); module_exit(l2cap_exit);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION); MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION);
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -2115,7 +2115,7 @@ MODULE_PARM_DESC(channel_mtu, "Default MTU for the RFCOMM channel"); ...@@ -2115,7 +2115,7 @@ MODULE_PARM_DESC(channel_mtu, "Default MTU for the RFCOMM channel");
module_param(l2cap_mtu, uint, 0644); module_param(l2cap_mtu, uint, 0644);
MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection"); MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection");
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION); MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION);
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -1002,7 +1002,7 @@ module_exit(sco_exit); ...@@ -1002,7 +1002,7 @@ module_exit(sco_exit);
module_param(disable_esco, bool, 0644); module_param(disable_esco, bool, 0644);
MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation"); MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation");
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION); MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
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