diff mbox

[2/2] wl1271: Support firmware TX packet aggregation

Message ID 1285686464-8668-3-git-send-email-ido@wizery.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Ido Yariv Sept. 28, 2010, 3:07 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index 1b8295c..e3dc13c 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
@@ -43,13 +43,17 @@  static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb)
 	return -EBUSY;
 }
 
-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 -EBUSY;
+
 	/* allocate free identifier for the packet */
 	id = wl1271_tx_id(wl, skb);
 	if (id < 0)
@@ -82,7 +86,7 @@  static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
 	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;
@@ -133,59 +137,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;
@@ -208,19 +170,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)
@@ -245,7 +210,7 @@  void wl1271_tx_work(struct work_struct *work)
 	struct sk_buff *skb;
 	bool woken_up = false;
 	u32 sta_rates = 0;
-	u32 prev_tx_packets_count;
+	u32 buf_offset;
 	int ret;
 
 	/* check if the rates supported by the AP have changed */
@@ -262,14 +227,15 @@  void wl1271_tx_work(struct work_struct *work)
 	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)) {
 		wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
 		wl1271_acx_rate_policies(wl);
 	}
 
+	/* Prepare the transfer buffer, by aggregating all
+	 * available packets */
+	buf_offset = 0;
 	while ((skb = skb_dequeue(&wl->tx_queue))) {
 		if (!woken_up) {
 			ret = wl1271_ps_elp_wakeup(wl, false);
@@ -278,21 +244,30 @@  void wl1271_tx_work(struct work_struct *work)
 			woken_up = true;
 		}
 
-		ret = wl1271_tx_frame(wl, skb);
+		ret = wl1271_prepare_tx_frame(wl, skb, buf_offset);
 		if (ret == -EBUSY) {
-			/* firmware buffer is full, lets stop transmitting. */
+			/*
+			 * Either the firmware buffer is full, or the
+			 * aggregation buffer is.
+			 * Queue back last skb, and stop aggregating.
+			 */
 			skb_queue_head(&wl->tx_queue, skb);
 			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);
+		/* interrupt the firmware with the new packets */
 		wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
+	}
 
 out:
 	if (woken_up)