Commit | Line | Data |
---|---|---|
f5fc0f86 LC |
1 | /* |
2 | * This file is part of wl1271 | |
3 | * | |
4 | * Copyright (C) 2009 Nokia Corporation | |
5 | * | |
6 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
20 | * 02110-1301 USA | |
21 | * | |
22 | */ | |
23 | ||
24 | #include <linux/kernel.h> | |
25 | #include <linux/module.h> | |
c6c8a65d | 26 | #include <linux/etherdevice.h> |
f5fc0f86 | 27 | |
00d20100 SL |
28 | #include "wl12xx.h" |
29 | #include "io.h" | |
30 | #include "reg.h" | |
31 | #include "ps.h" | |
32 | #include "tx.h" | |
56d4f8f6 | 33 | #include "event.h" |
f5fc0f86 | 34 | |
7f179b46 AN |
35 | static int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id) |
36 | { | |
37 | int ret; | |
38 | bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); | |
39 | ||
40 | if (is_ap) | |
c690ec81 | 41 | ret = wl12xx_cmd_set_default_wep_key(wl, id, |
e51ae9be | 42 | wl->ap_bcast_hlid); |
7f179b46 | 43 | else |
c690ec81 | 44 | ret = wl12xx_cmd_set_default_wep_key(wl, id, wl->sta_hlid); |
7f179b46 AN |
45 | |
46 | if (ret < 0) | |
47 | return ret; | |
48 | ||
49 | wl1271_debug(DEBUG_CRYPT, "default wep key idx: %d", (int)id); | |
50 | return 0; | |
51 | } | |
52 | ||
25eeb9e3 | 53 | static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb) |
f5fc0f86 | 54 | { |
25eeb9e3 IY |
55 | int id; |
56 | ||
57 | id = find_first_zero_bit(wl->tx_frames_map, ACX_TX_DESCRIPTORS); | |
58 | if (id >= ACX_TX_DESCRIPTORS) | |
59 | return -EBUSY; | |
60 | ||
61 | __set_bit(id, wl->tx_frames_map); | |
62 | wl->tx_frames[id] = skb; | |
63 | wl->tx_frames_cnt++; | |
64 | return id; | |
65 | } | |
f5fc0f86 | 66 | |
25eeb9e3 IY |
67 | static void wl1271_free_tx_id(struct wl1271 *wl, int id) |
68 | { | |
69 | if (__test_and_clear_bit(id, wl->tx_frames_map)) { | |
ef2e3004 IY |
70 | if (unlikely(wl->tx_frames_cnt == ACX_TX_DESCRIPTORS)) |
71 | clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); | |
72 | ||
25eeb9e3 IY |
73 | wl->tx_frames[id] = NULL; |
74 | wl->tx_frames_cnt--; | |
75 | } | |
f5fc0f86 LC |
76 | } |
77 | ||
c5745187 OBC |
78 | static int wl1271_tx_update_filters(struct wl1271 *wl, |
79 | struct sk_buff *skb) | |
80 | { | |
81 | struct ieee80211_hdr *hdr; | |
251c177f | 82 | int ret; |
c5745187 | 83 | |
df4c849f | 84 | hdr = (struct ieee80211_hdr *)skb->data; |
c5745187 OBC |
85 | |
86 | /* | |
87 | * stop bssid-based filtering before transmitting authentication | |
88 | * requests. this way the hw will never drop authentication | |
89 | * responses coming from BSSIDs it isn't familiar with (e.g. on | |
90 | * roaming) | |
91 | */ | |
92 | if (!ieee80211_is_auth(hdr->frame_control)) | |
93 | return 0; | |
94 | ||
251c177f EP |
95 | if (wl->dev_hlid != WL12XX_INVALID_LINK_ID) |
96 | goto out; | |
97 | ||
98 | wl1271_debug(DEBUG_CMD, "starting device role for roaming"); | |
99 | ret = wl12xx_cmd_role_start_dev(wl); | |
100 | if (ret < 0) | |
101 | goto out; | |
102 | ||
103 | ret = wl12xx_roc(wl, wl->dev_role_id); | |
104 | if (ret < 0) | |
105 | goto out; | |
106 | out: | |
08c1d1c7 | 107 | return 0; |
c5745187 OBC |
108 | } |
109 | ||
99a2775d AN |
110 | static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, |
111 | struct sk_buff *skb) | |
112 | { | |
113 | struct ieee80211_hdr *hdr; | |
114 | ||
115 | /* | |
116 | * add the station to the known list before transmitting the | |
117 | * authentication response. this way it won't get de-authed by FW | |
118 | * when transmitting too soon. | |
119 | */ | |
120 | hdr = (struct ieee80211_hdr *)(skb->data + | |
121 | sizeof(struct wl1271_tx_hw_descr)); | |
122 | if (ieee80211_is_auth(hdr->frame_control)) | |
123 | wl1271_acx_set_inconnection_sta(wl, hdr->addr1); | |
124 | } | |
125 | ||
b622d992 AN |
126 | static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid) |
127 | { | |
da03209e | 128 | bool fw_ps, single_sta; |
9b17f1b3 | 129 | u8 tx_pkts; |
b622d992 AN |
130 | |
131 | /* only regulate station links */ | |
132 | if (hlid < WL1271_AP_STA_HLID_START) | |
133 | return; | |
134 | ||
56d4f8f6 AN |
135 | if (WARN_ON(!wl1271_is_active_sta(wl, hlid))) |
136 | return; | |
137 | ||
b622d992 | 138 | fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); |
9b17f1b3 | 139 | tx_pkts = wl->links[hlid].allocated_pkts; |
da03209e | 140 | single_sta = (wl->active_sta_count == 1); |
b622d992 AN |
141 | |
142 | /* | |
143 | * if in FW PS and there is enough data in FW we can put the link | |
144 | * into high-level PS and clean out its TX queues. | |
da03209e AN |
145 | * Make an exception if this is the only connected station. In this |
146 | * case FW-memory congestion is not a problem. | |
b622d992 | 147 | */ |
da03209e | 148 | if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) |
b622d992 AN |
149 | wl1271_ps_link_start(wl, hlid, true); |
150 | } | |
151 | ||
f8e0af6b | 152 | bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb) |
f4df1bd5 EP |
153 | { |
154 | return wl->dummy_packet == skb; | |
155 | } | |
156 | ||
157 | u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct sk_buff *skb) | |
a8c0ddb5 AN |
158 | { |
159 | struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb); | |
160 | ||
161 | if (control->control.sta) { | |
162 | struct wl1271_station *wl_sta; | |
163 | ||
164 | wl_sta = (struct wl1271_station *) | |
165 | control->control.sta->drv_priv; | |
166 | return wl_sta->hlid; | |
167 | } else { | |
168 | struct ieee80211_hdr *hdr; | |
169 | ||
f4df1bd5 EP |
170 | if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) |
171 | return wl->system_hlid; | |
172 | ||
a8c0ddb5 AN |
173 | hdr = (struct ieee80211_hdr *)skb->data; |
174 | if (ieee80211_is_mgmt(hdr->frame_control)) | |
e51ae9be | 175 | return wl->ap_global_hlid; |
a8c0ddb5 | 176 | else |
e51ae9be | 177 | return wl->ap_bcast_hlid; |
a8c0ddb5 AN |
178 | } |
179 | } | |
180 | ||
f4df1bd5 EP |
181 | static u8 wl1271_tx_get_hlid(struct wl1271 *wl, struct sk_buff *skb) |
182 | { | |
df4c849f EP |
183 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; |
184 | ||
f4df1bd5 EP |
185 | if (wl12xx_is_dummy_packet(wl, skb)) |
186 | return wl->system_hlid; | |
187 | ||
188 | if (wl->bss_type == BSS_TYPE_AP_BSS) | |
189 | return wl12xx_tx_get_hlid_ap(wl, skb); | |
190 | ||
df4c849f EP |
191 | wl1271_tx_update_filters(wl, skb); |
192 | ||
193 | if ((test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) || | |
194 | test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags)) && | |
195 | !ieee80211_is_auth(hdr->frame_control) && | |
196 | !ieee80211_is_assoc_req(hdr->frame_control)) | |
f4df1bd5 EP |
197 | return wl->sta_hlid; |
198 | else | |
199 | return wl->dev_hlid; | |
200 | } | |
201 | ||
0da13da7 IY |
202 | static unsigned int wl12xx_calc_packet_alignment(struct wl1271 *wl, |
203 | unsigned int packet_length) | |
204 | { | |
205 | if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT) | |
206 | return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE); | |
207 | else | |
208 | return ALIGN(packet_length, WL1271_TX_ALIGN_TO); | |
209 | } | |
210 | ||
a19606b4 | 211 | static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, |
09039f42 | 212 | u32 buf_offset, u8 hlid) |
f5fc0f86 LC |
213 | { |
214 | struct wl1271_tx_hw_descr *desc; | |
215 | u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; | |
48a61477 | 216 | u32 len; |
5c9417f1 | 217 | u32 total_blocks; |
742246f8 | 218 | int id, ret = -EBUSY, ac; |
e9eb8cbe | 219 | u32 spare_blocks = wl->tx_spare_blocks; |
f5fc0f86 | 220 | |
a19606b4 | 221 | if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE) |
6c6e669e | 222 | return -EAGAIN; |
a19606b4 | 223 | |
f5fc0f86 | 224 | /* allocate free identifier for the packet */ |
25eeb9e3 | 225 | id = wl1271_alloc_tx_id(wl, skb); |
f5fc0f86 LC |
226 | if (id < 0) |
227 | return id; | |
228 | ||
229 | /* approximate the number of blocks required for this packet | |
230 | in the firmware */ | |
0da13da7 | 231 | len = wl12xx_calc_packet_alignment(wl, total_len); |
48a61477 | 232 | |
e9eb8cbe GE |
233 | /* in case of a dummy packet, use default amount of spare mem blocks */ |
234 | if (unlikely(wl12xx_is_dummy_packet(wl, skb))) | |
235 | spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT; | |
236 | ||
48a61477 | 237 | total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE + |
e7ddf549 | 238 | spare_blocks; |
48a61477 | 239 | |
f5fc0f86 LC |
240 | if (total_blocks <= wl->tx_blocks_available) { |
241 | desc = (struct wl1271_tx_hw_descr *)skb_push( | |
242 | skb, total_len - skb->len); | |
243 | ||
ae77eccf SL |
244 | /* HW descriptor fields change between wl127x and wl128x */ |
245 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
246 | desc->wl128x_mem.total_mem_blocks = total_blocks; | |
247 | } else { | |
e7ddf549 | 248 | desc->wl127x_mem.extra_blocks = spare_blocks; |
ae77eccf SL |
249 | desc->wl127x_mem.total_mem_blocks = total_blocks; |
250 | } | |
251 | ||
f5fc0f86 LC |
252 | desc->id = id; |
253 | ||
254 | wl->tx_blocks_available -= total_blocks; | |
7bb5d6ce | 255 | wl->tx_allocated_blocks += total_blocks; |
f5fc0f86 | 256 | |
742246f8 AN |
257 | ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); |
258 | wl->tx_allocated_pkts[ac]++; | |
bf54e301 | 259 | |
9b17f1b3 AN |
260 | if (wl->bss_type == BSS_TYPE_AP_BSS && |
261 | hlid >= WL1271_AP_STA_HLID_START) | |
262 | wl->links[hlid].allocated_pkts++; | |
09039f42 | 263 | |
f5fc0f86 LC |
264 | ret = 0; |
265 | ||
266 | wl1271_debug(DEBUG_TX, | |
267 | "tx_allocate: size: %d, blocks: %d, id: %d", | |
268 | total_len, total_blocks, id); | |
781608c4 | 269 | } else { |
25eeb9e3 | 270 | wl1271_free_tx_id(wl, id); |
781608c4 | 271 | } |
f5fc0f86 LC |
272 | |
273 | return ret; | |
274 | } | |
275 | ||
a19606b4 | 276 | static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, |
09039f42 AN |
277 | u32 extra, struct ieee80211_tx_info *control, |
278 | u8 hlid) | |
f5fc0f86 | 279 | { |
ac5e1e39 | 280 | struct timespec ts; |
f5fc0f86 | 281 | struct wl1271_tx_hw_descr *desc; |
48a61477 | 282 | int aligned_len, ac, rate_idx; |
ac5e1e39 | 283 | s64 hosttime; |
d0f63b20 | 284 | u16 tx_attr; |
f5fc0f86 LC |
285 | |
286 | desc = (struct wl1271_tx_hw_descr *) skb->data; | |
287 | ||
1e2b7976 JO |
288 | /* relocate space for security header */ |
289 | if (extra) { | |
290 | void *framestart = skb->data + sizeof(*desc); | |
291 | u16 fc = *(u16 *)(framestart + extra); | |
d0f63b20 | 292 | int hdrlen = ieee80211_hdrlen(cpu_to_le16(fc)); |
1e2b7976 JO |
293 | memmove(framestart, framestart + extra, hdrlen); |
294 | } | |
295 | ||
f5fc0f86 | 296 | /* configure packet life time */ |
ac5e1e39 JO |
297 | getnstimeofday(&ts); |
298 | hosttime = (timespec_to_ns(&ts) >> 10); | |
299 | desc->start_time = cpu_to_le32(hosttime - wl->time_offset); | |
c6c8a65d AN |
300 | |
301 | if (wl->bss_type != BSS_TYPE_AP_BSS) | |
302 | desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU); | |
303 | else | |
304 | desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU); | |
f5fc0f86 | 305 | |
db674d24 | 306 | /* queue */ |
c6999d83 | 307 | ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); |
db674d24 | 308 | desc->tid = skb->priority; |
c6c8a65d | 309 | |
990f5de7 | 310 | if (wl12xx_is_dummy_packet(wl, skb)) { |
ae47c45f SL |
311 | /* |
312 | * FW expects the dummy packet to have an invalid session id - | |
313 | * any session id that is different than the one set in the join | |
314 | */ | |
315 | tx_attr = ((~wl->session_counter) << | |
316 | TX_HW_ATTR_OFST_SESSION_COUNTER) & | |
317 | TX_HW_ATTR_SESSION_COUNTER; | |
318 | ||
319 | tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ; | |
ae47c45f SL |
320 | } else { |
321 | /* configure the tx attributes */ | |
322 | tx_attr = | |
323 | wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; | |
324 | } | |
325 | ||
79b122dc | 326 | desc->hlid = hlid; |
c6c8a65d | 327 | |
79b122dc | 328 | if (wl->bss_type != BSS_TYPE_AP_BSS) { |
c6c8a65d AN |
329 | /* if the packets are destined for AP (have a STA entry) |
330 | send them with AP rate policies, otherwise use default | |
331 | basic rates */ | |
332 | if (control->control.sta) | |
333 | rate_idx = ACX_TX_AP_FULL_RATE; | |
334 | else | |
335 | rate_idx = ACX_TX_BASIC_RATE; | |
336 | } else { | |
e51ae9be | 337 | if (hlid == wl->ap_global_hlid) |
09039f42 | 338 | rate_idx = ACX_TX_AP_MODE_MGMT_RATE; |
e51ae9be | 339 | else if (hlid == wl->ap_bcast_hlid) |
09039f42 | 340 | rate_idx = ACX_TX_AP_MODE_BCST_RATE; |
e51ae9be | 341 | else |
c6c8a65d | 342 | rate_idx = ac; |
c6c8a65d AN |
343 | } |
344 | ||
345 | tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY; | |
f5fc0f86 LC |
346 | desc->reserved = 0; |
347 | ||
0da13da7 | 348 | aligned_len = wl12xx_calc_packet_alignment(wl, skb->len); |
48a61477 | 349 | |
0da13da7 | 350 | if (wl->chip.id == CHIP_ID_1283_PG20) { |
48a61477 SL |
351 | desc->wl128x_mem.extra_bytes = aligned_len - skb->len; |
352 | desc->length = cpu_to_le16(aligned_len >> 2); | |
ae77eccf SL |
353 | |
354 | wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d " | |
355 | "tx_attr: 0x%x len: %d life: %d mem: %d", | |
356 | desc->hlid, tx_attr, | |
357 | le16_to_cpu(desc->length), | |
358 | le16_to_cpu(desc->life_time), | |
359 | desc->wl128x_mem.total_mem_blocks); | |
48a61477 SL |
360 | } else { |
361 | int pad; | |
362 | ||
0da13da7 | 363 | /* Store the aligned length in terms of words */ |
48a61477 SL |
364 | desc->length = cpu_to_le16(aligned_len >> 2); |
365 | ||
366 | /* calculate number of padding bytes */ | |
367 | pad = aligned_len - skb->len; | |
368 | tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; | |
f5fc0f86 | 369 | |
ae77eccf SL |
370 | wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d " |
371 | "tx_attr: 0x%x len: %d life: %d mem: %d", pad, | |
372 | desc->hlid, tx_attr, | |
373 | le16_to_cpu(desc->length), | |
374 | le16_to_cpu(desc->life_time), | |
375 | desc->wl127x_mem.total_mem_blocks); | |
48a61477 | 376 | } |
d0f63b20 LC |
377 | |
378 | desc->tx_attr = cpu_to_le16(tx_attr); | |
f5fc0f86 LC |
379 | } |
380 | ||
381 | /* caller must hold wl->mutex */ | |
a19606b4 IY |
382 | static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, |
383 | u32 buf_offset) | |
f5fc0f86 LC |
384 | { |
385 | struct ieee80211_tx_info *info; | |
386 | u32 extra = 0; | |
387 | int ret = 0; | |
a19606b4 | 388 | u32 total_len; |
09039f42 | 389 | u8 hlid; |
f5fc0f86 LC |
390 | |
391 | if (!skb) | |
392 | return -EINVAL; | |
393 | ||
394 | info = IEEE80211_SKB_CB(skb); | |
395 | ||
396 | if (info->control.hw_key && | |
97359d12 | 397 | info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) |
f5fc0f86 LC |
398 | extra = WL1271_TKIP_IV_SPACE; |
399 | ||
400 | if (info->control.hw_key) { | |
7f179b46 AN |
401 | bool is_wep; |
402 | u8 idx = info->control.hw_key->hw_key_idx; | |
403 | u32 cipher = info->control.hw_key->cipher; | |
404 | ||
405 | is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) || | |
406 | (cipher == WLAN_CIPHER_SUITE_WEP104); | |
f5fc0f86 | 407 | |
7f179b46 AN |
408 | if (unlikely(is_wep && wl->default_key != idx)) { |
409 | ret = wl1271_set_default_wep_key(wl, idx); | |
f5fc0f86 LC |
410 | if (ret < 0) |
411 | return ret; | |
ee444cf0 | 412 | wl->default_key = idx; |
f5fc0f86 LC |
413 | } |
414 | } | |
415 | ||
f4df1bd5 | 416 | hlid = wl1271_tx_get_hlid(wl, skb); |
79b122dc EP |
417 | if (hlid == WL12XX_INVALID_LINK_ID) { |
418 | wl1271_error("invalid hlid. dropping skb 0x%p", skb); | |
419 | return -EINVAL; | |
420 | } | |
09039f42 AN |
421 | |
422 | ret = wl1271_tx_allocate(wl, skb, extra, buf_offset, hlid); | |
f5fc0f86 LC |
423 | if (ret < 0) |
424 | return ret; | |
425 | ||
fae2fd76 AN |
426 | wl1271_tx_fill_hdr(wl, skb, extra, info, hlid); |
427 | ||
b622d992 | 428 | if (wl->bss_type == BSS_TYPE_AP_BSS) { |
99a2775d | 429 | wl1271_tx_ap_update_inconnection_sta(wl, skb); |
b622d992 AN |
430 | wl1271_tx_regulate_link(wl, hlid); |
431 | } | |
99a2775d | 432 | |
a19606b4 | 433 | /* |
48a61477 SL |
434 | * The length of each packet is stored in terms of |
435 | * words. Thus, we must pad the skb data to make sure its | |
436 | * length is aligned. The number of padding bytes is computed | |
437 | * and set in wl1271_tx_fill_hdr. | |
438 | * In special cases, we want to align to a specific block size | |
439 | * (eg. for wl128x with SDIO we align to 256). | |
a19606b4 | 440 | */ |
0da13da7 | 441 | total_len = wl12xx_calc_packet_alignment(wl, skb->len); |
48a61477 | 442 | |
a19606b4 IY |
443 | memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); |
444 | memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); | |
f5fc0f86 | 445 | |
990f5de7 IY |
446 | /* Revert side effects in the dummy packet skb, so it can be reused */ |
447 | if (wl12xx_is_dummy_packet(wl, skb)) | |
448 | skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); | |
449 | ||
a19606b4 | 450 | return total_len; |
f5fc0f86 LC |
451 | } |
452 | ||
af7fbb28 EP |
453 | u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, |
454 | enum ieee80211_band rate_band) | |
830fb67b JO |
455 | { |
456 | struct ieee80211_supported_band *band; | |
457 | u32 enabled_rates = 0; | |
458 | int bit; | |
459 | ||
af7fbb28 | 460 | band = wl->hw->wiphy->bands[rate_band]; |
830fb67b JO |
461 | for (bit = 0; bit < band->n_bitrates; bit++) { |
462 | if (rate_set & 0x1) | |
463 | enabled_rates |= band->bitrates[bit].hw_value; | |
464 | rate_set >>= 1; | |
465 | } | |
466 | ||
18357850 SL |
467 | /* MCS rates indication are on bits 16 - 23 */ |
468 | rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates; | |
469 | ||
470 | for (bit = 0; bit < 8; bit++) { | |
471 | if (rate_set & 0x1) | |
472 | enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit); | |
473 | rate_set >>= 1; | |
474 | } | |
18357850 | 475 | |
830fb67b JO |
476 | return enabled_rates; |
477 | } | |
478 | ||
a8c0ddb5 | 479 | void wl1271_handle_tx_low_watermark(struct wl1271 *wl) |
2fe33e8c IY |
480 | { |
481 | unsigned long flags; | |
708bb3cf | 482 | int i; |
2fe33e8c | 483 | |
708bb3cf AN |
484 | for (i = 0; i < NUM_TX_QUEUES; i++) { |
485 | if (test_bit(i, &wl->stopped_queues_map) && | |
f1a46384 | 486 | wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) { |
708bb3cf AN |
487 | /* firmware buffer has space, restart queues */ |
488 | spin_lock_irqsave(&wl->wl_lock, flags); | |
489 | ieee80211_wake_queue(wl->hw, | |
490 | wl1271_tx_get_mac80211_queue(i)); | |
491 | clear_bit(i, &wl->stopped_queues_map); | |
492 | spin_unlock_irqrestore(&wl->wl_lock, flags); | |
493 | } | |
2fe33e8c IY |
494 | } |
495 | } | |
496 | ||
742246f8 AN |
497 | static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl, |
498 | struct sk_buff_head *queues) | |
499 | { | |
500 | int i, q = -1, ac; | |
501 | u32 min_pkts = 0xffffffff; | |
502 | ||
503 | /* | |
504 | * Find a non-empty ac where: | |
505 | * 1. There are packets to transmit | |
506 | * 2. The FW has the least allocated blocks | |
507 | * | |
508 | * We prioritize the ACs according to VO>VI>BE>BK | |
509 | */ | |
510 | for (i = 0; i < NUM_TX_QUEUES; i++) { | |
511 | ac = wl1271_tx_get_queue(i); | |
512 | if (!skb_queue_empty(&queues[ac]) && | |
513 | (wl->tx_allocated_pkts[ac] < min_pkts)) { | |
514 | q = ac; | |
515 | min_pkts = wl->tx_allocated_pkts[q]; | |
516 | } | |
517 | } | |
518 | ||
519 | if (q == -1) | |
520 | return NULL; | |
521 | ||
522 | return &queues[q]; | |
523 | } | |
524 | ||
a8c0ddb5 | 525 | static struct sk_buff *wl1271_sta_skb_dequeue(struct wl1271 *wl) |
6742f554 JO |
526 | { |
527 | struct sk_buff *skb = NULL; | |
528 | unsigned long flags; | |
742246f8 | 529 | struct sk_buff_head *queue; |
6742f554 | 530 | |
742246f8 AN |
531 | queue = wl1271_select_queue(wl, wl->tx_queue); |
532 | if (!queue) | |
7bb5d6ce | 533 | goto out; |
742246f8 AN |
534 | |
535 | skb = skb_dequeue(queue); | |
6742f554 JO |
536 | |
537 | out: | |
538 | if (skb) { | |
f1a46384 | 539 | int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); |
6742f554 | 540 | spin_lock_irqsave(&wl->wl_lock, flags); |
f1a46384 | 541 | wl->tx_queue_count[q]--; |
6742f554 JO |
542 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
543 | } | |
544 | ||
545 | return skb; | |
546 | } | |
547 | ||
a8c0ddb5 AN |
548 | static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl) |
549 | { | |
550 | struct sk_buff *skb = NULL; | |
551 | unsigned long flags; | |
552 | int i, h, start_hlid; | |
742246f8 | 553 | struct sk_buff_head *queue; |
a8c0ddb5 AN |
554 | |
555 | /* start from the link after the last one */ | |
556 | start_hlid = (wl->last_tx_hlid + 1) % AP_MAX_LINKS; | |
557 | ||
558 | /* dequeue according to AC, round robin on each link */ | |
559 | for (i = 0; i < AP_MAX_LINKS; i++) { | |
560 | h = (start_hlid + i) % AP_MAX_LINKS; | |
561 | ||
742246f8 AN |
562 | /* only consider connected stations */ |
563 | if (h >= WL1271_AP_STA_HLID_START && | |
564 | !test_bit(h - WL1271_AP_STA_HLID_START, wl->ap_hlid_map)) | |
565 | continue; | |
566 | ||
567 | queue = wl1271_select_queue(wl, wl->links[h].tx_queue); | |
568 | if (!queue) | |
569 | continue; | |
570 | ||
571 | skb = skb_dequeue(queue); | |
7bb5d6ce | 572 | if (skb) |
742246f8 | 573 | break; |
a8c0ddb5 AN |
574 | } |
575 | ||
a8c0ddb5 | 576 | if (skb) { |
f1a46384 | 577 | int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); |
a8c0ddb5 AN |
578 | wl->last_tx_hlid = h; |
579 | spin_lock_irqsave(&wl->wl_lock, flags); | |
f1a46384 | 580 | wl->tx_queue_count[q]--; |
a8c0ddb5 AN |
581 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
582 | } else { | |
583 | wl->last_tx_hlid = 0; | |
584 | } | |
585 | ||
586 | return skb; | |
587 | } | |
588 | ||
589 | static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) | |
590 | { | |
990f5de7 IY |
591 | unsigned long flags; |
592 | struct sk_buff *skb = NULL; | |
593 | ||
a8c0ddb5 | 594 | if (wl->bss_type == BSS_TYPE_AP_BSS) |
990f5de7 IY |
595 | skb = wl1271_ap_skb_dequeue(wl); |
596 | else | |
597 | skb = wl1271_sta_skb_dequeue(wl); | |
a8c0ddb5 | 598 | |
990f5de7 IY |
599 | if (!skb && |
600 | test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) { | |
f1a46384 AN |
601 | int q; |
602 | ||
990f5de7 | 603 | skb = wl->dummy_packet; |
f1a46384 | 604 | q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); |
990f5de7 | 605 | spin_lock_irqsave(&wl->wl_lock, flags); |
f1a46384 | 606 | wl->tx_queue_count[q]--; |
990f5de7 IY |
607 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
608 | } | |
609 | ||
610 | return skb; | |
a8c0ddb5 AN |
611 | } |
612 | ||
6742f554 JO |
613 | static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) |
614 | { | |
615 | unsigned long flags; | |
616 | int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); | |
617 | ||
990f5de7 IY |
618 | if (wl12xx_is_dummy_packet(wl, skb)) { |
619 | set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); | |
620 | } else if (wl->bss_type == BSS_TYPE_AP_BSS) { | |
f4df1bd5 | 621 | u8 hlid = wl1271_tx_get_hlid(wl, skb); |
a8c0ddb5 AN |
622 | skb_queue_head(&wl->links[hlid].tx_queue[q], skb); |
623 | ||
624 | /* make sure we dequeue the same packet next time */ | |
625 | wl->last_tx_hlid = (hlid + AP_MAX_LINKS - 1) % AP_MAX_LINKS; | |
626 | } else { | |
627 | skb_queue_head(&wl->tx_queue[q], skb); | |
628 | } | |
629 | ||
6742f554 | 630 | spin_lock_irqsave(&wl->wl_lock, flags); |
f1a46384 | 631 | wl->tx_queue_count[q]++; |
6742f554 JO |
632 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
633 | } | |
634 | ||
77ddaa10 EP |
635 | static bool wl1271_tx_is_data_present(struct sk_buff *skb) |
636 | { | |
637 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); | |
638 | ||
639 | return ieee80211_is_data_present(hdr->frame_control); | |
640 | } | |
641 | ||
a522550a | 642 | void wl1271_tx_work_locked(struct wl1271 *wl) |
f5fc0f86 | 643 | { |
f5fc0f86 | 644 | struct sk_buff *skb; |
6c6e669e IY |
645 | u32 buf_offset = 0; |
646 | bool sent_packets = false; | |
77ddaa10 EP |
647 | bool had_data = false; |
648 | bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); | |
f5fc0f86 LC |
649 | int ret; |
650 | ||
f5fc0f86 | 651 | if (unlikely(wl->state == WL1271_STATE_OFF)) |
c1b193eb | 652 | return; |
f5fc0f86 | 653 | |
6742f554 | 654 | while ((skb = wl1271_skb_dequeue(wl))) { |
77ddaa10 EP |
655 | if (wl1271_tx_is_data_present(skb)) |
656 | had_data = true; | |
657 | ||
a19606b4 | 658 | ret = wl1271_prepare_tx_frame(wl, skb, buf_offset); |
6c6e669e | 659 | if (ret == -EAGAIN) { |
a19606b4 | 660 | /* |
6c6e669e IY |
661 | * Aggregation buffer is full. |
662 | * Flush buffer and try again. | |
663 | */ | |
6742f554 | 664 | wl1271_skb_queue_head(wl, skb); |
6c6e669e | 665 | wl1271_write(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf, |
6742f554 | 666 | buf_offset, true); |
6c6e669e IY |
667 | sent_packets = true; |
668 | buf_offset = 0; | |
669 | continue; | |
670 | } else if (ret == -EBUSY) { | |
671 | /* | |
672 | * Firmware buffer is full. | |
a19606b4 IY |
673 | * Queue back last skb, and stop aggregating. |
674 | */ | |
6742f554 | 675 | wl1271_skb_queue_head(wl, skb); |
a522550a IY |
676 | /* No work left, avoid scheduling redundant tx work */ |
677 | set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); | |
ffb591cd | 678 | goto out_ack; |
f5fc0f86 LC |
679 | } else if (ret < 0) { |
680 | dev_kfree_skb(skb); | |
ffb591cd | 681 | goto out_ack; |
f5fc0f86 | 682 | } |
a19606b4 IY |
683 | buf_offset += ret; |
684 | wl->tx_packets_count++; | |
f5fc0f86 LC |
685 | } |
686 | ||
ffb591cd | 687 | out_ack: |
a19606b4 IY |
688 | if (buf_offset) { |
689 | wl1271_write(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf, | |
690 | buf_offset, true); | |
6c6e669e IY |
691 | sent_packets = true; |
692 | } | |
693 | if (sent_packets) { | |
606ea9fa IY |
694 | /* |
695 | * Interrupt the firmware with the new packets. This is only | |
696 | * required for older hardware revisions | |
697 | */ | |
698 | if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION) | |
699 | wl1271_write32(wl, WL1271_HOST_WR_ACCESS, | |
700 | wl->tx_packets_count); | |
701 | ||
a8c0ddb5 | 702 | wl1271_handle_tx_low_watermark(wl); |
a19606b4 | 703 | } |
77ddaa10 EP |
704 | if (!is_ap && wl->conf.rx_streaming.interval && had_data && |
705 | (wl->conf.rx_streaming.always || | |
706 | test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) { | |
707 | u32 timeout = wl->conf.rx_streaming.duration; | |
708 | ||
709 | /* enable rx streaming */ | |
710 | if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags)) | |
711 | ieee80211_queue_work(wl->hw, | |
712 | &wl->rx_streaming_enable_work); | |
713 | ||
714 | mod_timer(&wl->rx_streaming_timer, | |
715 | jiffies + msecs_to_jiffies(timeout)); | |
716 | } | |
a522550a | 717 | } |
f5fc0f86 | 718 | |
a522550a IY |
719 | void wl1271_tx_work(struct work_struct *work) |
720 | { | |
721 | struct wl1271 *wl = container_of(work, struct wl1271, tx_work); | |
c1b193eb | 722 | int ret; |
a522550a IY |
723 | |
724 | mutex_lock(&wl->mutex); | |
c1b193eb EP |
725 | ret = wl1271_ps_elp_wakeup(wl); |
726 | if (ret < 0) | |
727 | goto out; | |
728 | ||
a522550a | 729 | wl1271_tx_work_locked(wl); |
c1b193eb | 730 | |
c75bbcdb | 731 | wl1271_ps_elp_sleep(wl); |
c1b193eb | 732 | out: |
f5fc0f86 LC |
733 | mutex_unlock(&wl->mutex); |
734 | } | |
735 | ||
736 | static void wl1271_tx_complete_packet(struct wl1271 *wl, | |
737 | struct wl1271_tx_hw_res_descr *result) | |
738 | { | |
f5fc0f86 LC |
739 | struct ieee80211_tx_info *info; |
740 | struct sk_buff *skb; | |
f5fc0f86 | 741 | int id = result->id; |
31627dc5 JO |
742 | int rate = -1; |
743 | u8 retries = 0; | |
f5fc0f86 LC |
744 | |
745 | /* check for id legality */ | |
ffb591cd | 746 | if (unlikely(id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL)) { |
f5fc0f86 LC |
747 | wl1271_warning("TX result illegal id: %d", id); |
748 | return; | |
749 | } | |
750 | ||
751 | skb = wl->tx_frames[id]; | |
752 | info = IEEE80211_SKB_CB(skb); | |
753 | ||
990f5de7 | 754 | if (wl12xx_is_dummy_packet(wl, skb)) { |
ae47c45f SL |
755 | wl1271_free_tx_id(wl, id); |
756 | return; | |
757 | } | |
758 | ||
31627dc5 JO |
759 | /* update the TX status info */ |
760 | if (result->status == TX_SUCCESS) { | |
761 | if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) | |
f5fc0f86 | 762 | info->flags |= IEEE80211_TX_STAT_ACK; |
6a2de93b | 763 | rate = wl1271_rate_to_idx(result->rate_class_index, wl->band); |
31627dc5 JO |
764 | retries = result->ack_failures; |
765 | } else if (result->status == TX_RETRY_EXCEEDED) { | |
766 | wl->stats.excessive_retries++; | |
767 | retries = result->ack_failures; | |
f5fc0f86 LC |
768 | } |
769 | ||
31627dc5 JO |
770 | info->status.rates[0].idx = rate; |
771 | info->status.rates[0].count = retries; | |
772 | info->status.rates[0].flags = 0; | |
773 | info->status.ack_signal = -1; | |
774 | ||
f5fc0f86 LC |
775 | wl->stats.retry_count += result->ack_failures; |
776 | ||
b992c682 OK |
777 | /* |
778 | * update sequence number only when relevant, i.e. only in | |
779 | * sessions of TKIP, AES and GEM (not in open or WEP sessions) | |
780 | */ | |
781 | if (info->control.hw_key && | |
782 | (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP || | |
783 | info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP || | |
784 | info->control.hw_key->cipher == WL1271_CIPHER_SUITE_GEM)) { | |
785 | u8 fw_lsb = result->tx_security_sequence_number_lsb; | |
786 | u8 cur_lsb = wl->tx_security_last_seq_lsb; | |
787 | ||
788 | /* | |
789 | * update security sequence number, taking care of potential | |
790 | * wrap-around | |
791 | */ | |
792 | wl->tx_security_seq += (fw_lsb - cur_lsb + 256) % 256; | |
793 | wl->tx_security_last_seq_lsb = fw_lsb; | |
794 | } | |
ac4e4ce5 | 795 | |
1e2b7976 JO |
796 | /* remove private header from packet */ |
797 | skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); | |
798 | ||
799 | /* remove TKIP header space if present */ | |
f5fc0f86 | 800 | if (info->control.hw_key && |
97359d12 | 801 | info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { |
1e2b7976 JO |
802 | int hdrlen = ieee80211_get_hdrlen_from_skb(skb); |
803 | memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen); | |
804 | skb_pull(skb, WL1271_TKIP_IV_SPACE); | |
805 | } | |
f5fc0f86 LC |
806 | |
807 | wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" | |
808 | " status 0x%x", | |
809 | result->id, skb, result->ack_failures, | |
810 | result->rate_class_index, result->status); | |
811 | ||
f5fc0f86 | 812 | /* return the packet to the stack */ |
a620865e | 813 | skb_queue_tail(&wl->deferred_tx_queue, skb); |
92ef8960 | 814 | queue_work(wl->freezable_wq, &wl->netstack_work); |
25eeb9e3 | 815 | wl1271_free_tx_id(wl, result->id); |
f5fc0f86 LC |
816 | } |
817 | ||
818 | /* Called upon reception of a TX complete interrupt */ | |
ffb591cd | 819 | void wl1271_tx_complete(struct wl1271 *wl) |
f5fc0f86 LC |
820 | { |
821 | struct wl1271_acx_mem_map *memmap = | |
822 | (struct wl1271_acx_mem_map *)wl->target_mem_map; | |
ffb591cd | 823 | u32 count, fw_counter; |
f5fc0f86 LC |
824 | u32 i; |
825 | ||
f5fc0f86 | 826 | /* read the tx results from the chipset */ |
7b048c52 TP |
827 | wl1271_read(wl, le32_to_cpu(memmap->tx_result), |
828 | wl->tx_res_if, sizeof(*wl->tx_res_if), false); | |
ffb591cd JO |
829 | fw_counter = le32_to_cpu(wl->tx_res_if->tx_result_fw_counter); |
830 | ||
831 | /* write host counter to chipset (to ack) */ | |
832 | wl1271_write32(wl, le32_to_cpu(memmap->tx_result) + | |
833 | offsetof(struct wl1271_tx_hw_res_if, | |
834 | tx_result_host_counter), fw_counter); | |
835 | ||
836 | count = fw_counter - wl->tx_results_count; | |
06f7bc7d | 837 | wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); |
f5fc0f86 LC |
838 | |
839 | /* verify that the result buffer is not getting overrun */ | |
ffb591cd | 840 | if (unlikely(count > TX_HW_RESULT_QUEUE_LEN)) |
f5fc0f86 | 841 | wl1271_warning("TX result overflow from chipset: %d", count); |
f5fc0f86 LC |
842 | |
843 | /* process the results */ | |
844 | for (i = 0; i < count; i++) { | |
845 | struct wl1271_tx_hw_res_descr *result; | |
846 | u8 offset = wl->tx_results_count & TX_HW_RESULT_QUEUE_LEN_MASK; | |
847 | ||
848 | /* process the packet */ | |
849 | result = &(wl->tx_res_if->tx_results_queue[offset]); | |
850 | wl1271_tx_complete_packet(wl, result); | |
851 | ||
852 | wl->tx_results_count++; | |
853 | } | |
f5fc0f86 LC |
854 | } |
855 | ||
a8c0ddb5 AN |
856 | void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) |
857 | { | |
858 | struct sk_buff *skb; | |
f1a46384 | 859 | int i; |
a8c0ddb5 | 860 | unsigned long flags; |
1d36cd89 | 861 | struct ieee80211_tx_info *info; |
f1a46384 | 862 | int total[NUM_TX_QUEUES]; |
a8c0ddb5 AN |
863 | |
864 | for (i = 0; i < NUM_TX_QUEUES; i++) { | |
f1a46384 | 865 | total[i] = 0; |
a8c0ddb5 AN |
866 | while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { |
867 | wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb); | |
79ebec76 AN |
868 | |
869 | if (!wl12xx_is_dummy_packet(wl, skb)) { | |
870 | info = IEEE80211_SKB_CB(skb); | |
871 | info->status.rates[0].idx = -1; | |
872 | info->status.rates[0].count = 0; | |
873 | ieee80211_tx_status_ni(wl->hw, skb); | |
874 | } | |
875 | ||
f1a46384 | 876 | total[i]++; |
a8c0ddb5 AN |
877 | } |
878 | } | |
879 | ||
880 | spin_lock_irqsave(&wl->wl_lock, flags); | |
f1a46384 AN |
881 | for (i = 0; i < NUM_TX_QUEUES; i++) |
882 | wl->tx_queue_count[i] -= total[i]; | |
a8c0ddb5 AN |
883 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
884 | ||
885 | wl1271_handle_tx_low_watermark(wl); | |
886 | } | |
887 | ||
7dece1c8 AN |
888 | /* caller must hold wl->mutex and TX must be stopped */ |
889 | void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues) | |
f5fc0f86 LC |
890 | { |
891 | int i; | |
892 | struct sk_buff *skb; | |
1d36cd89 | 893 | struct ieee80211_tx_info *info; |
f5fc0f86 LC |
894 | |
895 | /* TX failure */ | |
a8c0ddb5 | 896 | if (wl->bss_type == BSS_TYPE_AP_BSS) { |
09039f42 | 897 | for (i = 0; i < AP_MAX_LINKS; i++) { |
f1acea9a | 898 | wl1271_free_sta(wl, i); |
a8c0ddb5 | 899 | wl1271_tx_reset_link_queues(wl, i); |
9b17f1b3 AN |
900 | wl->links[i].allocated_pkts = 0; |
901 | wl->links[i].prev_freed_pkts = 0; | |
09039f42 | 902 | } |
a8c0ddb5 AN |
903 | |
904 | wl->last_tx_hlid = 0; | |
905 | } else { | |
906 | for (i = 0; i < NUM_TX_QUEUES; i++) { | |
907 | while ((skb = skb_dequeue(&wl->tx_queue[i]))) { | |
908 | wl1271_debug(DEBUG_TX, "freeing skb 0x%p", | |
909 | skb); | |
ae47c45f | 910 | |
990f5de7 | 911 | if (!wl12xx_is_dummy_packet(wl, skb)) { |
ae47c45f SL |
912 | info = IEEE80211_SKB_CB(skb); |
913 | info->status.rates[0].idx = -1; | |
914 | info->status.rates[0].count = 0; | |
c27d3acc | 915 | ieee80211_tx_status_ni(wl->hw, skb); |
ae47c45f | 916 | } |
a8c0ddb5 | 917 | } |
6742f554 | 918 | } |
f1acea9a AN |
919 | |
920 | wl->ba_rx_bitmap = 0; | |
f5fc0f86 | 921 | } |
a8c0ddb5 | 922 | |
f1acea9a AN |
923 | for (i = 0; i < NUM_TX_QUEUES; i++) |
924 | wl->tx_queue_count[i] = 0; | |
925 | ||
708bb3cf | 926 | wl->stopped_queues_map = 0; |
f5fc0f86 | 927 | |
2fe33e8c IY |
928 | /* |
929 | * Make sure the driver is at a consistent state, in case this | |
930 | * function is called from a context other than interface removal. | |
7dece1c8 | 931 | * This call will always wake the TX queues. |
2fe33e8c | 932 | */ |
7dece1c8 AN |
933 | if (reset_tx_queues) |
934 | wl1271_handle_tx_low_watermark(wl); | |
2fe33e8c | 935 | |
50e9f746 IY |
936 | for (i = 0; i < ACX_TX_DESCRIPTORS; i++) { |
937 | if (wl->tx_frames[i] == NULL) | |
938 | continue; | |
939 | ||
940 | skb = wl->tx_frames[i]; | |
941 | wl1271_free_tx_id(wl, i); | |
942 | wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); | |
943 | ||
990f5de7 | 944 | if (!wl12xx_is_dummy_packet(wl, skb)) { |
ae47c45f SL |
945 | /* |
946 | * Remove private headers before passing the skb to | |
947 | * mac80211 | |
948 | */ | |
949 | info = IEEE80211_SKB_CB(skb); | |
950 | skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); | |
951 | if (info->control.hw_key && | |
952 | info->control.hw_key->cipher == | |
953 | WLAN_CIPHER_SUITE_TKIP) { | |
954 | int hdrlen = ieee80211_get_hdrlen_from_skb(skb); | |
955 | memmove(skb->data + WL1271_TKIP_IV_SPACE, | |
956 | skb->data, hdrlen); | |
957 | skb_pull(skb, WL1271_TKIP_IV_SPACE); | |
958 | } | |
50e9f746 | 959 | |
ae47c45f SL |
960 | info->status.rates[0].idx = -1; |
961 | info->status.rates[0].count = 0; | |
50e9f746 | 962 | |
c27d3acc | 963 | ieee80211_tx_status_ni(wl->hw, skb); |
ae47c45f | 964 | } |
50e9f746 | 965 | } |
781608c4 JO |
966 | } |
967 | ||
968 | #define WL1271_TX_FLUSH_TIMEOUT 500000 | |
969 | ||
970 | /* caller must *NOT* hold wl->mutex */ | |
971 | void wl1271_tx_flush(struct wl1271 *wl) | |
972 | { | |
973 | unsigned long timeout; | |
974 | timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT); | |
975 | ||
976 | while (!time_after(jiffies, timeout)) { | |
977 | mutex_lock(&wl->mutex); | |
a8c0ddb5 | 978 | wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d", |
f1a46384 AN |
979 | wl->tx_frames_cnt, |
980 | wl1271_tx_total_queue_count(wl)); | |
981 | if ((wl->tx_frames_cnt == 0) && | |
982 | (wl1271_tx_total_queue_count(wl) == 0)) { | |
781608c4 JO |
983 | mutex_unlock(&wl->mutex); |
984 | return; | |
985 | } | |
986 | mutex_unlock(&wl->mutex); | |
987 | msleep(1); | |
988 | } | |
989 | ||
990 | wl1271_warning("Unable to flush all TX buffers, timed out."); | |
f5fc0f86 | 991 | } |
e0fe371b | 992 | |
af7fbb28 | 993 | u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) |
e0fe371b | 994 | { |
af7fbb28 EP |
995 | if (WARN_ON(!rate_set)) |
996 | return 0; | |
e0fe371b | 997 | |
af7fbb28 | 998 | return BIT(__ffs(rate_set)); |
e0fe371b | 999 | } |