]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/net/wireless/wl12xx/wl1271_tx.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[net-next-2.6.git] / drivers / net / wireless / wl12xx / wl1271_tx.c
index c592cc2e9fe88690bcfb97014a423489fbbedb6d..279be5b98d9fe2f00e6439f4961b80b7e9823516 100644 (file)
 #include "wl1271_ps.h"
 #include "wl1271_tx.h"
 
-static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb)
+static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb)
 {
-       int i;
-       for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
-               if (wl->tx_frames[i] == NULL) {
-                       wl->tx_frames[i] = skb;
-                       wl->tx_frames_cnt++;
-                       return i;
-               }
+       int id;
+
+       id = find_first_zero_bit(wl->tx_frames_map, ACX_TX_DESCRIPTORS);
+       if (id >= ACX_TX_DESCRIPTORS)
+               return -EBUSY;
+
+       __set_bit(id, wl->tx_frames_map);
+       wl->tx_frames[id] = skb;
+       wl->tx_frames_cnt++;
+       return id;
+}
 
-       return -EBUSY;
+static void wl1271_free_tx_id(struct wl1271 *wl, int id)
+{
+       if (__test_and_clear_bit(id, wl->tx_frames_map)) {
+               wl->tx_frames[id] = NULL;
+               wl->tx_frames_cnt--;
+       }
 }
 
-static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
+static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
+                               u32 buf_offset)
 {
        struct wl1271_tx_hw_descr *desc;
        u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
        u32 total_blocks;
        int id, ret = -EBUSY;
 
+       if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
+               return -EAGAIN;
+
        /* allocate free identifier for the packet */
-       id = wl1271_tx_id(wl, skb);
+       id = wl1271_alloc_tx_id(wl, skb);
        if (id < 0)
                return id;
 
@@ -75,14 +88,13 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
                             "tx_allocate: size: %d, blocks: %d, id: %d",
                             total_len, total_blocks, id);
        } else {
-               wl->tx_frames[id] = NULL;
-               wl->tx_frames_cnt--;
+               wl1271_free_tx_id(wl, id);
        }
 
        return ret;
 }
 
-static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
+static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
                              u32 extra, struct ieee80211_tx_info *control)
 {
        struct timespec ts;
@@ -110,9 +122,9 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
        /* configure the tx attributes */
        tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER;
 
-       /* queue */
+       /* queue (we use same identifiers for tid's and ac's */
        ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
-       desc->tid = wl1271_tx_ac_to_tid(ac);
+       desc->tid = ac;
 
        desc->aid = TX_HW_DEFAULT_AID;
        desc->reserved = 0;
@@ -133,59 +145,17 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
        desc->tx_attr = cpu_to_le16(tx_attr);
 
        wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad);
-       return 0;
-}
-
-static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb,
-                                struct ieee80211_tx_info *control)
-{
-
-       struct wl1271_tx_hw_descr *desc;
-       int len;
-
-       /* FIXME: This is a workaround for getting non-aligned packets.
-          This happens at least with EAPOL packets from the user space.
-          Our DMA requires packets to be aligned on a 4-byte boundary.
-       */
-       if (unlikely((long)skb->data & 0x03)) {
-               int offset = (4 - (long)skb->data) & 0x03;
-               wl1271_debug(DEBUG_TX, "skb offset %d", offset);
-
-               /* check whether the current skb can be used */
-               if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) {
-                       unsigned char *src = skb->data;
-
-                       /* align the buffer on a 4-byte boundary */
-                       skb_reserve(skb, offset);
-                       memmove(skb->data, src, skb->len);
-               } else {
-                       wl1271_info("No handler, fixme!");
-                       return -EINVAL;
-               }
-       }
-
-       len = WL1271_TX_ALIGN(skb->len);
-
-       /* perform a fixed address block write with the packet */
-       wl1271_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true);
-
-       /* write packet new counter into the write access register */
-       wl->tx_packets_count++;
-
-       desc = (struct wl1271_tx_hw_descr *) skb->data;
-       wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)",
-                    desc->id, skb, len, desc->length);
-
-       return 0;
 }
 
 /* caller must hold wl->mutex */
-static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb)
+static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
+                                                       u32 buf_offset)
 {
        struct ieee80211_tx_info *info;
        u32 extra = 0;
        int ret = 0;
        u8 idx;
+       u32 total_len;
 
        if (!skb)
                return -EINVAL;
@@ -193,7 +163,7 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb)
        info = IEEE80211_SKB_CB(skb);
 
        if (info->control.hw_key &&
-           info->control.hw_key->alg == ALG_TKIP)
+           info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
                extra = WL1271_TKIP_IV_SPACE;
 
        if (info->control.hw_key) {
@@ -208,19 +178,22 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb)
                }
        }
 
-       ret = wl1271_tx_allocate(wl, skb, extra);
+       ret = wl1271_tx_allocate(wl, skb, extra, buf_offset);
        if (ret < 0)
                return ret;
 
-       ret = wl1271_tx_fill_hdr(wl, skb, extra, info);
-       if (ret < 0)
-               return ret;
+       wl1271_tx_fill_hdr(wl, skb, extra, info);
 
-       ret = wl1271_tx_send_packet(wl, skb, info);
-       if (ret < 0)
-               return ret;
+       /*
+        * The length of each packet is stored in terms of words. Thus, we must
+        * pad the skb data to make sure its length is aligned.
+        * The number of padding bytes is computed and set in wl1271_tx_fill_hdr
+        */
+       total_len = WL1271_TX_ALIGN(skb->len);
+       memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len);
+       memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len);
 
-       return ret;
+       return total_len;
 }
 
 u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
@@ -236,36 +209,63 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
                rate_set >>= 1;
        }
 
+#ifdef CONFIG_WL1271_HT
+       /* MCS rates indication are on bits 16 - 23 */
+       rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates;
+
+       for (bit = 0; bit < 8; bit++) {
+               if (rate_set & 0x1)
+                       enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit);
+               rate_set >>= 1;
+       }
+#endif
+
        return enabled_rates;
 }
 
-void wl1271_tx_work(struct work_struct *work)
+static void handle_tx_low_watermark(struct wl1271 *wl)
+{
+       unsigned long flags;
+
+       if (test_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags) &&
+           skb_queue_len(&wl->tx_queue) <= WL1271_TX_QUEUE_LOW_WATERMARK) {
+               /* firmware buffer has space, restart queues */
+               spin_lock_irqsave(&wl->wl_lock, flags);
+               ieee80211_wake_queues(wl->hw);
+               clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
+               spin_unlock_irqrestore(&wl->wl_lock, flags);
+       }
+}
+
+void wl1271_tx_work_locked(struct wl1271 *wl)
 {
-       struct wl1271 *wl = container_of(work, struct wl1271, tx_work);
        struct sk_buff *skb;
        bool woken_up = false;
        u32 sta_rates = 0;
-       u32 prev_tx_packets_count;
+       u32 buf_offset = 0;
+       bool sent_packets = false;
        int ret;
 
        /* check if the rates supported by the AP have changed */
        if (unlikely(test_and_clear_bit(WL1271_FLAG_STA_RATES_CHANGED,
                                        &wl->flags))) {
                unsigned long flags;
+
                spin_lock_irqsave(&wl->wl_lock, flags);
                sta_rates = wl->sta_rate_set;
                spin_unlock_irqrestore(&wl->wl_lock, flags);
        }
 
-       mutex_lock(&wl->mutex);
-
        if (unlikely(wl->state == WL1271_STATE_OFF))
                goto out;
 
-       prev_tx_packets_count = wl->tx_packets_count;
-
        /* if rates have changed, re-configure the rate policy */
        if (unlikely(sta_rates)) {
+               ret = wl1271_ps_elp_wakeup(wl, false);
+               if (ret < 0)
+                       goto out;
+               woken_up = true;
+
                wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
                wl1271_acx_rate_policies(wl);
        }
@@ -278,26 +278,58 @@ void wl1271_tx_work(struct work_struct *work)
                        woken_up = true;
                }
 
-               ret = wl1271_tx_frame(wl, skb);
-               if (ret == -EBUSY) {
-                       /* firmware buffer is full, lets stop transmitting. */
+               ret = wl1271_prepare_tx_frame(wl, skb, buf_offset);
+               if (ret == -EAGAIN) {
+                       /*
+                        * Aggregation buffer is full.
+                        * Flush buffer and try again.
+                        */
+                       skb_queue_head(&wl->tx_queue, skb);
+                       wl1271_write(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
+                               buf_offset, true);
+                       sent_packets = true;
+                       buf_offset = 0;
+                       continue;
+               } else if (ret == -EBUSY) {
+                       /*
+                        * Firmware buffer is full.
+                        * Queue back last skb, and stop aggregating.
+                        */
                        skb_queue_head(&wl->tx_queue, skb);
+                       /* No work left, avoid scheduling redundant tx work */
+                       set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
                        goto out_ack;
                } else if (ret < 0) {
                        dev_kfree_skb(skb);
                        goto out_ack;
                }
+               buf_offset += ret;
+               wl->tx_packets_count++;
        }
 
 out_ack:
-       /* interrupt the firmware with the new packets */
-       if (prev_tx_packets_count != wl->tx_packets_count)
+       if (buf_offset) {
+               wl1271_write(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
+                               buf_offset, true);
+               sent_packets = true;
+       }
+       if (sent_packets) {
+               /* interrupt the firmware with the new packets */
                wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
+               handle_tx_low_watermark(wl);
+       }
 
 out:
        if (woken_up)
                wl1271_ps_elp_sleep(wl);
+}
+
+void wl1271_tx_work(struct work_struct *work)
+{
+       struct wl1271 *wl = container_of(work, struct wl1271, tx_work);
 
+       mutex_lock(&wl->mutex);
+       wl1271_tx_work_locked(wl);
        mutex_unlock(&wl->mutex);
 }
 
@@ -323,7 +355,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
        if (result->status == TX_SUCCESS) {
                if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
                        info->flags |= IEEE80211_TX_STAT_ACK;
-               rate = wl1271_rate_to_idx(wl, result->rate_class_index);
+               rate = wl1271_rate_to_idx(result->rate_class_index, wl->band);
                retries = result->ack_failures;
        } else if (result->status == TX_RETRY_EXCEEDED) {
                wl->stats.excessive_retries++;
@@ -347,7 +379,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
 
        /* remove TKIP header space if present */
        if (info->control.hw_key &&
-           info->control.hw_key->alg == ALG_TKIP) {
+           info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
                int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
                memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen);
                skb_pull(skb, WL1271_TKIP_IV_SPACE);
@@ -360,8 +392,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
 
        /* return the packet to the stack */
        ieee80211_tx_status(wl->hw, skb);
-       wl->tx_frames[result->id] = NULL;
-       wl->tx_frames_cnt--;
+       wl1271_free_tx_id(wl, result->id);
 }
 
 /* Called upon reception of a TX complete interrupt */
@@ -400,19 +431,6 @@ void wl1271_tx_complete(struct wl1271 *wl)
 
                wl->tx_results_count++;
        }
-
-       if (test_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags) &&
-           skb_queue_len(&wl->tx_queue) <= WL1271_TX_QUEUE_LOW_WATERMARK) {
-               unsigned long flags;
-
-               /* firmware buffer has space, restart queues */
-               wl1271_debug(DEBUG_TX, "tx_complete: waking queues");
-               spin_lock_irqsave(&wl->wl_lock, flags);
-               ieee80211_wake_queues(wl->hw);
-               clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
-               spin_unlock_irqrestore(&wl->wl_lock, flags);
-               ieee80211_queue_work(wl->hw, &wl->tx_work);
-       }
 }
 
 /* caller must hold wl->mutex */
@@ -422,21 +440,24 @@ void wl1271_tx_reset(struct wl1271 *wl)
        struct sk_buff *skb;
 
        /* TX failure */
-/*     control->flags = 0; FIXME */
-
        while ((skb = skb_dequeue(&wl->tx_queue))) {
                wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb);
                ieee80211_tx_status(wl->hw, skb);
        }
 
+       /*
+        * Make sure the driver is at a consistent state, in case this
+        * function is called from a context other than interface removal.
+        */
+       handle_tx_low_watermark(wl);
+
        for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
                if (wl->tx_frames[i] != NULL) {
                        skb = wl->tx_frames[i];
-                       wl->tx_frames[i] = NULL;
+                       wl1271_free_tx_id(wl, i);
                        wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb);
                        ieee80211_tx_status(wl->hw, skb);
                }
-       wl->tx_frames_cnt = 0;
 }
 
 #define WL1271_TX_FLUSH_TIMEOUT 500000