Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/holtmann/bluetooth-2.6
This commit is contained in:
commit
d2805395aa
91
MAINTAINERS
91
MAINTAINERS
|
@ -936,96 +936,21 @@ M: joern@lazybastard.org
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
||||||
|
BLUETOOTH DRIVERS
|
||||||
|
P: Marcel Holtmann
|
||||||
|
M: marcel@holtmann.org
|
||||||
|
L: linux-bluetooth@vger.kernel.org
|
||||||
|
W: http://www.bluez.org/
|
||||||
|
S: Maintained
|
||||||
|
|
||||||
BLUETOOTH SUBSYSTEM
|
BLUETOOTH SUBSYSTEM
|
||||||
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
|
T: git kernel.org:/pub/scm/linux/kernel/git/holtmann/bluetooth-2.6.git
|
||||||
S: Maintained
|
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
|
|
||||||
|
|
||||||
BLUETOOTH HIDP LAYER
|
|
||||||
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
|
|
||||||
M: marcel@holtmann.org
|
|
||||||
S: Maintained
|
|
||||||
|
|
||||||
BLUETOOTH HCI VHCI DRIVER
|
|
||||||
P: Maxim Krasnyansky
|
|
||||||
M: maxk@qualcomm.com
|
|
||||||
S: Maintained
|
|
||||||
|
|
||||||
BONDING DRIVER
|
BONDING DRIVER
|
||||||
P: Jay Vosburgh
|
P: Jay Vosburgh
|
||||||
M: fubar@us.ibm.com
|
M: fubar@us.ibm.com
|
||||||
|
|
|
@ -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
|
||||||
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 @@
|
||||||
/* ======================== 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 @@
|
||||||
*
|
*
|
||||||
* 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 @@
|
||||||
#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[] = {
|
||||||
{ } /* 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 {
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
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);
|
||||||
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);
|
||||||
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);
|
||||||
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)
|
||||||
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");
|
||||||
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 @@
|
||||||
#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 @@
|
||||||
#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)
|
||||||
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,
|
||||||
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)
|
static struct attribute_group *bt_host_groups[] = {
|
||||||
{
|
&bt_host_group,
|
||||||
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
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
struct class *bt_class = NULL;
|
static void bt_host_release(struct device *dev)
|
||||||
EXPORT_SYMBOL_GPL(bt_class);
|
|
||||||
|
|
||||||
static struct bus_type bt_bus = {
|
|
||||||
.name = "bluetooth",
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct platform_device *bt_platform;
|
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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);
|
||||||
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");
|
||||||
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);
|
||||||
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");
|
||||||
|
|
Loading…
Reference in New Issue