summaryrefslogtreecommitdiff
path: root/meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-bluetooth-suspend.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-bluetooth-suspend.patch')
-rw-r--r--meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-bluetooth-suspend.patch465
1 files changed, 465 insertions, 0 deletions
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 <net/bluetooth/bluetooth.h>
+ #include <net/bluetooth/hci_core.h>
+
+-#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;
+ }