diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/wl1271_tx.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_tx.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c new file mode 100644 index 000000000000..ff221258b941 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_tx.c | |||
@@ -0,0 +1,378 @@ | |||
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> | ||
26 | |||
27 | #include "wl1271.h" | ||
28 | #include "wl1271_spi.h" | ||
29 | #include "wl1271_reg.h" | ||
30 | #include "wl1271_ps.h" | ||
31 | #include "wl1271_tx.h" | ||
32 | |||
33 | static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb) | ||
34 | { | ||
35 | int i; | ||
36 | |||
37 | for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) | ||
38 | if (wl->tx_frames[i] == NULL) { | ||
39 | wl->tx_frames[i] = skb; | ||
40 | return i; | ||
41 | } | ||
42 | |||
43 | return -EBUSY; | ||
44 | } | ||
45 | |||
46 | static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra) | ||
47 | { | ||
48 | struct wl1271_tx_hw_descr *desc; | ||
49 | u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; | ||
50 | u32 total_blocks, excluded; | ||
51 | int id, ret = -EBUSY; | ||
52 | |||
53 | /* allocate free identifier for the packet */ | ||
54 | id = wl1271_tx_id(wl, skb); | ||
55 | if (id < 0) | ||
56 | return id; | ||
57 | |||
58 | /* approximate the number of blocks required for this packet | ||
59 | in the firmware */ | ||
60 | /* FIXME: try to figure out what is done here and make it cleaner */ | ||
61 | total_blocks = (skb->len) >> TX_HW_BLOCK_SHIFT_DIV; | ||
62 | excluded = (total_blocks << 2) + (skb->len & 0xff) + 34; | ||
63 | total_blocks += (excluded > 252) ? 2 : 1; | ||
64 | total_blocks += TX_HW_BLOCK_SPARE; | ||
65 | |||
66 | if (total_blocks <= wl->tx_blocks_available) { | ||
67 | desc = (struct wl1271_tx_hw_descr *)skb_push( | ||
68 | skb, total_len - skb->len); | ||
69 | |||
70 | desc->extra_mem_blocks = TX_HW_BLOCK_SPARE; | ||
71 | desc->total_mem_blocks = total_blocks; | ||
72 | desc->id = id; | ||
73 | |||
74 | wl->tx_blocks_available -= total_blocks; | ||
75 | |||
76 | ret = 0; | ||
77 | |||
78 | wl1271_debug(DEBUG_TX, | ||
79 | "tx_allocate: size: %d, blocks: %d, id: %d", | ||
80 | total_len, total_blocks, id); | ||
81 | } else | ||
82 | wl->tx_frames[id] = NULL; | ||
83 | |||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, | ||
88 | u32 extra, struct ieee80211_tx_info *control) | ||
89 | { | ||
90 | struct wl1271_tx_hw_descr *desc; | ||
91 | int pad; | ||
92 | |||
93 | desc = (struct wl1271_tx_hw_descr *) skb->data; | ||
94 | |||
95 | /* configure packet life time */ | ||
96 | desc->start_time = jiffies_to_usecs(jiffies) - wl->time_offset; | ||
97 | desc->life_time = TX_HW_MGMT_PKT_LIFETIME_TU; | ||
98 | |||
99 | /* configure the tx attributes */ | ||
100 | desc->tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; | ||
101 | /* FIXME: do we know the packet priority? can we identify mgmt | ||
102 | packets, and use max prio for them at least? */ | ||
103 | desc->tid = 0; | ||
104 | desc->aid = TX_HW_DEFAULT_AID; | ||
105 | desc->reserved = 0; | ||
106 | |||
107 | /* align the length (and store in terms of words) */ | ||
108 | pad = WL1271_TX_ALIGN(skb->len); | ||
109 | desc->length = pad >> 2; | ||
110 | |||
111 | /* calculate number of padding bytes */ | ||
112 | pad = pad - skb->len; | ||
113 | desc->tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; | ||
114 | |||
115 | wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad); | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb, | ||
120 | struct ieee80211_tx_info *control) | ||
121 | { | ||
122 | |||
123 | struct wl1271_tx_hw_descr *desc; | ||
124 | int len; | ||
125 | |||
126 | /* FIXME: This is a workaround for getting non-aligned packets. | ||
127 | This happens at least with EAPOL packets from the user space. | ||
128 | Our DMA requires packets to be aligned on a 4-byte boundary. | ||
129 | */ | ||
130 | if (unlikely((long)skb->data & 0x03)) { | ||
131 | int offset = (4 - (long)skb->data) & 0x03; | ||
132 | wl1271_debug(DEBUG_TX, "skb offset %d", offset); | ||
133 | |||
134 | /* check whether the current skb can be used */ | ||
135 | if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) { | ||
136 | unsigned char *src = skb->data; | ||
137 | |||
138 | /* align the buffer on a 4-byte boundary */ | ||
139 | skb_reserve(skb, offset); | ||
140 | memmove(skb->data, src, skb->len); | ||
141 | } else { | ||
142 | wl1271_info("No handler, fixme!"); | ||
143 | return -EINVAL; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | len = WL1271_TX_ALIGN(skb->len); | ||
148 | |||
149 | /* perform a fixed address block write with the packet */ | ||
150 | wl1271_spi_reg_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); | ||
151 | |||
152 | /* write packet new counter into the write access register */ | ||
153 | wl->tx_packets_count++; | ||
154 | wl1271_reg_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); | ||
155 | |||
156 | desc = (struct wl1271_tx_hw_descr *) skb->data; | ||
157 | wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)", | ||
158 | desc->id, skb, len, desc->length); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /* caller must hold wl->mutex */ | ||
164 | static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb) | ||
165 | { | ||
166 | struct ieee80211_tx_info *info; | ||
167 | u32 extra = 0; | ||
168 | int ret = 0; | ||
169 | u8 idx; | ||
170 | |||
171 | if (!skb) | ||
172 | return -EINVAL; | ||
173 | |||
174 | info = IEEE80211_SKB_CB(skb); | ||
175 | |||
176 | if (info->control.hw_key && | ||
177 | info->control.hw_key->alg == ALG_TKIP) | ||
178 | extra = WL1271_TKIP_IV_SPACE; | ||
179 | |||
180 | if (info->control.hw_key) { | ||
181 | idx = info->control.hw_key->hw_key_idx; | ||
182 | |||
183 | /* FIXME: do we have to do this if we're not using WEP? */ | ||
184 | if (unlikely(wl->default_key != idx)) { | ||
185 | ret = wl1271_cmd_set_default_wep_key(wl, idx); | ||
186 | if (ret < 0) | ||
187 | return ret; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | ret = wl1271_tx_allocate(wl, skb, extra); | ||
192 | if (ret < 0) | ||
193 | return ret; | ||
194 | |||
195 | ret = wl1271_tx_fill_hdr(wl, skb, extra, info); | ||
196 | if (ret < 0) | ||
197 | return ret; | ||
198 | |||
199 | ret = wl1271_tx_send_packet(wl, skb, info); | ||
200 | if (ret < 0) | ||
201 | return ret; | ||
202 | |||
203 | return ret; | ||
204 | } | ||
205 | |||
206 | void wl1271_tx_work(struct work_struct *work) | ||
207 | { | ||
208 | struct wl1271 *wl = container_of(work, struct wl1271, tx_work); | ||
209 | struct sk_buff *skb; | ||
210 | bool woken_up = false; | ||
211 | int ret; | ||
212 | |||
213 | mutex_lock(&wl->mutex); | ||
214 | |||
215 | if (unlikely(wl->state == WL1271_STATE_OFF)) | ||
216 | goto out; | ||
217 | |||
218 | while ((skb = skb_dequeue(&wl->tx_queue))) { | ||
219 | if (!woken_up) { | ||
220 | ret = wl1271_ps_elp_wakeup(wl, false); | ||
221 | if (ret < 0) | ||
222 | goto out; | ||
223 | woken_up = true; | ||
224 | } | ||
225 | |||
226 | ret = wl1271_tx_frame(wl, skb); | ||
227 | if (ret == -EBUSY) { | ||
228 | /* firmware buffer is full, stop queues */ | ||
229 | wl1271_debug(DEBUG_TX, "tx_work: fw buffer full, " | ||
230 | "stop queues"); | ||
231 | ieee80211_stop_queues(wl->hw); | ||
232 | wl->tx_queue_stopped = true; | ||
233 | skb_queue_head(&wl->tx_queue, skb); | ||
234 | goto out; | ||
235 | } else if (ret < 0) { | ||
236 | dev_kfree_skb(skb); | ||
237 | goto out; | ||
238 | } else if (wl->tx_queue_stopped) { | ||
239 | /* firmware buffer has space, restart queues */ | ||
240 | wl1271_debug(DEBUG_TX, | ||
241 | "complete_packet: waking queues"); | ||
242 | ieee80211_wake_queues(wl->hw); | ||
243 | wl->tx_queue_stopped = false; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | out: | ||
248 | if (woken_up) | ||
249 | wl1271_ps_elp_sleep(wl); | ||
250 | |||
251 | mutex_unlock(&wl->mutex); | ||
252 | } | ||
253 | |||
254 | static void wl1271_tx_complete_packet(struct wl1271 *wl, | ||
255 | struct wl1271_tx_hw_res_descr *result) | ||
256 | { | ||
257 | |||
258 | struct ieee80211_tx_info *info; | ||
259 | struct sk_buff *skb; | ||
260 | u32 header_len; | ||
261 | int id = result->id; | ||
262 | |||
263 | /* check for id legality */ | ||
264 | if (id >= TX_HW_RESULT_QUEUE_LEN || wl->tx_frames[id] == NULL) { | ||
265 | wl1271_warning("TX result illegal id: %d", id); | ||
266 | return; | ||
267 | } | ||
268 | |||
269 | skb = wl->tx_frames[id]; | ||
270 | info = IEEE80211_SKB_CB(skb); | ||
271 | |||
272 | /* update packet status */ | ||
273 | if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { | ||
274 | if (result->status == TX_SUCCESS) | ||
275 | info->flags |= IEEE80211_TX_STAT_ACK; | ||
276 | if (result->status & TX_RETRY_EXCEEDED) { | ||
277 | /* FIXME */ | ||
278 | /* info->status.excessive_retries = 1; */ | ||
279 | wl->stats.excessive_retries++; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /* FIXME */ | ||
284 | /* info->status.retry_count = result->ack_failures; */ | ||
285 | wl->stats.retry_count += result->ack_failures; | ||
286 | |||
287 | /* get header len */ | ||
288 | if (info->control.hw_key && | ||
289 | info->control.hw_key->alg == ALG_TKIP) | ||
290 | header_len = WL1271_TKIP_IV_SPACE + | ||
291 | sizeof(struct wl1271_tx_hw_descr); | ||
292 | else | ||
293 | header_len = sizeof(struct wl1271_tx_hw_descr); | ||
294 | |||
295 | wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" | ||
296 | " status 0x%x", | ||
297 | result->id, skb, result->ack_failures, | ||
298 | result->rate_class_index, result->status); | ||
299 | |||
300 | /* remove private header from packet */ | ||
301 | skb_pull(skb, header_len); | ||
302 | |||
303 | /* return the packet to the stack */ | ||
304 | ieee80211_tx_status(wl->hw, skb); | ||
305 | wl->tx_frames[result->id] = NULL; | ||
306 | } | ||
307 | |||
308 | /* Called upon reception of a TX complete interrupt */ | ||
309 | void wl1271_tx_complete(struct wl1271 *wl, u32 count) | ||
310 | { | ||
311 | struct wl1271_acx_mem_map *memmap = | ||
312 | (struct wl1271_acx_mem_map *)wl->target_mem_map; | ||
313 | u32 i; | ||
314 | |||
315 | wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); | ||
316 | |||
317 | /* read the tx results from the chipset */ | ||
318 | wl1271_spi_mem_read(wl, memmap->tx_result, | ||
319 | wl->tx_res_if, sizeof(*wl->tx_res_if)); | ||
320 | |||
321 | /* verify that the result buffer is not getting overrun */ | ||
322 | if (count > TX_HW_RESULT_QUEUE_LEN) { | ||
323 | wl1271_warning("TX result overflow from chipset: %d", count); | ||
324 | count = TX_HW_RESULT_QUEUE_LEN; | ||
325 | } | ||
326 | |||
327 | /* process the results */ | ||
328 | for (i = 0; i < count; i++) { | ||
329 | struct wl1271_tx_hw_res_descr *result; | ||
330 | u8 offset = wl->tx_results_count & TX_HW_RESULT_QUEUE_LEN_MASK; | ||
331 | |||
332 | /* process the packet */ | ||
333 | result = &(wl->tx_res_if->tx_results_queue[offset]); | ||
334 | wl1271_tx_complete_packet(wl, result); | ||
335 | |||
336 | wl->tx_results_count++; | ||
337 | } | ||
338 | |||
339 | /* write host counter to chipset (to ack) */ | ||
340 | wl1271_mem_write32(wl, memmap->tx_result + | ||
341 | offsetof(struct wl1271_tx_hw_res_if, | ||
342 | tx_result_host_counter), | ||
343 | wl->tx_res_if->tx_result_fw_counter); | ||
344 | } | ||
345 | |||
346 | /* caller must hold wl->mutex */ | ||
347 | void wl1271_tx_flush(struct wl1271 *wl) | ||
348 | { | ||
349 | int i; | ||
350 | struct sk_buff *skb; | ||
351 | struct ieee80211_tx_info *info; | ||
352 | |||
353 | /* TX failure */ | ||
354 | /* control->flags = 0; FIXME */ | ||
355 | |||
356 | while ((skb = skb_dequeue(&wl->tx_queue))) { | ||
357 | info = IEEE80211_SKB_CB(skb); | ||
358 | |||
359 | wl1271_debug(DEBUG_TX, "flushing skb 0x%p", skb); | ||
360 | |||
361 | if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) | ||
362 | continue; | ||
363 | |||
364 | ieee80211_tx_status(wl->hw, skb); | ||
365 | } | ||
366 | |||
367 | for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) | ||
368 | if (wl->tx_frames[i] != NULL) { | ||
369 | skb = wl->tx_frames[i]; | ||
370 | info = IEEE80211_SKB_CB(skb); | ||
371 | |||
372 | if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) | ||
373 | continue; | ||
374 | |||
375 | ieee80211_tx_status(wl->hw, skb); | ||
376 | wl->tx_frames[i] = NULL; | ||
377 | } | ||
378 | } | ||