diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/hif_usb.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hif_usb.c | 46 |
1 files changed, 39 insertions, 7 deletions
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 22b68b3c8566..5ab3084eb9cb 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c | |||
@@ -153,16 +153,36 @@ static void hif_usb_tx_cb(struct urb *urb) | |||
153 | case -ENODEV: | 153 | case -ENODEV: |
154 | case -ESHUTDOWN: | 154 | case -ESHUTDOWN: |
155 | /* | 155 | /* |
156 | * The URB has been killed, free the SKBs | 156 | * The URB has been killed, free the SKBs. |
157 | * and return. | ||
158 | */ | 157 | */ |
159 | ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue); | 158 | ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue); |
160 | return; | 159 | |
160 | /* | ||
161 | * If the URBs are being flushed, no need to add this | ||
162 | * URB to the free list. | ||
163 | */ | ||
164 | spin_lock(&hif_dev->tx.tx_lock); | ||
165 | if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) { | ||
166 | spin_unlock(&hif_dev->tx.tx_lock); | ||
167 | return; | ||
168 | } | ||
169 | spin_unlock(&hif_dev->tx.tx_lock); | ||
170 | |||
171 | /* | ||
172 | * In the stop() case, this URB has to be added to | ||
173 | * the free list. | ||
174 | */ | ||
175 | goto add_free; | ||
161 | default: | 176 | default: |
162 | break; | 177 | break; |
163 | } | 178 | } |
164 | 179 | ||
165 | /* Check if TX has been stopped */ | 180 | /* |
181 | * Check if TX has been stopped, this is needed because | ||
182 | * this CB could have been invoked just after the TX lock | ||
183 | * was released in hif_stop() and kill_urb() hasn't been | ||
184 | * called yet. | ||
185 | */ | ||
166 | spin_lock(&hif_dev->tx.tx_lock); | 186 | spin_lock(&hif_dev->tx.tx_lock); |
167 | if (hif_dev->tx.flags & HIF_USB_TX_STOP) { | 187 | if (hif_dev->tx.flags & HIF_USB_TX_STOP) { |
168 | spin_unlock(&hif_dev->tx.tx_lock); | 188 | spin_unlock(&hif_dev->tx.tx_lock); |
@@ -314,6 +334,7 @@ static void hif_usb_start(void *hif_handle, u8 pipe_id) | |||
314 | static void hif_usb_stop(void *hif_handle, u8 pipe_id) | 334 | static void hif_usb_stop(void *hif_handle, u8 pipe_id) |
315 | { | 335 | { |
316 | struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; | 336 | struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; |
337 | struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; | ||
317 | unsigned long flags; | 338 | unsigned long flags; |
318 | 339 | ||
319 | spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); | 340 | spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); |
@@ -321,6 +342,12 @@ static void hif_usb_stop(void *hif_handle, u8 pipe_id) | |||
321 | hif_dev->tx.tx_skb_cnt = 0; | 342 | hif_dev->tx.tx_skb_cnt = 0; |
322 | hif_dev->tx.flags |= HIF_USB_TX_STOP; | 343 | hif_dev->tx.flags |= HIF_USB_TX_STOP; |
323 | spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); | 344 | spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); |
345 | |||
346 | /* The pending URBs have to be canceled. */ | ||
347 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, | ||
348 | &hif_dev->tx.tx_pending, list) { | ||
349 | usb_kill_urb(tx_buf->urb); | ||
350 | } | ||
324 | } | 351 | } |
325 | 352 | ||
326 | static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb, | 353 | static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb, |
@@ -587,6 +614,7 @@ free: | |||
587 | static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) | 614 | static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) |
588 | { | 615 | { |
589 | struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; | 616 | struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; |
617 | unsigned long flags; | ||
590 | 618 | ||
591 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, | 619 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, |
592 | &hif_dev->tx.tx_buf, list) { | 620 | &hif_dev->tx.tx_buf, list) { |
@@ -597,6 +625,10 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) | |||
597 | kfree(tx_buf); | 625 | kfree(tx_buf); |
598 | } | 626 | } |
599 | 627 | ||
628 | spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); | ||
629 | hif_dev->tx.flags |= HIF_USB_TX_FLUSH; | ||
630 | spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); | ||
631 | |||
600 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, | 632 | list_for_each_entry_safe(tx_buf, tx_buf_tmp, |
601 | &hif_dev->tx.tx_pending, list) { | 633 | &hif_dev->tx.tx_pending, list) { |
602 | usb_kill_urb(tx_buf->urb); | 634 | usb_kill_urb(tx_buf->urb); |
@@ -993,16 +1025,16 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface) | |||
993 | { | 1025 | { |
994 | struct usb_device *udev = interface_to_usbdev(interface); | 1026 | struct usb_device *udev = interface_to_usbdev(interface); |
995 | struct hif_device_usb *hif_dev = usb_get_intfdata(interface); | 1027 | struct hif_device_usb *hif_dev = usb_get_intfdata(interface); |
1028 | bool unplugged = (udev->state == USB_STATE_NOTATTACHED) ? true : false; | ||
996 | 1029 | ||
997 | if (hif_dev) { | 1030 | if (hif_dev) { |
998 | ath9k_htc_hw_deinit(hif_dev->htc_handle, | 1031 | ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); |
999 | (udev->state == USB_STATE_NOTATTACHED) ? true : false); | ||
1000 | ath9k_htc_hw_free(hif_dev->htc_handle); | 1032 | ath9k_htc_hw_free(hif_dev->htc_handle); |
1001 | ath9k_hif_usb_dev_deinit(hif_dev); | 1033 | ath9k_hif_usb_dev_deinit(hif_dev); |
1002 | usb_set_intfdata(interface, NULL); | 1034 | usb_set_intfdata(interface, NULL); |
1003 | } | 1035 | } |
1004 | 1036 | ||
1005 | if (hif_dev->flags & HIF_USB_START) | 1037 | if (!unplugged && (hif_dev->flags & HIF_USB_START)) |
1006 | ath9k_hif_usb_reboot(udev); | 1038 | ath9k_hif_usb_reboot(udev); |
1007 | 1039 | ||
1008 | kfree(hif_dev); | 1040 | kfree(hif_dev); |