summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k/hif_usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/hif_usb.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.c148
1 files changed, 86 insertions, 62 deletions
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index fe994e229898..74872ca76f9a 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -93,14 +93,24 @@ static int hif_usb_send_regout(struct hif_device_usb *hif_dev,
return ret;
}
+static inline void ath9k_skb_queue_purge(struct hif_device_usb *hif_dev,
+ struct sk_buff_head *list)
+{
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(list)) != NULL) {
+ dev_kfree_skb_any(skb);
+ TX_STAT_INC(skb_dropped);
+ }
+}
+
static void hif_usb_tx_cb(struct urb *urb)
{
struct tx_buf *tx_buf = (struct tx_buf *) urb->context;
struct hif_device_usb *hif_dev = tx_buf->hif_dev;
struct sk_buff *skb;
- bool drop, flush;
- if (!hif_dev)
+ if (!hif_dev || !tx_buf)
return;
switch (urb->status) {
@@ -108,52 +118,47 @@ static void hif_usb_tx_cb(struct urb *urb)
break;
case -ENOENT:
case -ECONNRESET:
- break;
case -ENODEV:
case -ESHUTDOWN:
+ /*
+ * The URB has been killed, free the SKBs
+ * and return.
+ */
+ ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
return;
default:
break;
}
- if (tx_buf) {
- spin_lock(&hif_dev->tx.tx_lock);
- drop = !!(hif_dev->tx.flags & HIF_USB_TX_STOP);
- flush = !!(hif_dev->tx.flags & HIF_USB_TX_FLUSH);
- spin_unlock(&hif_dev->tx.tx_lock);
-
- while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) {
- if (!drop && !flush) {
- ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
- skb, 1);
- TX_STAT_INC(skb_completed);
- } else {
- dev_kfree_skb_any(skb);
- }
- }
-
- if (flush)
- return;
-
- tx_buf->len = tx_buf->offset = 0;
- __skb_queue_head_init(&tx_buf->skb_queue);
-
- spin_lock(&hif_dev->tx.tx_lock);
- list_del(&tx_buf->list);
- list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf);
- hif_dev->tx.tx_buf_cnt++;
- if (!drop)
- __hif_usb_tx(hif_dev); /* Check for pending SKBs */
- TX_STAT_INC(buf_completed);
+ /* Check if TX has been stopped */
+ spin_lock(&hif_dev->tx.tx_lock);
+ if (hif_dev->tx.flags & HIF_USB_TX_STOP) {
spin_unlock(&hif_dev->tx.tx_lock);
- }
-}
-
-static inline void ath9k_skb_queue_purge(struct sk_buff_head *list)
-{
- struct sk_buff *skb;
- while ((skb = __skb_dequeue(list)) != NULL)
- dev_kfree_skb_any(skb);
+ ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
+ goto add_free;
+ }
+ spin_unlock(&hif_dev->tx.tx_lock);
+
+ /* Complete the queued SKBs. */
+ while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) {
+ ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
+ skb, 1);
+ TX_STAT_INC(skb_completed);
+ }
+
+add_free:
+ /* Re-initialize the SKB queue */
+ tx_buf->len = tx_buf->offset = 0;
+ __skb_queue_head_init(&tx_buf->skb_queue);
+
+ /* Add this TX buffer to the free list */
+ spin_lock(&hif_dev->tx.tx_lock);
+ list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf);
+ hif_dev->tx.tx_buf_cnt++;
+ if (!(hif_dev->tx.flags & HIF_USB_TX_STOP))
+ __hif_usb_tx(hif_dev); /* Check for pending SKBs */
+ TX_STAT_INC(buf_completed);
+ spin_unlock(&hif_dev->tx.tx_lock);
}
/* TX lock has to be taken */
@@ -173,8 +178,7 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
return 0;
tx_buf = list_first_entry(&hif_dev->tx.tx_buf, struct tx_buf, list);
- list_del(&tx_buf->list);
- list_add_tail(&tx_buf->list, &hif_dev->tx.tx_pending);
+ list_move_tail(&tx_buf->list, &hif_dev->tx.tx_pending);
hif_dev->tx.tx_buf_cnt--;
tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM);
@@ -214,7 +218,7 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
ret = usb_submit_urb(tx_buf->urb, GFP_ATOMIC);
if (ret) {
tx_buf->len = tx_buf->offset = 0;
- ath9k_skb_queue_purge(&tx_buf->skb_queue);
+ ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
__skb_queue_head_init(&tx_buf->skb_queue);
list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf);
hif_dev->tx.tx_buf_cnt++;
@@ -281,7 +285,7 @@ static void hif_usb_stop(void *hif_handle, u8 pipe_id)
unsigned long flags;
spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
- ath9k_skb_queue_purge(&hif_dev->tx.tx_skb_queue);
+ ath9k_skb_queue_purge(hif_dev, &hif_dev->tx.tx_skb_queue);
hif_dev->tx.tx_skb_cnt = 0;
hif_dev->tx.flags |= HIF_USB_TX_STOP;
spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
@@ -506,9 +510,18 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)
if (likely(urb->actual_length != 0)) {
skb_put(skb, urb->actual_length);
+ /* Process the command first */
+ ath9k_htc_rx_msg(hif_dev->htc_handle, skb,
+ skb->len, USB_REG_IN_PIPE);
+
+
nskb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_ATOMIC);
- if (!nskb)
- goto resubmit;
+ if (!nskb) {
+ dev_err(&hif_dev->udev->dev,
+ "ath9k_htc: REG_IN memory allocation failure\n");
+ urb->context = NULL;
+ return;
+ }
usb_fill_int_urb(urb, hif_dev->udev,
usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE),
@@ -518,12 +531,9 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret) {
kfree_skb(nskb);
- goto free;
+ urb->context = NULL;
}
- ath9k_htc_rx_msg(hif_dev->htc_handle, skb,
- skb->len, USB_REG_IN_PIPE);
-
return;
}
@@ -543,20 +553,17 @@ free:
static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)
{
- unsigned long flags;
struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL;
- list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_buf, list) {
+ list_for_each_entry_safe(tx_buf, tx_buf_tmp,
+ &hif_dev->tx.tx_buf, list) {
+ usb_kill_urb(tx_buf->urb);
list_del(&tx_buf->list);
usb_free_urb(tx_buf->urb);
kfree(tx_buf->buf);
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);
@@ -565,10 +572,6 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)
kfree(tx_buf->buf);
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);
}
static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev)
@@ -892,6 +895,26 @@ err_alloc:
return ret;
}
+static void ath9k_hif_usb_reboot(struct usb_device *udev)
+{
+ u32 reboot_cmd = 0xffffffff;
+ void *buf;
+ int ret;
+
+ buf = kmalloc(4, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ memcpy(buf, &reboot_cmd, 4);
+
+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, USB_REG_OUT_PIPE),
+ buf, 4, NULL, HZ);
+ if (ret)
+ dev_err(&udev->dev, "ath9k_htc: USB reboot failed\n");
+
+ kfree(buf);
+}
+
static void ath9k_hif_usb_disconnect(struct usb_interface *interface)
{
struct usb_device *udev = interface_to_usbdev(interface);
@@ -899,14 +922,15 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface)
(struct hif_device_usb *) usb_get_intfdata(interface);
if (hif_dev) {
- ath9k_htc_hw_deinit(hif_dev->htc_handle, true);
+ ath9k_htc_hw_deinit(hif_dev->htc_handle,
+ (udev->state == USB_STATE_NOTATTACHED) ? true : false);
ath9k_htc_hw_free(hif_dev->htc_handle);
ath9k_hif_usb_dev_deinit(hif_dev);
usb_set_intfdata(interface, NULL);
}
if (hif_dev->flags & HIF_USB_START)
- usb_reset_device(udev);
+ ath9k_hif_usb_reboot(udev);
kfree(hif_dev);
dev_info(&udev->dev, "ath9k_htc: USB layer deinitialized\n");
OpenPOWER on IntegriCloud