aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorIdo Yariv <ido@wizery.com>2010-09-30 07:28:28 -0400
committerLuciano Coelho <luciano.coelho@nokia.com>2010-10-05 09:27:33 -0400
commita19606b4333ff34e9b2863f37c20fe86b42be14c (patch)
treec26739bb4570549c6d2dfbf1574c30a4c7a07869 /drivers/net
parent1f37cbc9363462c99794699442da39f36be0aaf7 (diff)
wl1271: Support firmware TX packet aggregation
Instead of sending one packet at a time to the firmware, try to send all available packets at once. This optimization decreases the number of transactions, which saves CPU cycles and increases network throughput. Signed-off-by: Ido Yariv <ido@wizery.com> Tested-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_tx.c99
1 files changed, 37 insertions, 62 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index 1b8295c5dde4..e3dc13c4d01a 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)
43 return -EBUSY; 43 return -EBUSY;
44} 44}
45 45
46static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra) 46static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
47 u32 buf_offset)
47{ 48{
48 struct wl1271_tx_hw_descr *desc; 49 struct wl1271_tx_hw_descr *desc;
49 u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; 50 u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
50 u32 total_blocks; 51 u32 total_blocks;
51 int id, ret = -EBUSY; 52 int id, ret = -EBUSY;
52 53
54 if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
55 return -EBUSY;
56
53 /* allocate free identifier for the packet */ 57 /* allocate free identifier for the packet */
54 id = wl1271_tx_id(wl, skb); 58 id = wl1271_tx_id(wl, skb);
55 if (id < 0) 59 if (id < 0)
@@ -82,7 +86,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
82 return ret; 86 return ret;
83} 87}
84 88
85static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, 89static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
86 u32 extra, struct ieee80211_tx_info *control) 90 u32 extra, struct ieee80211_tx_info *control)
87{ 91{
88 struct timespec ts; 92 struct timespec ts;
@@ -133,59 +137,17 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
133 desc->tx_attr = cpu_to_le16(tx_attr); 137 desc->tx_attr = cpu_to_le16(tx_attr);
134 138
135 wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad); 139 wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad);
136 return 0;
137}
138
139static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb,
140 struct ieee80211_tx_info *control)
141{
142
143 struct wl1271_tx_hw_descr *desc;
144 int len;
145
146 /* FIXME: This is a workaround for getting non-aligned packets.
147 This happens at least with EAPOL packets from the user space.
148 Our DMA requires packets to be aligned on a 4-byte boundary.
149 */
150 if (unlikely((long)skb->data & 0x03)) {
151 int offset = (4 - (long)skb->data) & 0x03;
152 wl1271_debug(DEBUG_TX, "skb offset %d", offset);
153
154 /* check whether the current skb can be used */
155 if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) {
156 unsigned char *src = skb->data;
157
158 /* align the buffer on a 4-byte boundary */
159 skb_reserve(skb, offset);
160 memmove(skb->data, src, skb->len);
161 } else {
162 wl1271_info("No handler, fixme!");
163 return -EINVAL;
164 }
165 }
166
167 len = WL1271_TX_ALIGN(skb->len);
168
169 /* perform a fixed address block write with the packet */
170 wl1271_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true);
171
172 /* write packet new counter into the write access register */
173 wl->tx_packets_count++;
174
175 desc = (struct wl1271_tx_hw_descr *) skb->data;
176 wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)",
177 desc->id, skb, len, desc->length);
178
179 return 0;
180} 140}
181 141
182/* caller must hold wl->mutex */ 142/* caller must hold wl->mutex */
183static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb) 143static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
144 u32 buf_offset)
184{ 145{
185 struct ieee80211_tx_info *info; 146 struct ieee80211_tx_info *info;
186 u32 extra = 0; 147 u32 extra = 0;
187 int ret = 0; 148 int ret = 0;
188 u8 idx; 149 u8 idx;
150 u32 total_len;
189 151
190 if (!skb) 152 if (!skb)
191 return -EINVAL; 153 return -EINVAL;
@@ -208,19 +170,22 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb)
208 } 170 }
209 } 171 }
210 172
211 ret = wl1271_tx_allocate(wl, skb, extra); 173 ret = wl1271_tx_allocate(wl, skb, extra, buf_offset);
212 if (ret < 0) 174 if (ret < 0)
213 return ret; 175 return ret;
214 176
215 ret = wl1271_tx_fill_hdr(wl, skb, extra, info); 177 wl1271_tx_fill_hdr(wl, skb, extra, info);
216 if (ret < 0)
217 return ret;
218 178
219 ret = wl1271_tx_send_packet(wl, skb, info); 179 /*
220 if (ret < 0) 180 * The length of each packet is stored in terms of words. Thus, we must
221 return ret; 181 * pad the skb data to make sure its length is aligned.
182 * The number of padding bytes is computed and set in wl1271_tx_fill_hdr
183 */
184 total_len = WL1271_TX_ALIGN(skb->len);
185 memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len);
186 memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len);
222 187
223 return ret; 188 return total_len;
224} 189}
225 190
226u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set) 191u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
@@ -245,7 +210,7 @@ void wl1271_tx_work(struct work_struct *work)
245 struct sk_buff *skb; 210 struct sk_buff *skb;
246 bool woken_up = false; 211 bool woken_up = false;
247 u32 sta_rates = 0; 212 u32 sta_rates = 0;
248 u32 prev_tx_packets_count; 213 u32 buf_offset;
249 int ret; 214 int ret;
250 215
251 /* check if the rates supported by the AP have changed */ 216 /* check if the rates supported by the AP have changed */
@@ -262,14 +227,15 @@ void wl1271_tx_work(struct work_struct *work)
262 if (unlikely(wl->state == WL1271_STATE_OFF)) 227 if (unlikely(wl->state == WL1271_STATE_OFF))
263 goto out; 228 goto out;
264 229
265 prev_tx_packets_count = wl->tx_packets_count;
266
267 /* if rates have changed, re-configure the rate policy */ 230 /* if rates have changed, re-configure the rate policy */
268 if (unlikely(sta_rates)) { 231 if (unlikely(sta_rates)) {
269 wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates); 232 wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
270 wl1271_acx_rate_policies(wl); 233 wl1271_acx_rate_policies(wl);
271 } 234 }
272 235
236 /* Prepare the transfer buffer, by aggregating all
237 * available packets */
238 buf_offset = 0;
273 while ((skb = skb_dequeue(&wl->tx_queue))) { 239 while ((skb = skb_dequeue(&wl->tx_queue))) {
274 if (!woken_up) { 240 if (!woken_up) {
275 ret = wl1271_ps_elp_wakeup(wl, false); 241 ret = wl1271_ps_elp_wakeup(wl, false);
@@ -278,21 +244,30 @@ void wl1271_tx_work(struct work_struct *work)
278 woken_up = true; 244 woken_up = true;
279 } 245 }
280 246
281 ret = wl1271_tx_frame(wl, skb); 247 ret = wl1271_prepare_tx_frame(wl, skb, buf_offset);
282 if (ret == -EBUSY) { 248 if (ret == -EBUSY) {
283 /* firmware buffer is full, lets stop transmitting. */ 249 /*
250 * Either the firmware buffer is full, or the
251 * aggregation buffer is.
252 * Queue back last skb, and stop aggregating.
253 */
284 skb_queue_head(&wl->tx_queue, skb); 254 skb_queue_head(&wl->tx_queue, skb);
285 goto out_ack; 255 goto out_ack;
286 } else if (ret < 0) { 256 } else if (ret < 0) {
287 dev_kfree_skb(skb); 257 dev_kfree_skb(skb);
288 goto out_ack; 258 goto out_ack;
289 } 259 }
260 buf_offset += ret;
261 wl->tx_packets_count++;
290 } 262 }
291 263
292out_ack: 264out_ack:
293 /* interrupt the firmware with the new packets */ 265 if (buf_offset) {
294 if (prev_tx_packets_count != wl->tx_packets_count) 266 wl1271_write(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
267 buf_offset, true);
268 /* interrupt the firmware with the new packets */
295 wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); 269 wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
270 }
296 271
297out: 272out:
298 if (woken_up) 273 if (woken_up)