|
@@ -153,16 +153,36 @@ static void hif_usb_tx_cb(struct urb *urb)
|
|
|
case -ENODEV:
|
|
|
case -ESHUTDOWN:
|
|
|
/*
|
|
|
- * The URB has been killed, free the SKBs
|
|
|
- * and return.
|
|
|
+ * The URB has been killed, free the SKBs.
|
|
|
*/
|
|
|
ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
|
|
|
- return;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the URBs are being flushed, no need to add this
|
|
|
+ * URB to the free list.
|
|
|
+ */
|
|
|
+ spin_lock(&hif_dev->tx.tx_lock);
|
|
|
+ if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) {
|
|
|
+ spin_unlock(&hif_dev->tx.tx_lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ spin_unlock(&hif_dev->tx.tx_lock);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * In the stop() case, this URB has to be added to
|
|
|
+ * the free list.
|
|
|
+ */
|
|
|
+ goto add_free;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- /* Check if TX has been stopped */
|
|
|
+ /*
|
|
|
+ * Check if TX has been stopped, this is needed because
|
|
|
+ * this CB could have been invoked just after the TX lock
|
|
|
+ * was released in hif_stop() and kill_urb() hasn't been
|
|
|
+ * called yet.
|
|
|
+ */
|
|
|
spin_lock(&hif_dev->tx.tx_lock);
|
|
|
if (hif_dev->tx.flags & HIF_USB_TX_STOP) {
|
|
|
spin_unlock(&hif_dev->tx.tx_lock);
|
|
@@ -314,6 +334,7 @@ static void hif_usb_start(void *hif_handle, u8 pipe_id)
|
|
|
static void hif_usb_stop(void *hif_handle, u8 pipe_id)
|
|
|
{
|
|
|
struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;
|
|
|
+ struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL;
|
|
|
unsigned long flags;
|
|
|
|
|
|
spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
|
|
@@ -321,6 +342,12 @@ static void hif_usb_stop(void *hif_handle, u8 pipe_id)
|
|
|
hif_dev->tx.tx_skb_cnt = 0;
|
|
|
hif_dev->tx.flags |= HIF_USB_TX_STOP;
|
|
|
spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
|
|
|
+
|
|
|
+ /* The pending URBs have to be canceled. */
|
|
|
+ list_for_each_entry_safe(tx_buf, tx_buf_tmp,
|
|
|
+ &hif_dev->tx.tx_pending, list) {
|
|
|
+ usb_kill_urb(tx_buf->urb);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb,
|
|
@@ -587,6 +614,7 @@ free:
|
|
|
static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)
|
|
|
{
|
|
|
struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL;
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
list_for_each_entry_safe(tx_buf, tx_buf_tmp,
|
|
|
&hif_dev->tx.tx_buf, list) {
|
|
@@ -597,6 +625,10 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)
|
|
|
kfree(tx_buf);
|
|
|
}
|
|
|
|
|
|
+ spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
|
|
|
+ hif_dev->tx.flags |= HIF_USB_TX_FLUSH;
|
|
|
+ spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
|
|
|
+
|
|
|
list_for_each_entry_safe(tx_buf, tx_buf_tmp,
|
|
|
&hif_dev->tx.tx_pending, list) {
|
|
|
usb_kill_urb(tx_buf->urb);
|