|
@@ -268,20 +268,24 @@ struct btusb_data {
|
|
|
struct usb_interface *intf;
|
|
|
struct usb_interface *isoc;
|
|
|
|
|
|
- spinlock_t lock;
|
|
|
-
|
|
|
unsigned long flags;
|
|
|
|
|
|
struct work_struct work;
|
|
|
struct work_struct waker;
|
|
|
|
|
|
+ struct usb_anchor deferred;
|
|
|
struct usb_anchor tx_anchor;
|
|
|
+ int tx_in_flight;
|
|
|
+ spinlock_t txlock;
|
|
|
+
|
|
|
struct usb_anchor intr_anchor;
|
|
|
struct usb_anchor bulk_anchor;
|
|
|
struct usb_anchor isoc_anchor;
|
|
|
- struct usb_anchor deferred;
|
|
|
- int tx_in_flight;
|
|
|
- spinlock_t txlock;
|
|
|
+ spinlock_t rxlock;
|
|
|
+
|
|
|
+ struct sk_buff *evt_skb;
|
|
|
+ struct sk_buff *acl_skb;
|
|
|
+ struct sk_buff *sco_skb;
|
|
|
|
|
|
struct usb_endpoint_descriptor *intr_ep;
|
|
|
struct usb_endpoint_descriptor *bulk_tx_ep;
|
|
@@ -296,18 +300,189 @@ struct btusb_data {
|
|
|
int suspend_count;
|
|
|
};
|
|
|
|
|
|
-static int inc_tx(struct btusb_data *data)
|
|
|
+static inline void btusb_free_frags(struct btusb_data *data)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
- int rv;
|
|
|
|
|
|
- spin_lock_irqsave(&data->txlock, flags);
|
|
|
- rv = test_bit(BTUSB_SUSPENDING, &data->flags);
|
|
|
- if (!rv)
|
|
|
- data->tx_in_flight++;
|
|
|
- spin_unlock_irqrestore(&data->txlock, flags);
|
|
|
+ spin_lock_irqsave(&data->rxlock, flags);
|
|
|
+
|
|
|
+ kfree_skb(data->evt_skb);
|
|
|
+ data->evt_skb = NULL;
|
|
|
+
|
|
|
+ kfree_skb(data->acl_skb);
|
|
|
+ data->acl_skb = NULL;
|
|
|
+
|
|
|
+ kfree_skb(data->sco_skb);
|
|
|
+ data->sco_skb = NULL;
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&data->rxlock, flags);
|
|
|
+}
|
|
|
+
|
|
|
+static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ spin_lock(&data->rxlock);
|
|
|
+ skb = data->evt_skb;
|
|
|
+
|
|
|
+ while (count) {
|
|
|
+ int len;
|
|
|
+
|
|
|
+ if (!skb) {
|
|
|
+ skb = bt_skb_alloc(HCI_MAX_EVENT_SIZE, GFP_ATOMIC);
|
|
|
+ if (!skb) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
|
|
|
+ bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = min_t(uint, bt_cb(skb)->expect, count);
|
|
|
+ memcpy(skb_put(skb, len), buffer, len);
|
|
|
+
|
|
|
+ count -= len;
|
|
|
+ buffer += len;
|
|
|
+ bt_cb(skb)->expect -= len;
|
|
|
+
|
|
|
+ if (skb->len == HCI_EVENT_HDR_SIZE) {
|
|
|
+ /* Complete event header */
|
|
|
+ bt_cb(skb)->expect = hci_event_hdr(skb)->plen;
|
|
|
+
|
|
|
+ if (skb_tailroom(skb) < bt_cb(skb)->expect) {
|
|
|
+ kfree_skb(skb);
|
|
|
+ skb = NULL;
|
|
|
+
|
|
|
+ err = -EILSEQ;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bt_cb(skb)->expect == 0) {
|
|
|
+ /* Complete frame */
|
|
|
+ hci_recv_frame(data->hdev, skb);
|
|
|
+ skb = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ data->evt_skb = skb;
|
|
|
+ spin_unlock(&data->rxlock);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ spin_lock(&data->rxlock);
|
|
|
+ skb = data->acl_skb;
|
|
|
+
|
|
|
+ while (count) {
|
|
|
+ int len;
|
|
|
|
|
|
- return rv;
|
|
|
+ if (!skb) {
|
|
|
+ skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
|
|
+ if (!skb) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
|
|
|
+ bt_cb(skb)->expect = HCI_ACL_HDR_SIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = min_t(uint, bt_cb(skb)->expect, count);
|
|
|
+ memcpy(skb_put(skb, len), buffer, len);
|
|
|
+
|
|
|
+ count -= len;
|
|
|
+ buffer += len;
|
|
|
+ bt_cb(skb)->expect -= len;
|
|
|
+
|
|
|
+ if (skb->len == HCI_ACL_HDR_SIZE) {
|
|
|
+ __le16 dlen = hci_acl_hdr(skb)->dlen;
|
|
|
+
|
|
|
+ /* Complete ACL header */
|
|
|
+ bt_cb(skb)->expect = __le16_to_cpu(dlen);
|
|
|
+
|
|
|
+ if (skb_tailroom(skb) < bt_cb(skb)->expect) {
|
|
|
+ kfree_skb(skb);
|
|
|
+ skb = NULL;
|
|
|
+
|
|
|
+ err = -EILSEQ;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bt_cb(skb)->expect == 0) {
|
|
|
+ /* Complete frame */
|
|
|
+ hci_recv_frame(data->hdev, skb);
|
|
|
+ skb = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ data->acl_skb = skb;
|
|
|
+ spin_unlock(&data->rxlock);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ spin_lock(&data->rxlock);
|
|
|
+ skb = data->sco_skb;
|
|
|
+
|
|
|
+ while (count) {
|
|
|
+ int len;
|
|
|
+
|
|
|
+ if (!skb) {
|
|
|
+ skb = bt_skb_alloc(HCI_MAX_SCO_SIZE, GFP_ATOMIC);
|
|
|
+ if (!skb) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
|
|
|
+ bt_cb(skb)->expect = HCI_SCO_HDR_SIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = min_t(uint, bt_cb(skb)->expect, count);
|
|
|
+ memcpy(skb_put(skb, len), buffer, len);
|
|
|
+
|
|
|
+ count -= len;
|
|
|
+ buffer += len;
|
|
|
+ bt_cb(skb)->expect -= len;
|
|
|
+
|
|
|
+ if (skb->len == HCI_SCO_HDR_SIZE) {
|
|
|
+ /* Complete SCO header */
|
|
|
+ bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen;
|
|
|
+
|
|
|
+ if (skb_tailroom(skb) < bt_cb(skb)->expect) {
|
|
|
+ kfree_skb(skb);
|
|
|
+ skb = NULL;
|
|
|
+
|
|
|
+ err = -EILSEQ;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bt_cb(skb)->expect == 0) {
|
|
|
+ /* Complete frame */
|
|
|
+ hci_recv_frame(data->hdev, skb);
|
|
|
+ skb = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ data->sco_skb = skb;
|
|
|
+ spin_unlock(&data->rxlock);
|
|
|
+
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
static void btusb_intr_complete(struct urb *urb)
|
|
@@ -316,8 +491,8 @@ static void btusb_intr_complete(struct urb *urb)
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
|
|
int err;
|
|
|
|
|
|
- BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
- urb, urb->status, urb->actual_length);
|
|
|
+ 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;
|
|
@@ -325,9 +500,8 @@ static void btusb_intr_complete(struct urb *urb)
|
|
|
if (urb->status == 0) {
|
|
|
hdev->stat.byte_rx += urb->actual_length;
|
|
|
|
|
|
- if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
|
|
|
- urb->transfer_buffer,
|
|
|
- urb->actual_length) < 0) {
|
|
|
+ if (btusb_recv_intr(data, urb->transfer_buffer,
|
|
|
+ urb->actual_length) < 0) {
|
|
|
BT_ERR("%s corrupted event packet", hdev->name);
|
|
|
hdev->stat.err_rx++;
|
|
|
}
|
|
@@ -348,7 +522,7 @@ static void btusb_intr_complete(struct urb *urb)
|
|
|
* -ENODEV: device got disconnected */
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
BT_ERR("%s urb %p failed to resubmit (%d)",
|
|
|
- hdev->name, urb, -err);
|
|
|
+ hdev->name, urb, -err);
|
|
|
usb_unanchor_urb(urb);
|
|
|
}
|
|
|
}
|
|
@@ -381,8 +555,7 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|
|
pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
|
|
|
|
|
|
usb_fill_int_urb(urb, data->udev, pipe, buf, size,
|
|
|
- btusb_intr_complete, hdev,
|
|
|
- data->intr_ep->bInterval);
|
|
|
+ btusb_intr_complete, hdev, data->intr_ep->bInterval);
|
|
|
|
|
|
urb->transfer_flags |= URB_FREE_BUFFER;
|
|
|
|
|
@@ -392,7 +565,7 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|
|
if (err < 0) {
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
BT_ERR("%s urb %p submission failed (%d)",
|
|
|
- hdev->name, urb, -err);
|
|
|
+ hdev->name, urb, -err);
|
|
|
usb_unanchor_urb(urb);
|
|
|
}
|
|
|
|
|
@@ -407,8 +580,8 @@ static void btusb_bulk_complete(struct urb *urb)
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
|
|
int err;
|
|
|
|
|
|
- BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
- urb, urb->status, urb->actual_length);
|
|
|
+ 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;
|
|
@@ -416,9 +589,8 @@ static void btusb_bulk_complete(struct urb *urb)
|
|
|
if (urb->status == 0) {
|
|
|
hdev->stat.byte_rx += urb->actual_length;
|
|
|
|
|
|
- if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
|
|
|
- urb->transfer_buffer,
|
|
|
- urb->actual_length) < 0) {
|
|
|
+ if (btusb_recv_bulk(data, urb->transfer_buffer,
|
|
|
+ urb->actual_length) < 0) {
|
|
|
BT_ERR("%s corrupted ACL packet", hdev->name);
|
|
|
hdev->stat.err_rx++;
|
|
|
}
|
|
@@ -439,7 +611,7 @@ static void btusb_bulk_complete(struct urb *urb)
|
|
|
* -ENODEV: device got disconnected */
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
BT_ERR("%s urb %p failed to resubmit (%d)",
|
|
|
- hdev->name, urb, -err);
|
|
|
+ hdev->name, urb, -err);
|
|
|
usb_unanchor_urb(urb);
|
|
|
}
|
|
|
}
|
|
@@ -469,8 +641,8 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|
|
|
|
|
pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
|
|
|
|
|
|
- usb_fill_bulk_urb(urb, data->udev, pipe,
|
|
|
- buf, size, btusb_bulk_complete, hdev);
|
|
|
+ usb_fill_bulk_urb(urb, data->udev, pipe, buf, size,
|
|
|
+ btusb_bulk_complete, hdev);
|
|
|
|
|
|
urb->transfer_flags |= URB_FREE_BUFFER;
|
|
|
|
|
@@ -481,7 +653,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|
|
if (err < 0) {
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
BT_ERR("%s urb %p submission failed (%d)",
|
|
|
- hdev->name, urb, -err);
|
|
|
+ hdev->name, urb, -err);
|
|
|
usb_unanchor_urb(urb);
|
|
|
}
|
|
|
|
|
@@ -496,8 +668,8 @@ static void btusb_isoc_complete(struct urb *urb)
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
|
|
int i, err;
|
|
|
|
|
|
- BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
- urb, urb->status, urb->actual_length);
|
|
|
+ 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;
|
|
@@ -512,9 +684,8 @@ static void btusb_isoc_complete(struct urb *urb)
|
|
|
|
|
|
hdev->stat.byte_rx += length;
|
|
|
|
|
|
- if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
|
|
|
- urb->transfer_buffer + offset,
|
|
|
- length) < 0) {
|
|
|
+ if (btusb_recv_isoc(data, urb->transfer_buffer + offset,
|
|
|
+ length) < 0) {
|
|
|
BT_ERR("%s corrupted SCO packet", hdev->name);
|
|
|
hdev->stat.err_rx++;
|
|
|
}
|
|
@@ -535,7 +706,7 @@ static void btusb_isoc_complete(struct urb *urb)
|
|
|
* -ENODEV: device got disconnected */
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
BT_ERR("%s urb %p failed to resubmit (%d)",
|
|
|
- hdev->name, urb, -err);
|
|
|
+ hdev->name, urb, -err);
|
|
|
usb_unanchor_urb(urb);
|
|
|
}
|
|
|
}
|
|
@@ -590,12 +761,12 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|
|
pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
|
|
|
|
|
|
usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_isoc_complete,
|
|
|
- hdev, data->isoc_rx_ep->bInterval);
|
|
|
+ hdev, data->isoc_rx_ep->bInterval);
|
|
|
|
|
|
- urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
|
|
|
+ urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
|
|
|
|
|
|
__fill_isoc_descriptor(urb, size,
|
|
|
- le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
|
|
|
+ le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
|
|
|
|
|
|
usb_anchor_urb(urb, &data->isoc_anchor);
|
|
|
|
|
@@ -603,7 +774,7 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|
|
if (err < 0) {
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
BT_ERR("%s urb %p submission failed (%d)",
|
|
|
- hdev->name, urb, -err);
|
|
|
+ hdev->name, urb, -err);
|
|
|
usb_unanchor_urb(urb);
|
|
|
}
|
|
|
|
|
@@ -615,11 +786,11 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|
|
static void btusb_tx_complete(struct urb *urb)
|
|
|
{
|
|
|
struct sk_buff *skb = urb->context;
|
|
|
- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
|
|
|
+ struct hci_dev *hdev = (struct hci_dev *)skb->dev;
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
|
|
|
|
|
- BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
- urb, urb->status, urb->actual_length);
|
|
|
+ BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
|
|
|
+ urb->actual_length);
|
|
|
|
|
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
goto done;
|
|
@@ -642,10 +813,10 @@ done:
|
|
|
static void btusb_isoc_tx_complete(struct urb *urb)
|
|
|
{
|
|
|
struct sk_buff *skb = urb->context;
|
|
|
- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
|
|
|
+ struct hci_dev *hdev = (struct hci_dev *)skb->dev;
|
|
|
|
|
|
- BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
- urb, urb->status, urb->actual_length);
|
|
|
+ BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
|
|
|
+ urb->actual_length);
|
|
|
|
|
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
goto done;
|
|
@@ -729,6 +900,8 @@ static int btusb_close(struct hci_dev *hdev)
|
|
|
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
|
|
|
|
|
btusb_stop_traffic(data);
|
|
|
+ btusb_free_frags(data);
|
|
|
+
|
|
|
err = usb_autopm_get_interface(data->intf);
|
|
|
if (err < 0)
|
|
|
goto failed;
|
|
@@ -748,122 +921,181 @@ static int btusb_flush(struct hci_dev *hdev)
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
|
usb_kill_anchored_urbs(&data->tx_anchor);
|
|
|
+ btusb_free_frags(data);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
+static struct urb *alloc_ctrl_urb(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
{
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
|
|
struct usb_ctrlrequest *dr;
|
|
|
struct urb *urb;
|
|
|
unsigned int pipe;
|
|
|
- int err;
|
|
|
|
|
|
- BT_DBG("%s", hdev->name);
|
|
|
+ urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
|
+ if (!urb)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
- return -EBUSY;
|
|
|
+ dr = kmalloc(sizeof(*dr), GFP_KERNEL);
|
|
|
+ if (!dr) {
|
|
|
+ usb_free_urb(urb);
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+ }
|
|
|
|
|
|
- skb->dev = (void *) hdev;
|
|
|
+ dr->bRequestType = data->cmdreq_type;
|
|
|
+ dr->bRequest = 0;
|
|
|
+ dr->wIndex = 0;
|
|
|
+ dr->wValue = 0;
|
|
|
+ dr->wLength = __cpu_to_le16(skb->len);
|
|
|
|
|
|
- switch (bt_cb(skb)->pkt_type) {
|
|
|
- case HCI_COMMAND_PKT:
|
|
|
- urb = usb_alloc_urb(0, GFP_ATOMIC);
|
|
|
- if (!urb)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
|
|
|
- if (!dr) {
|
|
|
- usb_free_urb(urb);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
+ pipe = usb_sndctrlpipe(data->udev, 0x00);
|
|
|
|
|
|
- dr->bRequestType = data->cmdreq_type;
|
|
|
- dr->bRequest = 0;
|
|
|
- dr->wIndex = 0;
|
|
|
- dr->wValue = 0;
|
|
|
- dr->wLength = __cpu_to_le16(skb->len);
|
|
|
+ usb_fill_control_urb(urb, data->udev, pipe, (void *)dr,
|
|
|
+ skb->data, skb->len, btusb_tx_complete, skb);
|
|
|
|
|
|
- pipe = usb_sndctrlpipe(data->udev, 0x00);
|
|
|
+ skb->dev = (void *)hdev;
|
|
|
|
|
|
- usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
|
|
|
- skb->data, skb->len, btusb_tx_complete, skb);
|
|
|
+ return urb;
|
|
|
+}
|
|
|
|
|
|
- hdev->stat.cmd_tx++;
|
|
|
- break;
|
|
|
+static struct urb *alloc_bulk_urb(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct btusb_data *data = hci_get_drvdata(hdev);
|
|
|
+ struct urb *urb;
|
|
|
+ unsigned int pipe;
|
|
|
|
|
|
- case HCI_ACLDATA_PKT:
|
|
|
- if (!data->bulk_tx_ep)
|
|
|
- return -ENODEV;
|
|
|
+ if (!data->bulk_tx_ep)
|
|
|
+ return ERR_PTR(-ENODEV);
|
|
|
|
|
|
- urb = usb_alloc_urb(0, GFP_ATOMIC);
|
|
|
- if (!urb)
|
|
|
- return -ENOMEM;
|
|
|
+ urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
|
+ if (!urb)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- pipe = usb_sndbulkpipe(data->udev,
|
|
|
- data->bulk_tx_ep->bEndpointAddress);
|
|
|
+ pipe = usb_sndbulkpipe(data->udev, data->bulk_tx_ep->bEndpointAddress);
|
|
|
|
|
|
- usb_fill_bulk_urb(urb, data->udev, pipe,
|
|
|
- skb->data, skb->len, btusb_tx_complete, skb);
|
|
|
+ usb_fill_bulk_urb(urb, data->udev, pipe,
|
|
|
+ skb->data, skb->len, btusb_tx_complete, skb);
|
|
|
|
|
|
- hdev->stat.acl_tx++;
|
|
|
- break;
|
|
|
+ skb->dev = (void *)hdev;
|
|
|
|
|
|
- case HCI_SCODATA_PKT:
|
|
|
- if (!data->isoc_tx_ep || hci_conn_num(hdev, SCO_LINK) < 1)
|
|
|
- return -ENODEV;
|
|
|
+ return urb;
|
|
|
+}
|
|
|
|
|
|
- urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
|
|
|
- if (!urb)
|
|
|
- return -ENOMEM;
|
|
|
+static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct btusb_data *data = hci_get_drvdata(hdev);
|
|
|
+ struct urb *urb;
|
|
|
+ unsigned int pipe;
|
|
|
|
|
|
- pipe = usb_sndisocpipe(data->udev,
|
|
|
- data->isoc_tx_ep->bEndpointAddress);
|
|
|
+ if (!data->isoc_tx_ep)
|
|
|
+ return ERR_PTR(-ENODEV);
|
|
|
|
|
|
- usb_fill_int_urb(urb, data->udev, pipe,
|
|
|
- skb->data, skb->len, btusb_isoc_tx_complete,
|
|
|
- skb, data->isoc_tx_ep->bInterval);
|
|
|
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
|
|
|
+ if (!urb)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- urb->transfer_flags = URB_ISO_ASAP;
|
|
|
+ pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress);
|
|
|
|
|
|
- __fill_isoc_descriptor(urb, skb->len,
|
|
|
- le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
|
|
|
+ usb_fill_int_urb(urb, data->udev, pipe,
|
|
|
+ skb->data, skb->len, btusb_isoc_tx_complete,
|
|
|
+ skb, data->isoc_tx_ep->bInterval);
|
|
|
|
|
|
- hdev->stat.sco_tx++;
|
|
|
- goto skip_waking;
|
|
|
+ urb->transfer_flags = URB_ISO_ASAP;
|
|
|
|
|
|
- default:
|
|
|
- return -EILSEQ;
|
|
|
- }
|
|
|
+ __fill_isoc_descriptor(urb, skb->len,
|
|
|
+ le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
|
|
|
|
|
|
- err = inc_tx(data);
|
|
|
- if (err) {
|
|
|
- usb_anchor_urb(urb, &data->deferred);
|
|
|
- schedule_work(&data->waker);
|
|
|
- err = 0;
|
|
|
- goto done;
|
|
|
- }
|
|
|
+ skb->dev = (void *)hdev;
|
|
|
+
|
|
|
+ return urb;
|
|
|
+}
|
|
|
+
|
|
|
+static int submit_tx_urb(struct hci_dev *hdev, struct urb *urb)
|
|
|
+{
|
|
|
+ struct btusb_data *data = hci_get_drvdata(hdev);
|
|
|
+ int err;
|
|
|
|
|
|
-skip_waking:
|
|
|
usb_anchor_urb(urb, &data->tx_anchor);
|
|
|
|
|
|
- err = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
+ err = usb_submit_urb(urb, GFP_KERNEL);
|
|
|
if (err < 0) {
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
BT_ERR("%s urb %p submission failed (%d)",
|
|
|
- hdev->name, urb, -err);
|
|
|
+ hdev->name, urb, -err);
|
|
|
kfree(urb->setup_packet);
|
|
|
usb_unanchor_urb(urb);
|
|
|
} else {
|
|
|
usb_mark_last_busy(data->udev);
|
|
|
}
|
|
|
|
|
|
-done:
|
|
|
usb_free_urb(urb);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int submit_or_queue_tx_urb(struct hci_dev *hdev, struct urb *urb)
|
|
|
+{
|
|
|
+ struct btusb_data *data = hci_get_drvdata(hdev);
|
|
|
+ unsigned long flags;
|
|
|
+ bool suspending;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&data->txlock, flags);
|
|
|
+ suspending = test_bit(BTUSB_SUSPENDING, &data->flags);
|
|
|
+ if (!suspending)
|
|
|
+ data->tx_in_flight++;
|
|
|
+ spin_unlock_irqrestore(&data->txlock, flags);
|
|
|
+
|
|
|
+ if (!suspending)
|
|
|
+ return submit_tx_urb(hdev, urb);
|
|
|
+
|
|
|
+ usb_anchor_urb(urb, &data->deferred);
|
|
|
+ schedule_work(&data->waker);
|
|
|
+
|
|
|
+ usb_free_urb(urb);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct urb *urb;
|
|
|
+
|
|
|
+ BT_DBG("%s", hdev->name);
|
|
|
+
|
|
|
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ switch (bt_cb(skb)->pkt_type) {
|
|
|
+ case HCI_COMMAND_PKT:
|
|
|
+ urb = alloc_ctrl_urb(hdev, skb);
|
|
|
+ if (IS_ERR(urb))
|
|
|
+ return PTR_ERR(urb);
|
|
|
+
|
|
|
+ hdev->stat.cmd_tx++;
|
|
|
+ return submit_or_queue_tx_urb(hdev, urb);
|
|
|
+
|
|
|
+ case HCI_ACLDATA_PKT:
|
|
|
+ urb = alloc_bulk_urb(hdev, skb);
|
|
|
+ if (IS_ERR(urb))
|
|
|
+ return PTR_ERR(urb);
|
|
|
+
|
|
|
+ hdev->stat.acl_tx++;
|
|
|
+ return submit_or_queue_tx_urb(hdev, urb);
|
|
|
+
|
|
|
+ case HCI_SCODATA_PKT:
|
|
|
+ if (hci_conn_num(hdev, SCO_LINK) < 1)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ urb = alloc_isoc_urb(hdev, skb);
|
|
|
+ if (IS_ERR(urb))
|
|
|
+ return PTR_ERR(urb);
|
|
|
+
|
|
|
+ hdev->stat.sco_tx++;
|
|
|
+ return submit_tx_urb(hdev, urb);
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EILSEQ;
|
|
|
+}
|
|
|
+
|
|
|
static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
|
|
|
{
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
|
@@ -940,6 +1172,7 @@ static void btusb_work(struct work_struct *work)
|
|
|
|
|
|
if (hdev->voice_setting & 0x0020) {
|
|
|
static const int alts[3] = { 2, 4, 5 };
|
|
|
+
|
|
|
new_alts = alts[data->sco_num - 1];
|
|
|
} else {
|
|
|
new_alts = data->sco_num;
|
|
@@ -1012,7 +1245,7 @@ static int btusb_setup_csr(struct hci_dev *hdev)
|
|
|
return -PTR_ERR(skb);
|
|
|
}
|
|
|
|
|
|
- rp = (struct hci_rp_read_local_version *) skb->data;
|
|
|
+ rp = (struct hci_rp_read_local_version *)skb->data;
|
|
|
|
|
|
if (!rp->status) {
|
|
|
if (le16_to_cpu(rp->manufacturer) != 10) {
|
|
@@ -1050,7 +1283,7 @@ struct intel_version {
|
|
|
} __packed;
|
|
|
|
|
|
static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
|
|
|
- struct intel_version *ver)
|
|
|
+ struct intel_version *ver)
|
|
|
{
|
|
|
const struct firmware *fw;
|
|
|
char fwname[64];
|
|
@@ -1226,7 +1459,7 @@ static int btusb_check_bdaddr_intel(struct hci_dev *hdev)
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
|
- rp = (struct hci_rp_read_bd_addr *) skb->data;
|
|
|
+ rp = (struct hci_rp_read_bd_addr *)skb->data;
|
|
|
if (rp->status) {
|
|
|
BT_ERR("%s Intel device address result failed (%02x)",
|
|
|
hdev->name, rp->status);
|
|
@@ -1356,6 +1589,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
|
|
|
|
|
|
if (skb->data[0]) {
|
|
|
u8 evt_status = skb->data[0];
|
|
|
+
|
|
|
BT_ERR("%s enable Intel manufacturer mode event failed (%02x)",
|
|
|
hdev->name, evt_status);
|
|
|
kfree_skb(skb);
|
|
@@ -1465,7 +1699,7 @@ static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
|
|
if (IS_ERR(skb)) {
|
|
|
ret = PTR_ERR(skb);
|
|
|
BT_ERR("%s: changing Intel device address failed (%ld)",
|
|
|
- hdev->name, ret);
|
|
|
+ hdev->name, ret);
|
|
|
return ret;
|
|
|
}
|
|
|
kfree_skb(skb);
|
|
@@ -1540,19 +1774,19 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
|
|
if (IS_ERR(skb)) {
|
|
|
ret = PTR_ERR(skb);
|
|
|
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
|
|
|
- hdev->name, ret);
|
|
|
+ hdev->name, ret);
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
if (skb->len != sizeof(*ver)) {
|
|
|
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
|
|
|
- hdev->name);
|
|
|
+ hdev->name);
|
|
|
kfree_skb(skb);
|
|
|
ret = -EIO;
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
- ver = (struct hci_rp_read_local_version *) skb->data;
|
|
|
+ ver = (struct hci_rp_read_local_version *)skb->data;
|
|
|
BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
|
|
|
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
|
|
|
ver->lmp_ver, ver->lmp_subver);
|
|
@@ -1563,7 +1797,7 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
|
|
if (IS_ERR(skb)) {
|
|
|
ret = PTR_ERR(skb);
|
|
|
BT_ERR("%s: BCM: Download Minidrv command failed (%ld)",
|
|
|
- hdev->name, ret);
|
|
|
+ hdev->name, ret);
|
|
|
goto reset_fw;
|
|
|
}
|
|
|
kfree_skb(skb);
|
|
@@ -1575,13 +1809,13 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
|
|
fw_size = fw->size;
|
|
|
|
|
|
while (fw_size >= sizeof(*cmd)) {
|
|
|
- cmd = (struct hci_command_hdr *) fw_ptr;
|
|
|
+ cmd = (struct hci_command_hdr *)fw_ptr;
|
|
|
fw_ptr += sizeof(*cmd);
|
|
|
fw_size -= sizeof(*cmd);
|
|
|
|
|
|
if (fw_size < cmd->plen) {
|
|
|
BT_ERR("%s: BCM: patch %s is corrupted",
|
|
|
- hdev->name, fw_name);
|
|
|
+ hdev->name, fw_name);
|
|
|
ret = -EINVAL;
|
|
|
goto reset_fw;
|
|
|
}
|
|
@@ -1597,7 +1831,7 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
|
|
if (IS_ERR(skb)) {
|
|
|
ret = PTR_ERR(skb);
|
|
|
BT_ERR("%s: BCM: patch command %04x failed (%ld)",
|
|
|
- hdev->name, opcode, ret);
|
|
|
+ hdev->name, opcode, ret);
|
|
|
goto reset_fw;
|
|
|
}
|
|
|
kfree_skb(skb);
|
|
@@ -1622,19 +1856,19 @@ reset_fw:
|
|
|
if (IS_ERR(skb)) {
|
|
|
ret = PTR_ERR(skb);
|
|
|
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
|
|
|
- hdev->name, ret);
|
|
|
+ hdev->name, ret);
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
if (skb->len != sizeof(*ver)) {
|
|
|
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
|
|
|
- hdev->name);
|
|
|
+ hdev->name);
|
|
|
kfree_skb(skb);
|
|
|
ret = -EIO;
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
- ver = (struct hci_rp_read_local_version *) skb->data;
|
|
|
+ ver = (struct hci_rp_read_local_version *)skb->data;
|
|
|
BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
|
|
|
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
|
|
|
ver->lmp_ver, ver->lmp_subver);
|
|
@@ -1646,19 +1880,19 @@ reset_fw:
|
|
|
if (IS_ERR(skb)) {
|
|
|
ret = PTR_ERR(skb);
|
|
|
BT_ERR("%s: HCI_OP_READ_BD_ADDR failed (%ld)",
|
|
|
- hdev->name, ret);
|
|
|
+ hdev->name, ret);
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
if (skb->len != sizeof(*bda)) {
|
|
|
BT_ERR("%s: HCI_OP_READ_BD_ADDR event length mismatch",
|
|
|
- hdev->name);
|
|
|
+ hdev->name);
|
|
|
kfree_skb(skb);
|
|
|
ret = -EIO;
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
- bda = (struct hci_rp_read_bd_addr *) skb->data;
|
|
|
+ bda = (struct hci_rp_read_bd_addr *)skb->data;
|
|
|
if (bda->status) {
|
|
|
BT_ERR("%s: HCI_OP_READ_BD_ADDR error status (%02x)",
|
|
|
hdev->name, bda->status);
|
|
@@ -1693,7 +1927,7 @@ static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
|
|
if (IS_ERR(skb)) {
|
|
|
ret = PTR_ERR(skb);
|
|
|
BT_ERR("%s: BCM: Change address command failed (%ld)",
|
|
|
- hdev->name, ret);
|
|
|
+ hdev->name, ret);
|
|
|
return ret;
|
|
|
}
|
|
|
kfree_skb(skb);
|
|
@@ -1702,7 +1936,7 @@ static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
|
|
}
|
|
|
|
|
|
static int btusb_probe(struct usb_interface *intf,
|
|
|
- const struct usb_device_id *id)
|
|
|
+ const struct usb_device_id *id)
|
|
|
{
|
|
|
struct usb_endpoint_descriptor *ep_desc;
|
|
|
struct btusb_data *data;
|
|
@@ -1717,6 +1951,7 @@ static int btusb_probe(struct usb_interface *intf,
|
|
|
|
|
|
if (!id->driver_info) {
|
|
|
const struct usb_device_id *match;
|
|
|
+
|
|
|
match = usb_match_id(intf, blacklist_table);
|
|
|
if (match)
|
|
|
id = match;
|
|
@@ -1765,17 +2000,16 @@ static int btusb_probe(struct usb_interface *intf,
|
|
|
data->udev = interface_to_usbdev(intf);
|
|
|
data->intf = intf;
|
|
|
|
|
|
- spin_lock_init(&data->lock);
|
|
|
-
|
|
|
INIT_WORK(&data->work, btusb_work);
|
|
|
INIT_WORK(&data->waker, btusb_waker);
|
|
|
+ init_usb_anchor(&data->deferred);
|
|
|
+ init_usb_anchor(&data->tx_anchor);
|
|
|
spin_lock_init(&data->txlock);
|
|
|
|
|
|
- init_usb_anchor(&data->tx_anchor);
|
|
|
init_usb_anchor(&data->intr_anchor);
|
|
|
init_usb_anchor(&data->bulk_anchor);
|
|
|
init_usb_anchor(&data->isoc_anchor);
|
|
|
- init_usb_anchor(&data->deferred);
|
|
|
+ spin_lock_init(&data->rxlock);
|
|
|
|
|
|
hdev = hci_alloc_dev();
|
|
|
if (!hdev)
|
|
@@ -1867,7 +2101,7 @@ static int btusb_probe(struct usb_interface *intf,
|
|
|
|
|
|
if (data->isoc) {
|
|
|
err = usb_driver_claim_interface(&btusb_driver,
|
|
|
- data->isoc, data);
|
|
|
+ data->isoc, data);
|
|
|
if (err < 0) {
|
|
|
hci_free_dev(hdev);
|
|
|
return err;
|
|
@@ -1908,6 +2142,7 @@ static void btusb_disconnect(struct usb_interface *intf)
|
|
|
else if (data->isoc)
|
|
|
usb_driver_release_interface(&btusb_driver, data->isoc);
|
|
|
|
|
|
+ btusb_free_frags(data);
|
|
|
hci_free_dev(hdev);
|
|
|
}
|
|
|
|