From 684d263e75a6a7ede638afa60e35a238e24c12ba Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 26 Jan 2010 15:59:18 +0000 Subject: linux-moblin: Add 2.6.31.5 Signed-off-by: Richard Purdie --- .../linux-2.6.31-bluetooth-suspend.patch | 465 +++++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100644 meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-bluetooth-suspend.patch (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-bluetooth-suspend.patch') diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-bluetooth-suspend.patch b/meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-bluetooth-suspend.patch new file mode 100644 index 000000000..786e1f2fc --- /dev/null +++ b/meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-bluetooth-suspend.patch @@ -0,0 +1,465 @@ +diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c +index e70c57e..8e36fc8 100644 +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -35,7 +36,7 @@ + #include + #include + +-#define VERSION "0.5" ++#define VERSION "0.6" + + static int ignore_dga; + static int ignore_csr; +@@ -145,6 +146,7 @@ static struct usb_device_id blacklist_table[] = { + #define BTUSB_INTR_RUNNING 0 + #define BTUSB_BULK_RUNNING 1 + #define BTUSB_ISOC_RUNNING 2 ++#define BTUSB_SUSPENDING 3 + + struct btusb_data { + struct hci_dev *hdev; +@@ -157,11 +159,15 @@ struct btusb_data { + unsigned long flags; + + struct work_struct work; ++ struct work_struct waker; + + struct usb_anchor tx_anchor; + 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; + + struct usb_endpoint_descriptor *intr_ep; + struct usb_endpoint_descriptor *bulk_tx_ep; +@@ -174,8 +180,26 @@ struct btusb_data { + unsigned int sco_num; + int isoc_altsetting; + int suspend_count; ++ int did_iso_resume:1; + }; + ++static int inc_tx(struct btusb_data *data) ++{ ++ unsigned long flags; ++ int rv; ++ ++ spin_lock_irqsave(&data->txlock, flags); ++ rv = test_bit(BTUSB_SUSPENDING, &data->flags); ++ BT_DBG("BTUSB_SUSPENDING bit = %d for intf %p in %s", ++ rv, data->intf, __func__); ++ if (!rv) ++ data->tx_in_flight++; ++ spin_unlock_irqrestore(&data->txlock, flags); ++ ++ return rv; ++} ++ ++ + static void btusb_intr_complete(struct urb *urb) + { + struct hci_dev *hdev = urb->context; +@@ -202,6 +226,7 @@ static void btusb_intr_complete(struct urb *urb) + if (!test_bit(BTUSB_INTR_RUNNING, &data->flags)) + return; + ++ usb_mark_last_busy(data->udev); + usb_anchor_urb(urb, &data->intr_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); +@@ -327,6 +352,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) + + urb->transfer_flags |= URB_FREE_BUFFER; + ++ usb_mark_last_busy(data->udev); + usb_anchor_urb(urb, &data->bulk_anchor); + + err = usb_submit_urb(urb, mem_flags); +@@ -465,6 +491,33 @@ static void btusb_tx_complete(struct urb *urb) + { + struct sk_buff *skb = urb->context; + struct hci_dev *hdev = (struct hci_dev *) skb->dev; ++ struct btusb_data *data = hdev->driver_data; ++ ++ 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; ++ ++ if (!urb->status) ++ hdev->stat.byte_tx += urb->transfer_buffer_length; ++ else ++ hdev->stat.err_tx++; ++ ++done: ++ spin_lock(&data->txlock); ++ data->tx_in_flight--; ++ spin_unlock(&data->txlock); ++ ++ kfree(urb->setup_packet); ++ ++ kfree_skb(skb); ++} ++ ++static void btusb_isoc_tx_complete(struct urb *urb) ++{ ++ struct sk_buff *skb = urb->context; ++ 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); +@@ -490,11 +543,16 @@ static int btusb_open(struct hci_dev *hdev) + + BT_DBG("%s", hdev->name); + ++ err = usb_autopm_get_interface(data->intf); ++ if (err < 0) ++ return err; ++ data->intf->needs_remote_wakeup = 1; ++ + if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) +- return 0; ++ goto out; + + if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) +- return 0; ++ goto out; + + err = btusb_submit_intr_urb(hdev, GFP_KERNEL); + if (err < 0) +@@ -502,6 +560,7 @@ static int btusb_open(struct hci_dev *hdev) + + err = btusb_submit_bulk_urb(hdev, GFP_KERNEL); + if (err < 0) { ++ BT_DBG("kill urbs %s", __func__); + usb_kill_anchored_urbs(&data->intr_anchor); + goto failed; + } +@@ -509,17 +568,28 @@ static int btusb_open(struct hci_dev *hdev) + set_bit(BTUSB_BULK_RUNNING, &data->flags); + btusb_submit_bulk_urb(hdev, GFP_KERNEL); + ++out: ++ usb_autopm_put_interface(data->intf); + return 0; + + failed: + clear_bit(BTUSB_INTR_RUNNING, &data->flags); + clear_bit(HCI_RUNNING, &hdev->flags); ++ usb_autopm_put_interface(data->intf); + return err; + } + ++static void btusb_stop_traffic(struct btusb_data *data) ++{ ++ usb_kill_anchored_urbs(&data->intr_anchor); ++ usb_kill_anchored_urbs(&data->bulk_anchor); ++ usb_kill_anchored_urbs(&data->isoc_anchor); ++} ++ + static int btusb_close(struct hci_dev *hdev) + { + struct btusb_data *data = hdev->driver_data; ++ int err; + + BT_DBG("%s", hdev->name); + +@@ -529,13 +599,16 @@ static int btusb_close(struct hci_dev *hdev) + cancel_work_sync(&data->work); + + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); +- usb_kill_anchored_urbs(&data->isoc_anchor); +- + clear_bit(BTUSB_BULK_RUNNING, &data->flags); +- usb_kill_anchored_urbs(&data->bulk_anchor); +- + clear_bit(BTUSB_INTR_RUNNING, &data->flags); +- usb_kill_anchored_urbs(&data->intr_anchor); ++ ++ BT_DBG("kill urbs %s", __func__); ++ btusb_stop_traffic(data); ++ err = usb_autopm_get_interface(data->intf); ++ if (!err) { ++ data->intf->needs_remote_wakeup = 0; ++ usb_autopm_put_interface(data->intf); ++ } + + return 0; + } +@@ -546,6 +619,7 @@ static int btusb_flush(struct hci_dev *hdev) + + BT_DBG("%s", hdev->name); + ++ BT_DBG("kill urbs %s", __func__); + usb_kill_anchored_urbs(&data->tx_anchor); + + return 0; +@@ -622,7 +696,7 @@ static int btusb_send_frame(struct sk_buff *skb) + urb->dev = data->udev; + urb->pipe = pipe; + urb->context = skb; +- urb->complete = btusb_tx_complete; ++ urb->complete = btusb_isoc_tx_complete; + urb->interval = data->isoc_tx_ep->bInterval; + + urb->transfer_flags = URB_ISO_ASAP; +@@ -633,12 +707,23 @@ static int btusb_send_frame(struct sk_buff *skb) + le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize)); + + hdev->stat.sco_tx++; +- break; ++ goto skip_waking; + + default: + return -EILSEQ; + } + ++ err = inc_tx(data); ++ if (err) { ++ ++ usb_anchor_urb(urb, &data->deferred); ++ schedule_work(&data->waker); ++ err = 0; ++ goto out; ++ } else { ++ ++ } ++skip_waking: + usb_anchor_urb(urb, &data->tx_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); +@@ -646,10 +731,13 @@ static int btusb_send_frame(struct sk_buff *skb) + BT_ERR("%s urb %p submission failed", hdev->name, urb); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); ++ } else { ++ usb_mark_last_busy(data->udev); + } + + usb_free_urb(urb); + ++out: + return err; + } + +@@ -721,10 +809,23 @@ static void btusb_work(struct work_struct *work) + { + struct btusb_data *data = container_of(work, struct btusb_data, work); + struct hci_dev *hdev = data->hdev; ++ int err; + + if (hdev->conn_hash.sco_num > 0) { ++ if (!data->did_iso_resume) { ++ err = usb_autopm_get_interface(data->isoc); ++ if (!err) { ++ data->did_iso_resume = 1; ++ } else { ++ clear_bit(BTUSB_ISOC_RUNNING, &data->flags); ++ BT_DBG("kill urbs %s", __func__); ++ usb_kill_anchored_urbs(&data->isoc_anchor); ++ return; ++ } ++ } + if (data->isoc_altsetting != 2) { + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); ++ BT_DBG("kill urbs %s", __func__); + usb_kill_anchored_urbs(&data->isoc_anchor); + + if (__set_isoc_interface(hdev, 2) < 0) +@@ -739,12 +840,28 @@ static void btusb_work(struct work_struct *work) + } + } else { + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); ++ BT_DBG("kill urbs %s", __func__); + usb_kill_anchored_urbs(&data->isoc_anchor); + + __set_isoc_interface(hdev, 0); ++ if (data->did_iso_resume) { ++ data->did_iso_resume = 0; ++ usb_autopm_put_interface(data->isoc); ++ } + } + } + ++static void btusb_waker(struct work_struct *work) ++{ ++ struct btusb_data *data = container_of(work, struct btusb_data, waker); ++ int err; ++ ++ ++ err = usb_autopm_get_interface(data->intf); ++ if (!err) ++ usb_autopm_put_interface(data->intf); ++} ++ + static int btusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) + { +@@ -814,11 +931,14 @@ static int btusb_probe(struct usb_interface *intf, + spin_lock_init(&data->lock); + + INIT_WORK(&data->work, btusb_work); ++ INIT_WORK(&data->waker, btusb_waker); ++ 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); + + hdev = hci_alloc_dev(); + if (!hdev) { +@@ -949,39 +1069,78 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message) + + BT_DBG("intf %p", intf); + +- if (data->suspend_count++) ++ if (data->suspend_count++) { ++ BT_DBG("data->suspend_count = %d for intf %p, returning from %s", ++ data->suspend_count, intf, __func__); + return 0; ++ } ++ BT_DBG("data->suspend_count = %d for intf %p, continuing %s", ++ data->suspend_count, intf, __func__); ++ ++ spin_lock_irq(&data->txlock); ++ if (!(interface_to_usbdev(intf)->auto_pm && data->tx_in_flight)) { ++ BT_DBG("Setting BTUSB_SUSPENDING bit in %s for intf %p", ++ __func__, intf); ++ set_bit(BTUSB_SUSPENDING, &data->flags); ++ spin_unlock_irq(&data->txlock); ++ } else { ++ spin_unlock_irq(&data->txlock); ++ BT_DBG("%d URBs in flight", data->tx_in_flight); ++ data->suspend_count--; ++ return -EBUSY; ++ } + + cancel_work_sync(&data->work); + ++ BT_DBG("kill urbs %s", __func__); ++ btusb_stop_traffic(data); + usb_kill_anchored_urbs(&data->tx_anchor); + +- usb_kill_anchored_urbs(&data->isoc_anchor); +- usb_kill_anchored_urbs(&data->bulk_anchor); +- usb_kill_anchored_urbs(&data->intr_anchor); +- + return 0; + } + ++static void play_deferred(struct btusb_data *data) ++{ ++ struct urb *urb; ++ int err; ++ ++ while ((urb = usb_get_from_anchor(&data->deferred))) { ++ err = usb_submit_urb(urb, GFP_ATOMIC); ++ if (err < 0) ++ break; ++ else ++ data->tx_in_flight++; ++ ++ } ++ usb_scuttle_anchored_urbs(&data->deferred); ++} ++ + static int btusb_resume(struct usb_interface *intf) + { + struct btusb_data *data = usb_get_intfdata(intf); + struct hci_dev *hdev = data->hdev; +- int err; ++ int err = 0; + + BT_DBG("intf %p", intf); + +- if (--data->suspend_count) ++ if (--data->suspend_count) { ++ BT_DBG("data->suspend_count = %d for intf %p, returning from %s", ++ data->suspend_count, intf, __func__); + return 0; ++ } + +- if (!test_bit(HCI_RUNNING, &hdev->flags)) +- return 0; ++ if (!test_bit(HCI_RUNNING, &hdev->flags)) { ++ BT_DBG("HCI not running, returning from %s", __func__); ++ goto no_io_needed; ++ } + + if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) { + err = btusb_submit_intr_urb(hdev, GFP_NOIO); + if (err < 0) { + clear_bit(BTUSB_INTR_RUNNING, &data->flags); +- return err; ++ BT_DBG("Error (%d) submitting interrupt URB, returning from %s", ++ err, __func__); ++ goto err_out; + } + } + +@@ -989,9 +1148,12 @@ static int btusb_resume(struct usb_interface *intf) + err = btusb_submit_bulk_urb(hdev, GFP_NOIO); + if (err < 0) { + clear_bit(BTUSB_BULK_RUNNING, &data->flags); +- return err; +- } else ++ BT_DBG("Error (%d) submitting bulk URB, returning from %s", ++ err, __func__); ++ goto err_out; ++ } else { + btusb_submit_bulk_urb(hdev, GFP_NOIO); ++ } + } + + if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) { +@@ -1001,7 +1163,24 @@ static int btusb_resume(struct usb_interface *intf) + btusb_submit_isoc_urb(hdev, GFP_NOIO); + } + ++ spin_lock_irq(&data->txlock); ++ play_deferred(data); ++ BT_DBG("Clearing BTUSB_SUSPENDING bit in %s for intf %p", __func__, intf); ++ clear_bit(BTUSB_SUSPENDING, &data->flags); ++ spin_unlock_irq(&data->txlock); ++ schedule_work(&data->work); ++ + return 0; ++ ++err_out: ++ usb_scuttle_anchored_urbs(&data->deferred); ++no_io_needed: ++ spin_lock_irq(&data->txlock); ++ BT_DBG("Clearing BTUSB_SUSPENDING bit in %s for intf %p", __func__, intf); ++ clear_bit(BTUSB_SUSPENDING, &data->flags); ++ spin_unlock_irq(&data->txlock); ++ ++ return err; + } + + static struct usb_driver btusb_driver = { +@@ -1011,6 +1190,7 @@ static struct usb_driver btusb_driver = { + .suspend = btusb_suspend, + .resume = btusb_resume, + .id_table = btusb_table, ++ .supports_autosuspend = 1, + }; + + static int __init btusb_init(void) +diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c +index e70c57e..ac94f91 100644 +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -908,6 +967,7 @@ static int btusb_probe(struct usb_interface *intf, + } + + usb_set_intfdata(intf, data); ++ usb_device_autosuspend_enable(data->udev); + + return 0; + } -- cgit v1.2.3