#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;
 #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;
        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;
        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);
+       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;
        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);
 
        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);
 }
 
 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", 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 done;
 
        if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
-               return 0;
+               goto done;
 
        err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
        if (err < 0)
        set_bit(BTUSB_BULK_RUNNING, &data->flags);
        btusb_submit_bulk_urb(hdev, GFP_KERNEL);
 
+done:
+       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);
 
        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);
+
+       btusb_stop_traffic(data);
+       err = usb_autopm_get_interface(data->intf);
+       if (err < 0)
+               return 0;
+
+       data->intf->needs_remote_wakeup = 0;
+       usb_autopm_put_interface(data->intf);
 
        return 0;
 }
                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;
                                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 done;
+       }
+
+skip_waking:
        usb_anchor_urb(urb, &data->tx_anchor);
 
        err = usb_submit_urb(urb, GFP_ATOMIC);
                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);
 
+done:
        return err;
 }
 
 {
        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 < 0) {
+                               clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+                               usb_kill_anchored_urbs(&data->isoc_anchor);
+                               return;
+                       }
+
+                       data->did_iso_resume = 1;
+               }
                if (data->isoc_altsetting != 2) {
                        clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
                        usb_kill_anchored_urbs(&data->isoc_anchor);
                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 < 0)
+               return;
+
+       usb_autopm_put_interface(data->intf);
+}
+
 static int btusb_probe(struct usb_interface *intf,
                                const struct usb_device_id *id)
 {
        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) {
        hci_free_dev(hdev);
 }
 
+#ifdef CONFIG_PM
 static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct btusb_data *data = usb_get_intfdata(intf);
        if (data->suspend_count++)
                return 0;
 
+       spin_lock_irq(&data->txlock);
+       if (!(interface_to_usbdev(intf)->auto_pm && data->tx_in_flight)) {
+               set_bit(BTUSB_SUSPENDING, &data->flags);
+               spin_unlock_irq(&data->txlock);
+       } else {
+               spin_unlock_irq(&data->txlock);
+               data->suspend_count--;
+               return -EBUSY;
+       }
+
        cancel_work_sync(&data->work);
 
+       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;
+
+               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);
 
                return 0;
 
        if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
+               goto done;
 
        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;
+                       goto failed;
                }
        }
 
                err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
                if (err < 0) {
                        clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-                       return err;
-               } else
-                       btusb_submit_bulk_urb(hdev, GFP_NOIO);
+                       goto failed;
+               }
+
+               btusb_submit_bulk_urb(hdev, GFP_NOIO);
        }
 
        if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
                        btusb_submit_isoc_urb(hdev, GFP_NOIO);
        }
 
+       spin_lock_irq(&data->txlock);
+       play_deferred(data);
+       clear_bit(BTUSB_SUSPENDING, &data->flags);
+       spin_unlock_irq(&data->txlock);
+       schedule_work(&data->work);
+
        return 0;
+
+failed:
+       usb_scuttle_anchored_urbs(&data->deferred);
+done:
+       spin_lock_irq(&data->txlock);
+       clear_bit(BTUSB_SUSPENDING, &data->flags);
+       spin_unlock_irq(&data->txlock);
+
+       return err;
 }
+#endif
 
 static struct usb_driver btusb_driver = {
        .name           = "btusb",
        .probe          = btusb_probe,
        .disconnect     = btusb_disconnect,
+#ifdef CONFIG_PM
        .suspend        = btusb_suspend,
        .resume         = btusb_resume,
+#endif
        .id_table       = btusb_table,
+       .supports_autosuspend = 1,
 };
 
 static int __init btusb_init(void)