diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/cmd.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/cmd.c | 852 |
1 files changed, 852 insertions, 0 deletions
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c new file mode 100644 index 000000000000..0106628aa5a2 --- /dev/null +++ b/drivers/net/wireless/wl12xx/cmd.c | |||
@@ -0,0 +1,852 @@ | |||
1 | /* | ||
2 | * This file is part of wl1271 | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 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/module.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/crc7.h> | ||
27 | #include <linux/spi/spi.h> | ||
28 | #include <linux/etherdevice.h> | ||
29 | #include <linux/ieee80211.h> | ||
30 | #include <linux/slab.h> | ||
31 | |||
32 | #include "wl12xx.h" | ||
33 | #include "reg.h" | ||
34 | #include "io.h" | ||
35 | #include "acx.h" | ||
36 | #include "wl12xx_80211.h" | ||
37 | #include "cmd.h" | ||
38 | #include "event.h" | ||
39 | |||
40 | #define WL1271_CMD_FAST_POLL_COUNT 50 | ||
41 | |||
42 | /* | ||
43 | * send command to firmware | ||
44 | * | ||
45 | * @wl: wl struct | ||
46 | * @id: command id | ||
47 | * @buf: buffer containing the command, must work with dma | ||
48 | * @len: length of the buffer | ||
49 | */ | ||
50 | int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, | ||
51 | size_t res_len) | ||
52 | { | ||
53 | struct wl1271_cmd_header *cmd; | ||
54 | unsigned long timeout; | ||
55 | u32 intr; | ||
56 | int ret = 0; | ||
57 | u16 status; | ||
58 | u16 poll_count = 0; | ||
59 | |||
60 | cmd = buf; | ||
61 | cmd->id = cpu_to_le16(id); | ||
62 | cmd->status = 0; | ||
63 | |||
64 | WARN_ON(len % 4 != 0); | ||
65 | |||
66 | wl1271_write(wl, wl->cmd_box_addr, buf, len, false); | ||
67 | |||
68 | wl1271_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); | ||
69 | |||
70 | timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); | ||
71 | |||
72 | intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); | ||
73 | while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { | ||
74 | if (time_after(jiffies, timeout)) { | ||
75 | wl1271_error("command complete timeout"); | ||
76 | ret = -ETIMEDOUT; | ||
77 | goto out; | ||
78 | } | ||
79 | |||
80 | poll_count++; | ||
81 | if (poll_count < WL1271_CMD_FAST_POLL_COUNT) | ||
82 | udelay(10); | ||
83 | else | ||
84 | msleep(1); | ||
85 | |||
86 | intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); | ||
87 | } | ||
88 | |||
89 | /* read back the status code of the command */ | ||
90 | if (res_len == 0) | ||
91 | res_len = sizeof(struct wl1271_cmd_header); | ||
92 | wl1271_read(wl, wl->cmd_box_addr, cmd, res_len, false); | ||
93 | |||
94 | status = le16_to_cpu(cmd->status); | ||
95 | if (status != CMD_STATUS_SUCCESS) { | ||
96 | wl1271_error("command execute failure %d", status); | ||
97 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | ||
98 | ret = -EIO; | ||
99 | } | ||
100 | |||
101 | wl1271_write32(wl, ACX_REG_INTERRUPT_ACK, | ||
102 | WL1271_ACX_INTR_CMD_COMPLETE); | ||
103 | |||
104 | out: | ||
105 | return ret; | ||
106 | } | ||
107 | |||
108 | int wl1271_cmd_general_parms(struct wl1271 *wl) | ||
109 | { | ||
110 | struct wl1271_general_parms_cmd *gen_parms; | ||
111 | struct wl1271_ini_general_params *gp = &wl->nvs->general_params; | ||
112 | bool answer = false; | ||
113 | int ret; | ||
114 | |||
115 | if (!wl->nvs) | ||
116 | return -ENODEV; | ||
117 | |||
118 | gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); | ||
119 | if (!gen_parms) | ||
120 | return -ENOMEM; | ||
121 | |||
122 | gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM; | ||
123 | |||
124 | memcpy(&gen_parms->general_params, gp, sizeof(*gp)); | ||
125 | |||
126 | if (gp->tx_bip_fem_auto_detect) | ||
127 | answer = true; | ||
128 | |||
129 | ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); | ||
130 | if (ret < 0) { | ||
131 | wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); | ||
132 | goto out; | ||
133 | } | ||
134 | |||
135 | gp->tx_bip_fem_manufacturer = | ||
136 | gen_parms->general_params.tx_bip_fem_manufacturer; | ||
137 | |||
138 | wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n", | ||
139 | answer ? "auto" : "manual", gp->tx_bip_fem_manufacturer); | ||
140 | |||
141 | out: | ||
142 | kfree(gen_parms); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | int wl1271_cmd_radio_parms(struct wl1271 *wl) | ||
147 | { | ||
148 | struct wl1271_radio_parms_cmd *radio_parms; | ||
149 | struct wl1271_ini_general_params *gp = &wl->nvs->general_params; | ||
150 | int ret; | ||
151 | |||
152 | if (!wl->nvs) | ||
153 | return -ENODEV; | ||
154 | |||
155 | radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); | ||
156 | if (!radio_parms) | ||
157 | return -ENOMEM; | ||
158 | |||
159 | radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; | ||
160 | |||
161 | /* 2.4GHz parameters */ | ||
162 | memcpy(&radio_parms->static_params_2, &wl->nvs->stat_radio_params_2, | ||
163 | sizeof(struct wl1271_ini_band_params_2)); | ||
164 | memcpy(&radio_parms->dyn_params_2, | ||
165 | &wl->nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params, | ||
166 | sizeof(struct wl1271_ini_fem_params_2)); | ||
167 | |||
168 | /* 5GHz parameters */ | ||
169 | memcpy(&radio_parms->static_params_5, | ||
170 | &wl->nvs->stat_radio_params_5, | ||
171 | sizeof(struct wl1271_ini_band_params_5)); | ||
172 | memcpy(&radio_parms->dyn_params_5, | ||
173 | &wl->nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params, | ||
174 | sizeof(struct wl1271_ini_fem_params_5)); | ||
175 | |||
176 | wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", | ||
177 | radio_parms, sizeof(*radio_parms)); | ||
178 | |||
179 | ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0); | ||
180 | if (ret < 0) | ||
181 | wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed"); | ||
182 | |||
183 | kfree(radio_parms); | ||
184 | return ret; | ||
185 | } | ||
186 | |||
187 | int wl1271_cmd_ext_radio_parms(struct wl1271 *wl) | ||
188 | { | ||
189 | struct wl1271_ext_radio_parms_cmd *ext_radio_parms; | ||
190 | struct conf_rf_settings *rf = &wl->conf.rf; | ||
191 | int ret; | ||
192 | |||
193 | if (!wl->nvs) | ||
194 | return -ENODEV; | ||
195 | |||
196 | ext_radio_parms = kzalloc(sizeof(*ext_radio_parms), GFP_KERNEL); | ||
197 | if (!ext_radio_parms) | ||
198 | return -ENOMEM; | ||
199 | |||
200 | ext_radio_parms->test.id = TEST_CMD_INI_FILE_RF_EXTENDED_PARAM; | ||
201 | |||
202 | memcpy(ext_radio_parms->tx_per_channel_power_compensation_2, | ||
203 | rf->tx_per_channel_power_compensation_2, | ||
204 | CONF_TX_PWR_COMPENSATION_LEN_2); | ||
205 | memcpy(ext_radio_parms->tx_per_channel_power_compensation_5, | ||
206 | rf->tx_per_channel_power_compensation_5, | ||
207 | CONF_TX_PWR_COMPENSATION_LEN_5); | ||
208 | |||
209 | wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_EXT_RADIO_PARAM: ", | ||
210 | ext_radio_parms, sizeof(*ext_radio_parms)); | ||
211 | |||
212 | ret = wl1271_cmd_test(wl, ext_radio_parms, sizeof(*ext_radio_parms), 0); | ||
213 | if (ret < 0) | ||
214 | wl1271_warning("TEST_CMD_INI_FILE_RF_EXTENDED_PARAM failed"); | ||
215 | |||
216 | kfree(ext_radio_parms); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * Poll the mailbox event field until any of the bits in the mask is set or a | ||
222 | * timeout occurs (WL1271_EVENT_TIMEOUT in msecs) | ||
223 | */ | ||
224 | static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) | ||
225 | { | ||
226 | u32 events_vector, event; | ||
227 | unsigned long timeout; | ||
228 | |||
229 | timeout = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT); | ||
230 | |||
231 | do { | ||
232 | if (time_after(jiffies, timeout)) { | ||
233 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | ||
234 | return -ETIMEDOUT; | ||
235 | } | ||
236 | |||
237 | msleep(1); | ||
238 | |||
239 | /* read from both event fields */ | ||
240 | wl1271_read(wl, wl->mbox_ptr[0], &events_vector, | ||
241 | sizeof(events_vector), false); | ||
242 | event = events_vector & mask; | ||
243 | wl1271_read(wl, wl->mbox_ptr[1], &events_vector, | ||
244 | sizeof(events_vector), false); | ||
245 | event |= events_vector & mask; | ||
246 | } while (!event); | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type) | ||
252 | { | ||
253 | struct wl1271_cmd_join *join; | ||
254 | int ret, i; | ||
255 | u8 *bssid; | ||
256 | |||
257 | join = kzalloc(sizeof(*join), GFP_KERNEL); | ||
258 | if (!join) { | ||
259 | ret = -ENOMEM; | ||
260 | goto out; | ||
261 | } | ||
262 | |||
263 | wl1271_debug(DEBUG_CMD, "cmd join"); | ||
264 | |||
265 | /* Reverse order BSSID */ | ||
266 | bssid = (u8 *) &join->bssid_lsb; | ||
267 | for (i = 0; i < ETH_ALEN; i++) | ||
268 | bssid[i] = wl->bssid[ETH_ALEN - i - 1]; | ||
269 | |||
270 | join->rx_config_options = cpu_to_le32(wl->rx_config); | ||
271 | join->rx_filter_options = cpu_to_le32(wl->rx_filter); | ||
272 | join->bss_type = bss_type; | ||
273 | join->basic_rate_set = cpu_to_le32(wl->basic_rate_set); | ||
274 | |||
275 | if (wl->band == IEEE80211_BAND_5GHZ) | ||
276 | join->bss_type |= WL1271_JOIN_CMD_BSS_TYPE_5GHZ; | ||
277 | |||
278 | join->beacon_interval = cpu_to_le16(wl->beacon_int); | ||
279 | join->dtim_interval = WL1271_DEFAULT_DTIM_PERIOD; | ||
280 | |||
281 | join->channel = wl->channel; | ||
282 | join->ssid_len = wl->ssid_len; | ||
283 | memcpy(join->ssid, wl->ssid, wl->ssid_len); | ||
284 | |||
285 | join->ctrl |= wl->session_counter << WL1271_JOIN_CMD_TX_SESSION_OFFSET; | ||
286 | |||
287 | /* reset TX security counters */ | ||
288 | wl->tx_security_last_seq = 0; | ||
289 | wl->tx_security_seq = 0; | ||
290 | |||
291 | ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join), 0); | ||
292 | if (ret < 0) { | ||
293 | wl1271_error("failed to initiate cmd join"); | ||
294 | goto out_free; | ||
295 | } | ||
296 | |||
297 | ret = wl1271_cmd_wait_for_event(wl, JOIN_EVENT_COMPLETE_ID); | ||
298 | if (ret < 0) | ||
299 | wl1271_error("cmd join event completion error"); | ||
300 | |||
301 | out_free: | ||
302 | kfree(join); | ||
303 | |||
304 | out: | ||
305 | return ret; | ||
306 | } | ||
307 | |||
308 | /** | ||
309 | * send test command to firmware | ||
310 | * | ||
311 | * @wl: wl struct | ||
312 | * @buf: buffer containing the command, with all headers, must work with dma | ||
313 | * @len: length of the buffer | ||
314 | * @answer: is answer needed | ||
315 | */ | ||
316 | int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer) | ||
317 | { | ||
318 | int ret; | ||
319 | size_t res_len = 0; | ||
320 | |||
321 | wl1271_debug(DEBUG_CMD, "cmd test"); | ||
322 | |||
323 | if (answer) | ||
324 | res_len = buf_len; | ||
325 | |||
326 | ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len, res_len); | ||
327 | |||
328 | if (ret < 0) { | ||
329 | wl1271_warning("TEST command failed"); | ||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | return ret; | ||
334 | } | ||
335 | |||
336 | /** | ||
337 | * read acx from firmware | ||
338 | * | ||
339 | * @wl: wl struct | ||
340 | * @id: acx id | ||
341 | * @buf: buffer for the response, including all headers, must work with dma | ||
342 | * @len: lenght of buf | ||
343 | */ | ||
344 | int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) | ||
345 | { | ||
346 | struct acx_header *acx = buf; | ||
347 | int ret; | ||
348 | |||
349 | wl1271_debug(DEBUG_CMD, "cmd interrogate"); | ||
350 | |||
351 | acx->id = cpu_to_le16(id); | ||
352 | |||
353 | /* payload length, does not include any headers */ | ||
354 | acx->len = cpu_to_le16(len - sizeof(*acx)); | ||
355 | |||
356 | ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len); | ||
357 | if (ret < 0) | ||
358 | wl1271_error("INTERROGATE command failed"); | ||
359 | |||
360 | return ret; | ||
361 | } | ||
362 | |||
363 | /** | ||
364 | * write acx value to firmware | ||
365 | * | ||
366 | * @wl: wl struct | ||
367 | * @id: acx id | ||
368 | * @buf: buffer containing acx, including all headers, must work with dma | ||
369 | * @len: length of buf | ||
370 | */ | ||
371 | int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) | ||
372 | { | ||
373 | struct acx_header *acx = buf; | ||
374 | int ret; | ||
375 | |||
376 | wl1271_debug(DEBUG_CMD, "cmd configure"); | ||
377 | |||
378 | acx->id = cpu_to_le16(id); | ||
379 | |||
380 | /* payload length, does not include any headers */ | ||
381 | acx->len = cpu_to_le16(len - sizeof(*acx)); | ||
382 | |||
383 | ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0); | ||
384 | if (ret < 0) { | ||
385 | wl1271_warning("CONFIGURE command NOK"); | ||
386 | return ret; | ||
387 | } | ||
388 | |||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | int wl1271_cmd_data_path(struct wl1271 *wl, bool enable) | ||
393 | { | ||
394 | struct cmd_enabledisable_path *cmd; | ||
395 | int ret; | ||
396 | u16 cmd_rx, cmd_tx; | ||
397 | |||
398 | wl1271_debug(DEBUG_CMD, "cmd data path"); | ||
399 | |||
400 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
401 | if (!cmd) { | ||
402 | ret = -ENOMEM; | ||
403 | goto out; | ||
404 | } | ||
405 | |||
406 | /* the channel here is only used for calibration, so hardcoded to 1 */ | ||
407 | cmd->channel = 1; | ||
408 | |||
409 | if (enable) { | ||
410 | cmd_rx = CMD_ENABLE_RX; | ||
411 | cmd_tx = CMD_ENABLE_TX; | ||
412 | } else { | ||
413 | cmd_rx = CMD_DISABLE_RX; | ||
414 | cmd_tx = CMD_DISABLE_TX; | ||
415 | } | ||
416 | |||
417 | ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd), 0); | ||
418 | if (ret < 0) { | ||
419 | wl1271_error("rx %s cmd for channel %d failed", | ||
420 | enable ? "start" : "stop", cmd->channel); | ||
421 | goto out; | ||
422 | } | ||
423 | |||
424 | wl1271_debug(DEBUG_BOOT, "rx %s cmd channel %d", | ||
425 | enable ? "start" : "stop", cmd->channel); | ||
426 | |||
427 | ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd), 0); | ||
428 | if (ret < 0) { | ||
429 | wl1271_error("tx %s cmd for channel %d failed", | ||
430 | enable ? "start" : "stop", cmd->channel); | ||
431 | goto out; | ||
432 | } | ||
433 | |||
434 | wl1271_debug(DEBUG_BOOT, "tx %s cmd channel %d", | ||
435 | enable ? "start" : "stop", cmd->channel); | ||
436 | |||
437 | out: | ||
438 | kfree(cmd); | ||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, u32 rates, bool send) | ||
443 | { | ||
444 | struct wl1271_cmd_ps_params *ps_params = NULL; | ||
445 | int ret = 0; | ||
446 | |||
447 | wl1271_debug(DEBUG_CMD, "cmd set ps mode"); | ||
448 | |||
449 | ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); | ||
450 | if (!ps_params) { | ||
451 | ret = -ENOMEM; | ||
452 | goto out; | ||
453 | } | ||
454 | |||
455 | ps_params->ps_mode = ps_mode; | ||
456 | ps_params->send_null_data = send; | ||
457 | ps_params->retries = wl->conf.conn.psm_entry_nullfunc_retries; | ||
458 | ps_params->hang_over_period = wl->conf.conn.psm_entry_hangover_period; | ||
459 | ps_params->null_data_rate = cpu_to_le32(rates); | ||
460 | |||
461 | ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params, | ||
462 | sizeof(*ps_params), 0); | ||
463 | if (ret < 0) { | ||
464 | wl1271_error("cmd set_ps_mode failed"); | ||
465 | goto out; | ||
466 | } | ||
467 | |||
468 | out: | ||
469 | kfree(ps_params); | ||
470 | return ret; | ||
471 | } | ||
472 | |||
473 | int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, | ||
474 | void *buf, size_t buf_len, int index, u32 rates) | ||
475 | { | ||
476 | struct wl1271_cmd_template_set *cmd; | ||
477 | int ret = 0; | ||
478 | |||
479 | wl1271_debug(DEBUG_CMD, "cmd template_set %d", template_id); | ||
480 | |||
481 | WARN_ON(buf_len > WL1271_CMD_TEMPL_MAX_SIZE); | ||
482 | buf_len = min_t(size_t, buf_len, WL1271_CMD_TEMPL_MAX_SIZE); | ||
483 | |||
484 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
485 | if (!cmd) { | ||
486 | ret = -ENOMEM; | ||
487 | goto out; | ||
488 | } | ||
489 | |||
490 | cmd->len = cpu_to_le16(buf_len); | ||
491 | cmd->template_type = template_id; | ||
492 | cmd->enabled_rates = cpu_to_le32(rates); | ||
493 | cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit; | ||
494 | cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit; | ||
495 | cmd->index = index; | ||
496 | |||
497 | if (buf) | ||
498 | memcpy(cmd->template_data, buf, buf_len); | ||
499 | |||
500 | ret = wl1271_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd), 0); | ||
501 | if (ret < 0) { | ||
502 | wl1271_warning("cmd set_template failed: %d", ret); | ||
503 | goto out_free; | ||
504 | } | ||
505 | |||
506 | out_free: | ||
507 | kfree(cmd); | ||
508 | |||
509 | out: | ||
510 | return ret; | ||
511 | } | ||
512 | |||
513 | int wl1271_cmd_build_null_data(struct wl1271 *wl) | ||
514 | { | ||
515 | struct sk_buff *skb = NULL; | ||
516 | int size; | ||
517 | void *ptr; | ||
518 | int ret = -ENOMEM; | ||
519 | |||
520 | |||
521 | if (wl->bss_type == BSS_TYPE_IBSS) { | ||
522 | size = sizeof(struct wl12xx_null_data_template); | ||
523 | ptr = NULL; | ||
524 | } else { | ||
525 | skb = ieee80211_nullfunc_get(wl->hw, wl->vif); | ||
526 | if (!skb) | ||
527 | goto out; | ||
528 | size = skb->len; | ||
529 | ptr = skb->data; | ||
530 | } | ||
531 | |||
532 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0, | ||
533 | wl->basic_rate); | ||
534 | |||
535 | out: | ||
536 | dev_kfree_skb(skb); | ||
537 | if (ret) | ||
538 | wl1271_warning("cmd buld null data failed %d", ret); | ||
539 | |||
540 | return ret; | ||
541 | |||
542 | } | ||
543 | |||
544 | int wl1271_cmd_build_klv_null_data(struct wl1271 *wl) | ||
545 | { | ||
546 | struct sk_buff *skb = NULL; | ||
547 | int ret = -ENOMEM; | ||
548 | |||
549 | skb = ieee80211_nullfunc_get(wl->hw, wl->vif); | ||
550 | if (!skb) | ||
551 | goto out; | ||
552 | |||
553 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, | ||
554 | skb->data, skb->len, | ||
555 | CMD_TEMPL_KLV_IDX_NULL_DATA, | ||
556 | wl->basic_rate); | ||
557 | |||
558 | out: | ||
559 | dev_kfree_skb(skb); | ||
560 | if (ret) | ||
561 | wl1271_warning("cmd build klv null data failed %d", ret); | ||
562 | |||
563 | return ret; | ||
564 | |||
565 | } | ||
566 | |||
567 | int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) | ||
568 | { | ||
569 | struct sk_buff *skb; | ||
570 | int ret = 0; | ||
571 | |||
572 | skb = ieee80211_pspoll_get(wl->hw, wl->vif); | ||
573 | if (!skb) | ||
574 | goto out; | ||
575 | |||
576 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data, | ||
577 | skb->len, 0, wl->basic_rate_set); | ||
578 | |||
579 | out: | ||
580 | dev_kfree_skb(skb); | ||
581 | return ret; | ||
582 | } | ||
583 | |||
584 | int wl1271_cmd_build_probe_req(struct wl1271 *wl, | ||
585 | const u8 *ssid, size_t ssid_len, | ||
586 | const u8 *ie, size_t ie_len, u8 band) | ||
587 | { | ||
588 | struct sk_buff *skb; | ||
589 | int ret; | ||
590 | |||
591 | skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, | ||
592 | ie, ie_len); | ||
593 | if (!skb) { | ||
594 | ret = -ENOMEM; | ||
595 | goto out; | ||
596 | } | ||
597 | |||
598 | wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); | ||
599 | |||
600 | if (band == IEEE80211_BAND_2GHZ) | ||
601 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, | ||
602 | skb->data, skb->len, 0, | ||
603 | wl->conf.tx.basic_rate); | ||
604 | else | ||
605 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, | ||
606 | skb->data, skb->len, 0, | ||
607 | wl->conf.tx.basic_rate_5); | ||
608 | |||
609 | out: | ||
610 | dev_kfree_skb(skb); | ||
611 | return ret; | ||
612 | } | ||
613 | |||
614 | struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, | ||
615 | struct sk_buff *skb) | ||
616 | { | ||
617 | int ret; | ||
618 | |||
619 | if (!skb) | ||
620 | skb = ieee80211_ap_probereq_get(wl->hw, wl->vif); | ||
621 | if (!skb) | ||
622 | goto out; | ||
623 | |||
624 | wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len); | ||
625 | |||
626 | if (wl->band == IEEE80211_BAND_2GHZ) | ||
627 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, | ||
628 | skb->data, skb->len, 0, | ||
629 | wl->conf.tx.basic_rate); | ||
630 | else | ||
631 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, | ||
632 | skb->data, skb->len, 0, | ||
633 | wl->conf.tx.basic_rate_5); | ||
634 | |||
635 | if (ret < 0) | ||
636 | wl1271_error("Unable to set ap probe request template."); | ||
637 | |||
638 | out: | ||
639 | return skb; | ||
640 | } | ||
641 | |||
642 | int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr) | ||
643 | { | ||
644 | int ret; | ||
645 | struct wl12xx_arp_rsp_template tmpl; | ||
646 | struct ieee80211_hdr_3addr *hdr; | ||
647 | struct arphdr *arp_hdr; | ||
648 | |||
649 | memset(&tmpl, 0, sizeof(tmpl)); | ||
650 | |||
651 | /* mac80211 header */ | ||
652 | hdr = &tmpl.hdr; | ||
653 | hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||
654 | IEEE80211_STYPE_DATA | | ||
655 | IEEE80211_FCTL_TODS); | ||
656 | memcpy(hdr->addr1, wl->vif->bss_conf.bssid, ETH_ALEN); | ||
657 | memcpy(hdr->addr2, wl->vif->addr, ETH_ALEN); | ||
658 | memset(hdr->addr3, 0xff, ETH_ALEN); | ||
659 | |||
660 | /* llc layer */ | ||
661 | memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header)); | ||
662 | tmpl.llc_type = htons(ETH_P_ARP); | ||
663 | |||
664 | /* arp header */ | ||
665 | arp_hdr = &tmpl.arp_hdr; | ||
666 | arp_hdr->ar_hrd = htons(ARPHRD_ETHER); | ||
667 | arp_hdr->ar_pro = htons(ETH_P_IP); | ||
668 | arp_hdr->ar_hln = ETH_ALEN; | ||
669 | arp_hdr->ar_pln = 4; | ||
670 | arp_hdr->ar_op = htons(ARPOP_REPLY); | ||
671 | |||
672 | /* arp payload */ | ||
673 | memcpy(tmpl.sender_hw, wl->vif->addr, ETH_ALEN); | ||
674 | tmpl.sender_ip = ip_addr; | ||
675 | |||
676 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, | ||
677 | &tmpl, sizeof(tmpl), 0, | ||
678 | wl->basic_rate); | ||
679 | |||
680 | return ret; | ||
681 | } | ||
682 | |||
683 | int wl1271_build_qos_null_data(struct wl1271 *wl) | ||
684 | { | ||
685 | struct ieee80211_qos_hdr template; | ||
686 | |||
687 | memset(&template, 0, sizeof(template)); | ||
688 | |||
689 | memcpy(template.addr1, wl->bssid, ETH_ALEN); | ||
690 | memcpy(template.addr2, wl->mac_addr, ETH_ALEN); | ||
691 | memcpy(template.addr3, wl->bssid, ETH_ALEN); | ||
692 | |||
693 | template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||
694 | IEEE80211_STYPE_QOS_NULLFUNC | | ||
695 | IEEE80211_FCTL_TODS); | ||
696 | |||
697 | /* FIXME: not sure what priority to use here */ | ||
698 | template.qos_ctrl = cpu_to_le16(0); | ||
699 | |||
700 | return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template, | ||
701 | sizeof(template), 0, | ||
702 | wl->basic_rate); | ||
703 | } | ||
704 | |||
705 | int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) | ||
706 | { | ||
707 | struct wl1271_cmd_set_keys *cmd; | ||
708 | int ret = 0; | ||
709 | |||
710 | wl1271_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id); | ||
711 | |||
712 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
713 | if (!cmd) { | ||
714 | ret = -ENOMEM; | ||
715 | goto out; | ||
716 | } | ||
717 | |||
718 | cmd->id = id; | ||
719 | cmd->key_action = cpu_to_le16(KEY_SET_ID); | ||
720 | cmd->key_type = KEY_WEP; | ||
721 | |||
722 | ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); | ||
723 | if (ret < 0) { | ||
724 | wl1271_warning("cmd set_default_wep_key failed: %d", ret); | ||
725 | goto out; | ||
726 | } | ||
727 | |||
728 | out: | ||
729 | kfree(cmd); | ||
730 | |||
731 | return ret; | ||
732 | } | ||
733 | |||
734 | int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, | ||
735 | u8 key_size, const u8 *key, const u8 *addr, | ||
736 | u32 tx_seq_32, u16 tx_seq_16) | ||
737 | { | ||
738 | struct wl1271_cmd_set_keys *cmd; | ||
739 | int ret = 0; | ||
740 | |||
741 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
742 | if (!cmd) { | ||
743 | ret = -ENOMEM; | ||
744 | goto out; | ||
745 | } | ||
746 | |||
747 | if (key_type != KEY_WEP) | ||
748 | memcpy(cmd->addr, addr, ETH_ALEN); | ||
749 | |||
750 | cmd->key_action = cpu_to_le16(action); | ||
751 | cmd->key_size = key_size; | ||
752 | cmd->key_type = key_type; | ||
753 | |||
754 | cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16); | ||
755 | cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32); | ||
756 | |||
757 | /* we have only one SSID profile */ | ||
758 | cmd->ssid_profile = 0; | ||
759 | |||
760 | cmd->id = id; | ||
761 | |||
762 | if (key_type == KEY_TKIP) { | ||
763 | /* | ||
764 | * We get the key in the following form: | ||
765 | * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) | ||
766 | * but the target is expecting: | ||
767 | * TKIP - RX MIC - TX MIC | ||
768 | */ | ||
769 | memcpy(cmd->key, key, 16); | ||
770 | memcpy(cmd->key + 16, key + 24, 8); | ||
771 | memcpy(cmd->key + 24, key + 16, 8); | ||
772 | |||
773 | } else { | ||
774 | memcpy(cmd->key, key, key_size); | ||
775 | } | ||
776 | |||
777 | wl1271_dump(DEBUG_CRYPT, "TARGET KEY: ", cmd, sizeof(*cmd)); | ||
778 | |||
779 | ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); | ||
780 | if (ret < 0) { | ||
781 | wl1271_warning("could not set keys"); | ||
782 | goto out; | ||
783 | } | ||
784 | |||
785 | out: | ||
786 | kfree(cmd); | ||
787 | |||
788 | return ret; | ||
789 | } | ||
790 | |||
791 | int wl1271_cmd_disconnect(struct wl1271 *wl) | ||
792 | { | ||
793 | struct wl1271_cmd_disconnect *cmd; | ||
794 | int ret = 0; | ||
795 | |||
796 | wl1271_debug(DEBUG_CMD, "cmd disconnect"); | ||
797 | |||
798 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
799 | if (!cmd) { | ||
800 | ret = -ENOMEM; | ||
801 | goto out; | ||
802 | } | ||
803 | |||
804 | cmd->rx_config_options = cpu_to_le32(wl->rx_config); | ||
805 | cmd->rx_filter_options = cpu_to_le32(wl->rx_filter); | ||
806 | /* disconnect reason is not used in immediate disconnections */ | ||
807 | cmd->type = DISCONNECT_IMMEDIATE; | ||
808 | |||
809 | ret = wl1271_cmd_send(wl, CMD_DISCONNECT, cmd, sizeof(*cmd), 0); | ||
810 | if (ret < 0) { | ||
811 | wl1271_error("failed to send disconnect command"); | ||
812 | goto out_free; | ||
813 | } | ||
814 | |||
815 | ret = wl1271_cmd_wait_for_event(wl, DISCONNECT_EVENT_COMPLETE_ID); | ||
816 | if (ret < 0) | ||
817 | wl1271_error("cmd disconnect event completion error"); | ||
818 | |||
819 | out_free: | ||
820 | kfree(cmd); | ||
821 | |||
822 | out: | ||
823 | return ret; | ||
824 | } | ||
825 | |||
826 | int wl1271_cmd_set_sta_state(struct wl1271 *wl) | ||
827 | { | ||
828 | struct wl1271_cmd_set_sta_state *cmd; | ||
829 | int ret = 0; | ||
830 | |||
831 | wl1271_debug(DEBUG_CMD, "cmd set sta state"); | ||
832 | |||
833 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
834 | if (!cmd) { | ||
835 | ret = -ENOMEM; | ||
836 | goto out; | ||
837 | } | ||
838 | |||
839 | cmd->state = WL1271_CMD_STA_STATE_CONNECTED; | ||
840 | |||
841 | ret = wl1271_cmd_send(wl, CMD_SET_STA_STATE, cmd, sizeof(*cmd), 0); | ||
842 | if (ret < 0) { | ||
843 | wl1271_error("failed to send set STA state command"); | ||
844 | goto out_free; | ||
845 | } | ||
846 | |||
847 | out_free: | ||
848 | kfree(cmd); | ||
849 | |||
850 | out: | ||
851 | return ret; | ||
852 | } | ||