]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/wireless/ath/ath9k/hif_usb.c
ath9k: avoid using trinary operator w/ TX_STAT_INC
[mv-sheeva.git] / drivers / net / wireless / ath / ath9k / hif_usb.c
index f1b8af64569cbdeea7d80316d071aea329eab494..48bcc1a21076b03c4fb2899e859a4a7f4de0aa8d 100644 (file)
 #include "htc.h"
 
 /* identify firmware images */
-#define FIRMWARE_AR7010                "ar7010.fw"
-#define FIRMWARE_AR7010_1_1    "ar7010_1_1.fw"
-#define FIRMWARE_AR9271                "ar9271.fw"
+#define FIRMWARE_AR7010_1_1     "htc_7010.fw"
+#define FIRMWARE_AR9271         "htc_9271.fw"
 
-MODULE_FIRMWARE(FIRMWARE_AR7010);
 MODULE_FIRMWARE(FIRMWARE_AR7010_1_1);
 MODULE_FIRMWARE(FIRMWARE_AR9271);
 
@@ -80,7 +78,7 @@ static void hif_usb_regout_cb(struct urb *urb)
 
        if (cmd) {
                ath9k_htc_txcompletion_cb(cmd->hif_dev->htc_handle,
-                                         cmd->skb, 1);
+                                         cmd->skb, true);
                kfree(cmd);
        }
 
@@ -126,6 +124,90 @@ static int hif_usb_send_regout(struct hif_device_usb *hif_dev,
        return ret;
 }
 
+static void hif_usb_mgmt_cb(struct urb *urb)
+{
+       struct cmd_buf *cmd = (struct cmd_buf *)urb->context;
+       struct hif_device_usb *hif_dev = cmd->hif_dev;
+       bool txok = true;
+
+       if (!cmd || !cmd->skb || !cmd->hif_dev)
+               return;
+
+       switch (urb->status) {
+       case 0:
+               break;
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ENODEV:
+       case -ESHUTDOWN:
+               txok = false;
+
+               /*
+                * If the URBs are being flushed, no need to complete
+                * this packet.
+                */
+               spin_lock(&hif_dev->tx.tx_lock);
+               if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) {
+                       spin_unlock(&hif_dev->tx.tx_lock);
+                       dev_kfree_skb_any(cmd->skb);
+                       kfree(cmd);
+                       return;
+               }
+               spin_unlock(&hif_dev->tx.tx_lock);
+
+               break;
+       default:
+               txok = false;
+               break;
+       }
+
+       skb_pull(cmd->skb, 4);
+       ath9k_htc_txcompletion_cb(cmd->hif_dev->htc_handle,
+                                 cmd->skb, txok);
+       kfree(cmd);
+}
+
+static int hif_usb_send_mgmt(struct hif_device_usb *hif_dev,
+                            struct sk_buff *skb)
+{
+       struct urb *urb;
+       struct cmd_buf *cmd;
+       int ret = 0;
+       __le16 *hdr;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (urb == NULL)
+               return -ENOMEM;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+       if (cmd == NULL) {
+               usb_free_urb(urb);
+               return -ENOMEM;
+       }
+
+       cmd->skb = skb;
+       cmd->hif_dev = hif_dev;
+
+       hdr = (__le16 *) skb_push(skb, 4);
+       *hdr++ = cpu_to_le16(skb->len - 4);
+       *hdr++ = cpu_to_le16(ATH_USB_TX_STREAM_MODE_TAG);
+
+       usb_fill_bulk_urb(urb, hif_dev->udev,
+                        usb_sndbulkpipe(hif_dev->udev, USB_WLAN_TX_PIPE),
+                        skb->data, skb->len,
+                        hif_usb_mgmt_cb, cmd);
+
+       usb_anchor_urb(urb, &hif_dev->mgmt_submitted);
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret) {
+               usb_unanchor_urb(urb);
+               kfree(cmd);
+       }
+       usb_free_urb(urb);
+
+       return ret;
+}
+
 static inline void ath9k_skb_queue_purge(struct hif_device_usb *hif_dev,
                                         struct sk_buff_head *list)
 {
@@ -133,7 +215,22 @@ static inline void ath9k_skb_queue_purge(struct hif_device_usb *hif_dev,
 
        while ((skb = __skb_dequeue(list)) != NULL) {
                dev_kfree_skb_any(skb);
-               TX_STAT_INC(skb_dropped);
+       }
+}
+
+static inline void ath9k_skb_queue_complete(struct hif_device_usb *hif_dev,
+                                           struct sk_buff_head *queue,
+                                           bool txok)
+{
+       struct sk_buff *skb;
+
+       while ((skb = __skb_dequeue(queue)) != NULL) {
+               ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
+                                         skb, txok);
+               if (txok)
+                       TX_STAT_INC(skb_success);
+               else
+                       TX_STAT_INC(skb_failed);
        }
 }
 
@@ -141,7 +238,7 @@ 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;
-       struct sk_buff *skb;
+       bool txok = true;
 
        if (!tx_buf || !tx_buf->hif_dev)
                return;
@@ -155,10 +252,7 @@ static void hif_usb_tx_cb(struct urb *urb)
        case -ECONNRESET:
        case -ENODEV:
        case -ESHUTDOWN:
-               /*
-                * The URB has been killed, free the SKBs.
-                */
-               ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
+               txok = false;
 
                /*
                 * If the URBs are being flushed, no need to add this
@@ -167,41 +261,19 @@ static void hif_usb_tx_cb(struct urb *urb)
                spin_lock(&hif_dev->tx.tx_lock);
                if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) {
                        spin_unlock(&hif_dev->tx.tx_lock);
+                       ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
                        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;
+               break;
        default:
+               txok = false;
                break;
        }
 
-       /*
-        * 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);
-               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);
-       }
+       ath9k_skb_queue_complete(hif_dev, &tx_buf->skb_queue, txok);
 
-add_free:
        /* Re-initialize the SKB queue */
        tx_buf->len = tx_buf->offset = 0;
        __skb_queue_head_init(&tx_buf->skb_queue);
@@ -274,7 +346,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(hif_dev, &tx_buf->skb_queue);
+               ath9k_skb_queue_complete(hif_dev, &tx_buf->skb_queue, false);
                __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++;
@@ -286,10 +358,11 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
        return ret;
 }
 
-static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb,
-                          struct ath9k_htc_tx_ctl *tx_ctl)
+static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb)
 {
+       struct ath9k_htc_tx_ctl *tx_ctl;
        unsigned long flags;
+       int ret = 0;
 
        spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
 
@@ -304,26 +377,36 @@ static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb,
                return -ENOMEM;
        }
 
-       __skb_queue_tail(&hif_dev->tx.tx_skb_queue, skb);
-       hif_dev->tx.tx_skb_cnt++;
+       spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
 
-       /* Send normal frames immediately */
-       if (!tx_ctl || (tx_ctl && (tx_ctl->type == ATH9K_HTC_NORMAL)))
-               __hif_usb_tx(hif_dev);
+       tx_ctl = HTC_SKB_CB(skb);
+
+       /* Mgmt/Beacon frames don't use the TX buffer pool */
+       if ((tx_ctl->type == ATH9K_HTC_MGMT) ||
+           (tx_ctl->type == ATH9K_HTC_BEACON)) {
+               ret = hif_usb_send_mgmt(hif_dev, skb);
+       }
+
+       spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
+
+       if ((tx_ctl->type == ATH9K_HTC_NORMAL) ||
+           (tx_ctl->type == ATH9K_HTC_AMPDU)) {
+               __skb_queue_tail(&hif_dev->tx.tx_skb_queue, skb);
+               hif_dev->tx.tx_skb_cnt++;
+       }
 
        /* Check if AMPDUs have to be sent immediately */
-       if (tx_ctl && (tx_ctl->type == ATH9K_HTC_AMPDU) &&
-           (hif_dev->tx.tx_buf_cnt == MAX_TX_URB_NUM) &&
+       if ((hif_dev->tx.tx_buf_cnt == MAX_TX_URB_NUM) &&
            (hif_dev->tx.tx_skb_cnt < 2)) {
                __hif_usb_tx(hif_dev);
        }
 
        spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
 
-       return 0;
+       return ret;
 }
 
-static void hif_usb_start(void *hif_handle, u8 pipe_id)
+static void hif_usb_start(void *hif_handle)
 {
        struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;
        unsigned long flags;
@@ -335,14 +418,14 @@ static void hif_usb_start(void *hif_handle, u8 pipe_id)
        spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
 }
 
-static void hif_usb_stop(void *hif_handle, u8 pipe_id)
+static void hif_usb_stop(void *hif_handle)
 {
        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);
-       ath9k_skb_queue_purge(hif_dev, &hif_dev->tx.tx_skb_queue);
+       ath9k_skb_queue_complete(hif_dev, &hif_dev->tx.tx_skb_queue, false);
        hif_dev->tx.tx_skb_cnt = 0;
        hif_dev->tx.flags |= HIF_USB_TX_STOP;
        spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
@@ -352,17 +435,18 @@ static void hif_usb_stop(void *hif_handle, u8 pipe_id)
                                 &hif_dev->tx.tx_pending, list) {
                usb_kill_urb(tx_buf->urb);
        }
+
+       usb_kill_anchored_urbs(&hif_dev->mgmt_submitted);
 }
 
-static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb,
-                       struct ath9k_htc_tx_ctl *tx_ctl)
+static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb)
 {
        struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;
        int ret = 0;
 
        switch (pipe_id) {
        case USB_WLAN_TX_PIPE:
-               ret = hif_usb_send_tx(hif_dev, skb, tx_ctl);
+               ret = hif_usb_send_tx(hif_dev, skb);
                break;
        case USB_REG_OUT_PIPE:
                ret = hif_usb_send_regout(hif_dev, skb);
@@ -377,6 +461,40 @@ static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb,
        return ret;
 }
 
+static inline bool check_index(struct sk_buff *skb, u8 idx)
+{
+       struct ath9k_htc_tx_ctl *tx_ctl;
+
+       tx_ctl = HTC_SKB_CB(skb);
+
+       if ((tx_ctl->type == ATH9K_HTC_AMPDU) &&
+           (tx_ctl->sta_idx == idx))
+               return true;
+
+       return false;
+}
+
+static void hif_usb_sta_drain(void *hif_handle, u8 idx)
+{
+       struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;
+       struct sk_buff *skb, *tmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
+
+       skb_queue_walk_safe(&hif_dev->tx.tx_skb_queue, skb, tmp) {
+               if (check_index(skb, idx)) {
+                       __skb_unlink(skb, &hif_dev->tx.tx_skb_queue);
+                       ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
+                                                 skb, false);
+                       hif_dev->tx.tx_skb_cnt--;
+                       TX_STAT_INC(skb_failed);
+               }
+       }
+
+       spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
+}
+
 static struct ath9k_htc_hif hif_usb = {
        .transport = ATH9K_HIF_USB,
        .name = "ath9k_hif_usb",
@@ -386,6 +504,7 @@ static struct ath9k_htc_hif hif_usb = {
 
        .start = hif_usb_start,
        .stop = hif_usb_stop,
+       .sta_drain = hif_usb_sta_drain,
        .send = hif_usb_send,
 };
 
@@ -567,6 +686,9 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)
        case -ESHUTDOWN:
                goto free;
        default:
+               skb_reset_tail_pointer(skb);
+               skb_trim(skb, 0);
+
                goto resubmit;
        }
 
@@ -591,23 +713,15 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)
                                                 USB_REG_IN_PIPE),
                                 nskb->data, MAX_REG_IN_BUF_SIZE,
                                 ath9k_hif_usb_reg_in_cb, nskb);
-
-               ret = usb_submit_urb(urb, GFP_ATOMIC);
-               if (ret) {
-                       kfree_skb(nskb);
-                       urb->context = NULL;
-               }
-
-               return;
        }
 
 resubmit:
-       skb_reset_tail_pointer(skb);
-       skb_trim(skb, 0);
-
+       usb_anchor_urb(urb, &hif_dev->reg_in_submitted);
        ret = usb_submit_urb(urb, GFP_ATOMIC);
-       if (ret)
+       if (ret) {
+               usb_unanchor_urb(urb);
                goto free;
+       }
 
        return;
 free:
@@ -641,6 +755,8 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)
                kfree(tx_buf->buf);
                kfree(tx_buf);
        }
+
+       usb_kill_anchored_urbs(&hif_dev->mgmt_submitted);
 }
 
 static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev)
@@ -652,6 +768,7 @@ static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev)
        INIT_LIST_HEAD(&hif_dev->tx.tx_pending);
        spin_lock_init(&hif_dev->tx.tx_lock);
        __skb_queue_head_init(&hif_dev->tx.tx_skb_queue);
+       init_usb_anchor(&hif_dev->mgmt_submitted);
 
        for (i = 0; i < MAX_TX_URB_NUM; i++) {
                tx_buf = kzalloc(sizeof(struct tx_buf), GFP_KERNEL);
@@ -748,43 +865,67 @@ err_urb:
        return ret;
 }
 
-static void ath9k_hif_usb_dealloc_reg_in_urb(struct hif_device_usb *hif_dev)
+static void ath9k_hif_usb_dealloc_reg_in_urbs(struct hif_device_usb *hif_dev)
 {
-       if (hif_dev->reg_in_urb) {
-               usb_kill_urb(hif_dev->reg_in_urb);
-               if (hif_dev->reg_in_urb->context)
-                       kfree_skb((void *)hif_dev->reg_in_urb->context);
-               usb_free_urb(hif_dev->reg_in_urb);
-               hif_dev->reg_in_urb = NULL;
-       }
+       usb_kill_anchored_urbs(&hif_dev->reg_in_submitted);
 }
 
-static int ath9k_hif_usb_alloc_reg_in_urb(struct hif_device_usb *hif_dev)
+static int ath9k_hif_usb_alloc_reg_in_urbs(struct hif_device_usb *hif_dev)
 {
-       struct sk_buff *skb;
+       struct urb *urb = NULL;
+       struct sk_buff *skb = NULL;
+       int i, ret;
 
-       hif_dev->reg_in_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (hif_dev->reg_in_urb == NULL)
-               return -ENOMEM;
+       init_usb_anchor(&hif_dev->reg_in_submitted);
 
-       skb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_KERNEL);
-       if (!skb)
-               goto err;
+       for (i = 0; i < MAX_REG_IN_URB_NUM; i++) {
 
-       usb_fill_bulk_urb(hif_dev->reg_in_urb, hif_dev->udev,
-                        usb_rcvbulkpipe(hif_dev->udev,
-                                        USB_REG_IN_PIPE),
-                        skb->data, MAX_REG_IN_BUF_SIZE,
-                        ath9k_hif_usb_reg_in_cb, skb);
+               /* Allocate URB */
+               urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (urb == NULL) {
+                       ret = -ENOMEM;
+                       goto err_urb;
+               }
 
-       if (usb_submit_urb(hif_dev->reg_in_urb, GFP_KERNEL) != 0)
-               goto err;
+               /* Allocate buffer */
+               skb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_KERNEL);
+               if (!skb) {
+                       ret = -ENOMEM;
+                       goto err_skb;
+               }
+
+               usb_fill_bulk_urb(urb, hif_dev->udev,
+                                 usb_rcvbulkpipe(hif_dev->udev,
+                                                 USB_REG_IN_PIPE),
+                                 skb->data, MAX_REG_IN_BUF_SIZE,
+                                 ath9k_hif_usb_reg_in_cb, skb);
+
+               /* Anchor URB */
+               usb_anchor_urb(urb, &hif_dev->reg_in_submitted);
+
+               /* Submit URB */
+               ret = usb_submit_urb(urb, GFP_KERNEL);
+               if (ret) {
+                       usb_unanchor_urb(urb);
+                       goto err_submit;
+               }
+
+               /*
+                * Drop reference count.
+                * This ensures that the URB is freed when killing them.
+                */
+               usb_free_urb(urb);
+       }
 
        return 0;
 
-err:
-       ath9k_hif_usb_dealloc_reg_in_urb(hif_dev);
-       return -ENOMEM;
+err_submit:
+       kfree_skb(skb);
+err_skb:
+       usb_free_urb(urb);
+err_urb:
+       ath9k_hif_usb_dealloc_reg_in_urbs(hif_dev);
+       return ret;
 }
 
 static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev)
@@ -801,7 +942,7 @@ static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev)
                goto err_rx;
 
        /* Register Read */
-       if (ath9k_hif_usb_alloc_reg_in_urb(hif_dev) < 0)
+       if (ath9k_hif_usb_alloc_reg_in_urbs(hif_dev) < 0)
                goto err_reg;
 
        return 0;
@@ -816,7 +957,7 @@ err:
 static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev)
 {
        usb_kill_anchored_urbs(&hif_dev->regout_submitted);
-       ath9k_hif_usb_dealloc_reg_in_urb(hif_dev);
+       ath9k_hif_usb_dealloc_reg_in_urbs(hif_dev);
        ath9k_hif_usb_dealloc_tx_urbs(hif_dev);
        ath9k_hif_usb_dealloc_rx_urbs(hif_dev);
 }
@@ -1026,10 +1167,7 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface,
        /* Find out which firmware to load */
 
        if (IS_AR7010_DEVICE(id->driver_info))
-               if (le16_to_cpu(udev->descriptor.bcdDevice) == 0x0202)
-                       hif_dev->fw_name = FIRMWARE_AR7010_1_1;
-               else
-                       hif_dev->fw_name = FIRMWARE_AR7010;
+               hif_dev->fw_name = FIRMWARE_AR7010_1_1;
        else
                hif_dev->fw_name = FIRMWARE_AR9271;