]>
Commit | Line | Data |
---|---|---|
2f01a1f5 KV |
1 | /* |
2 | * This file is part of wl12xx | |
3 | * | |
4 | * Copyright (C) 2008-2009 Nokia Corporation | |
5 | * | |
6 | * Contact: Kalle Valo <kalle.valo@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/interrupt.h> | |
26 | #include <linux/firmware.h> | |
27 | #include <linux/delay.h> | |
28 | #include <linux/irq.h> | |
29 | #include <linux/spi/spi.h> | |
30 | #include <linux/crc32.h> | |
31 | #include <linux/etherdevice.h> | |
32 | #include <linux/spi/wl12xx.h> | |
33 | ||
34 | #include "wl12xx.h" | |
35 | #include "wl12xx_80211.h" | |
36 | #include "reg.h" | |
37 | #include "wl1251.h" | |
38 | #include "spi.h" | |
39 | #include "event.h" | |
9f2ad4fb | 40 | #include "wl1251_tx.h" |
2f01a1f5 KV |
41 | #include "rx.h" |
42 | #include "ps.h" | |
43 | #include "init.h" | |
44 | #include "debugfs.h" | |
45 | ||
46 | static void wl12xx_disable_interrupts(struct wl12xx *wl) | |
47 | { | |
48 | disable_irq(wl->irq); | |
49 | } | |
50 | ||
51 | static void wl12xx_power_off(struct wl12xx *wl) | |
52 | { | |
53 | wl->set_power(false); | |
54 | } | |
55 | ||
56 | static void wl12xx_power_on(struct wl12xx *wl) | |
57 | { | |
58 | wl->set_power(true); | |
59 | } | |
60 | ||
61 | static irqreturn_t wl12xx_irq(int irq, void *cookie) | |
62 | { | |
63 | struct wl12xx *wl; | |
64 | ||
65 | wl12xx_debug(DEBUG_IRQ, "IRQ"); | |
66 | ||
67 | wl = cookie; | |
68 | ||
69 | schedule_work(&wl->irq_work); | |
70 | ||
71 | return IRQ_HANDLED; | |
72 | } | |
73 | ||
74 | static int wl12xx_fetch_firmware(struct wl12xx *wl) | |
75 | { | |
76 | const struct firmware *fw; | |
77 | int ret; | |
78 | ||
79 | ret = request_firmware(&fw, wl->chip.fw_filename, &wl->spi->dev); | |
80 | ||
81 | if (ret < 0) { | |
82 | wl12xx_error("could not get firmware: %d", ret); | |
83 | return ret; | |
84 | } | |
85 | ||
86 | if (fw->size % 4) { | |
8bce6121 | 87 | wl12xx_error("firmware size is not multiple of 32 bits: %zu", |
2f01a1f5 KV |
88 | fw->size); |
89 | ret = -EILSEQ; | |
90 | goto out; | |
91 | } | |
92 | ||
93 | wl->fw_len = fw->size; | |
94 | wl->fw = kmalloc(wl->fw_len, GFP_KERNEL); | |
95 | ||
96 | if (!wl->fw) { | |
97 | wl12xx_error("could not allocate memory for the firmware"); | |
98 | ret = -ENOMEM; | |
99 | goto out; | |
100 | } | |
101 | ||
102 | memcpy(wl->fw, fw->data, wl->fw_len); | |
103 | ||
104 | ret = 0; | |
105 | ||
106 | out: | |
107 | release_firmware(fw); | |
108 | ||
109 | return ret; | |
110 | } | |
111 | ||
112 | static int wl12xx_fetch_nvs(struct wl12xx *wl) | |
113 | { | |
114 | const struct firmware *fw; | |
115 | int ret; | |
116 | ||
117 | ret = request_firmware(&fw, wl->chip.nvs_filename, &wl->spi->dev); | |
118 | ||
119 | if (ret < 0) { | |
120 | wl12xx_error("could not get nvs file: %d", ret); | |
121 | return ret; | |
122 | } | |
123 | ||
124 | if (fw->size % 4) { | |
8bce6121 | 125 | wl12xx_error("nvs size is not multiple of 32 bits: %zu", |
2f01a1f5 KV |
126 | fw->size); |
127 | ret = -EILSEQ; | |
128 | goto out; | |
129 | } | |
130 | ||
131 | wl->nvs_len = fw->size; | |
132 | wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL); | |
133 | ||
134 | if (!wl->nvs) { | |
135 | wl12xx_error("could not allocate memory for the nvs file"); | |
136 | ret = -ENOMEM; | |
137 | goto out; | |
138 | } | |
139 | ||
140 | memcpy(wl->nvs, fw->data, wl->nvs_len); | |
141 | ||
142 | ret = 0; | |
143 | ||
144 | out: | |
145 | release_firmware(fw); | |
146 | ||
147 | return ret; | |
148 | } | |
149 | ||
150 | static void wl12xx_fw_wakeup(struct wl12xx *wl) | |
151 | { | |
152 | u32 elp_reg; | |
153 | ||
154 | elp_reg = ELPCTRL_WAKE_UP; | |
155 | wl12xx_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); | |
156 | elp_reg = wl12xx_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); | |
157 | ||
158 | if (!(elp_reg & ELPCTRL_WLAN_READY)) { | |
159 | wl12xx_warning("WLAN not ready"); | |
2f01a1f5 KV |
160 | } |
161 | } | |
162 | ||
163 | static int wl12xx_chip_wakeup(struct wl12xx *wl) | |
164 | { | |
165 | int ret = 0; | |
166 | ||
167 | wl12xx_power_on(wl); | |
168 | msleep(wl->chip.power_on_sleep); | |
169 | wl12xx_spi_reset(wl); | |
170 | wl12xx_spi_init(wl); | |
171 | ||
172 | /* We don't need a real memory partition here, because we only want | |
173 | * to use the registers at this point. */ | |
174 | wl12xx_set_partition(wl, | |
175 | 0x00000000, | |
176 | 0x00000000, | |
177 | REGISTERS_BASE, | |
178 | REGISTERS_DOWN_SIZE); | |
179 | ||
180 | /* ELP module wake up */ | |
181 | wl12xx_fw_wakeup(wl); | |
182 | ||
183 | /* whal_FwCtrl_BootSm() */ | |
184 | ||
185 | /* 0. read chip id from CHIP_ID */ | |
186 | wl->chip.id = wl12xx_reg_read32(wl, CHIP_ID_B); | |
187 | ||
188 | /* 1. check if chip id is valid */ | |
189 | ||
190 | switch (wl->chip.id) { | |
191 | case CHIP_ID_1251_PG12: | |
192 | wl12xx_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)", | |
193 | wl->chip.id); | |
194 | ||
195 | wl1251_setup(wl); | |
196 | ||
197 | break; | |
198 | case CHIP_ID_1271_PG10: | |
27797d68 LC |
199 | wl12xx_warning("chip id 0x%x (1271 PG10) support is obsolete", |
200 | wl->chip.id); | |
201 | break; | |
202 | case CHIP_ID_1271_PG20: | |
203 | wl12xx_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", | |
204 | wl->chip.id); | |
205 | break; | |
2f01a1f5 KV |
206 | case CHIP_ID_1251_PG10: |
207 | case CHIP_ID_1251_PG11: | |
208 | default: | |
209 | wl12xx_error("unsupported chip id: 0x%x", wl->chip.id); | |
210 | ret = -ENODEV; | |
211 | goto out; | |
212 | } | |
213 | ||
214 | if (wl->fw == NULL) { | |
215 | ret = wl12xx_fetch_firmware(wl); | |
216 | if (ret < 0) | |
217 | goto out; | |
218 | } | |
219 | ||
220 | /* No NVS from netlink, try to get it from the filesystem */ | |
221 | if (wl->nvs == NULL) { | |
222 | ret = wl12xx_fetch_nvs(wl); | |
223 | if (ret < 0) | |
224 | goto out; | |
225 | } | |
226 | ||
227 | out: | |
228 | return ret; | |
229 | } | |
230 | ||
231 | static void wl12xx_filter_work(struct work_struct *work) | |
232 | { | |
233 | struct wl12xx *wl = | |
234 | container_of(work, struct wl12xx, filter_work); | |
235 | int ret; | |
236 | ||
237 | mutex_lock(&wl->mutex); | |
238 | ||
239 | if (wl->state == WL12XX_STATE_OFF) | |
240 | goto out; | |
241 | ||
01d9cfbd KV |
242 | wl12xx_ps_elp_wakeup(wl); |
243 | ||
2f01a1f5 KV |
244 | ret = wl12xx_cmd_join(wl, wl->bss_type, 1, 100, 0); |
245 | if (ret < 0) | |
246 | goto out; | |
247 | ||
248 | out: | |
01d9cfbd | 249 | wl12xx_ps_elp_sleep(wl); |
2f01a1f5 KV |
250 | mutex_unlock(&wl->mutex); |
251 | } | |
252 | ||
253 | int wl12xx_plt_start(struct wl12xx *wl) | |
254 | { | |
255 | int ret; | |
256 | ||
257 | wl12xx_notice("power up"); | |
258 | ||
259 | if (wl->state != WL12XX_STATE_OFF) { | |
260 | wl12xx_error("cannot go into PLT state because not " | |
261 | "in off state: %d", wl->state); | |
262 | return -EBUSY; | |
263 | } | |
264 | ||
265 | wl->state = WL12XX_STATE_PLT; | |
266 | ||
267 | ret = wl12xx_chip_wakeup(wl); | |
268 | if (ret < 0) | |
269 | return ret; | |
270 | ||
271 | ret = wl->chip.op_boot(wl); | |
272 | if (ret < 0) | |
273 | return ret; | |
274 | ||
275 | wl12xx_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver); | |
276 | ||
277 | ret = wl->chip.op_plt_init(wl); | |
278 | if (ret < 0) | |
279 | return ret; | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
284 | int wl12xx_plt_stop(struct wl12xx *wl) | |
285 | { | |
286 | wl12xx_notice("power down"); | |
287 | ||
288 | if (wl->state != WL12XX_STATE_PLT) { | |
289 | wl12xx_error("cannot power down because not in PLT " | |
290 | "state: %d", wl->state); | |
291 | return -EBUSY; | |
292 | } | |
293 | ||
294 | wl12xx_disable_interrupts(wl); | |
295 | wl12xx_power_off(wl); | |
296 | ||
297 | wl->state = WL12XX_STATE_OFF; | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
302 | ||
303 | static int wl12xx_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | |
304 | { | |
305 | struct wl12xx *wl = hw->priv; | |
306 | ||
307 | skb_queue_tail(&wl->tx_queue, skb); | |
308 | ||
9f2ad4fb JO |
309 | /* |
310 | * The chip specific setup must run before the first TX packet - | |
311 | * before that, the tx_work will not be initialized! | |
312 | */ | |
313 | ||
2f01a1f5 KV |
314 | schedule_work(&wl->tx_work); |
315 | ||
316 | /* | |
317 | * The workqueue is slow to process the tx_queue and we need stop | |
318 | * the queue here, otherwise the queue will get too long. | |
319 | */ | |
320 | if (skb_queue_len(&wl->tx_queue) >= WL12XX_TX_QUEUE_MAX_LENGTH) { | |
321 | ieee80211_stop_queues(wl->hw); | |
322 | ||
323 | /* | |
324 | * FIXME: this is racy, the variable is not properly | |
325 | * protected. Maybe fix this by removing the stupid | |
326 | * variable altogether and checking the real queue state? | |
327 | */ | |
328 | wl->tx_queue_stopped = true; | |
329 | } | |
330 | ||
331 | return NETDEV_TX_OK; | |
332 | } | |
333 | ||
334 | static int wl12xx_op_start(struct ieee80211_hw *hw) | |
335 | { | |
336 | struct wl12xx *wl = hw->priv; | |
337 | int ret = 0; | |
338 | ||
339 | wl12xx_debug(DEBUG_MAC80211, "mac80211 start"); | |
340 | ||
341 | mutex_lock(&wl->mutex); | |
342 | ||
343 | if (wl->state != WL12XX_STATE_OFF) { | |
344 | wl12xx_error("cannot start because not in off state: %d", | |
345 | wl->state); | |
346 | ret = -EBUSY; | |
347 | goto out; | |
348 | } | |
349 | ||
350 | ret = wl12xx_chip_wakeup(wl); | |
351 | if (ret < 0) | |
352 | return ret; | |
353 | ||
354 | ret = wl->chip.op_boot(wl); | |
355 | if (ret < 0) | |
356 | goto out; | |
357 | ||
358 | ret = wl->chip.op_hw_init(wl); | |
359 | if (ret < 0) | |
360 | goto out; | |
361 | ||
362 | ret = wl12xx_acx_station_id(wl); | |
363 | if (ret < 0) | |
364 | goto out; | |
365 | ||
366 | wl->state = WL12XX_STATE_ON; | |
367 | ||
368 | wl12xx_info("firmware booted (%s)", wl->chip.fw_ver); | |
369 | ||
370 | out: | |
371 | if (ret < 0) | |
372 | wl12xx_power_off(wl); | |
373 | ||
374 | mutex_unlock(&wl->mutex); | |
375 | ||
376 | return ret; | |
377 | } | |
378 | ||
379 | static void wl12xx_op_stop(struct ieee80211_hw *hw) | |
380 | { | |
381 | struct wl12xx *wl = hw->priv; | |
382 | ||
383 | wl12xx_info("down"); | |
384 | ||
385 | wl12xx_debug(DEBUG_MAC80211, "mac80211 stop"); | |
386 | ||
387 | mutex_lock(&wl->mutex); | |
388 | ||
389 | WARN_ON(wl->state != WL12XX_STATE_ON); | |
390 | ||
391 | if (wl->scanning) { | |
392 | mutex_unlock(&wl->mutex); | |
393 | ieee80211_scan_completed(wl->hw, true); | |
394 | mutex_lock(&wl->mutex); | |
395 | wl->scanning = false; | |
396 | } | |
397 | ||
398 | wl->state = WL12XX_STATE_OFF; | |
399 | ||
400 | wl12xx_disable_interrupts(wl); | |
401 | ||
402 | mutex_unlock(&wl->mutex); | |
403 | ||
404 | cancel_work_sync(&wl->irq_work); | |
405 | cancel_work_sync(&wl->tx_work); | |
406 | cancel_work_sync(&wl->filter_work); | |
407 | ||
408 | mutex_lock(&wl->mutex); | |
409 | ||
410 | /* let's notify MAC80211 about the remaining pending TX frames */ | |
9f2ad4fb | 411 | wl->chip.op_tx_flush(wl); |
2f01a1f5 KV |
412 | wl12xx_power_off(wl); |
413 | ||
414 | memset(wl->bssid, 0, ETH_ALEN); | |
415 | wl->listen_int = 1; | |
416 | wl->bss_type = MAX_BSS_TYPE; | |
417 | ||
418 | wl->data_in_count = 0; | |
419 | wl->rx_counter = 0; | |
420 | wl->rx_handled = 0; | |
421 | wl->rx_current_buffer = 0; | |
422 | wl->rx_last_id = 0; | |
423 | wl->next_tx_complete = 0; | |
424 | wl->elp = false; | |
425 | wl->psm = 0; | |
426 | wl->tx_queue_stopped = false; | |
427 | wl->power_level = WL12XX_DEFAULT_POWER_LEVEL; | |
428 | ||
429 | wl12xx_debugfs_reset(wl); | |
430 | ||
431 | mutex_unlock(&wl->mutex); | |
432 | } | |
433 | ||
434 | static int wl12xx_op_add_interface(struct ieee80211_hw *hw, | |
435 | struct ieee80211_if_init_conf *conf) | |
436 | { | |
437 | struct wl12xx *wl = hw->priv; | |
438 | DECLARE_MAC_BUF(mac); | |
439 | int ret = 0; | |
440 | ||
441 | wl12xx_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %s", | |
442 | conf->type, print_mac(mac, conf->mac_addr)); | |
443 | ||
444 | mutex_lock(&wl->mutex); | |
445 | ||
446 | switch (conf->type) { | |
447 | case NL80211_IFTYPE_STATION: | |
448 | wl->bss_type = BSS_TYPE_STA_BSS; | |
449 | break; | |
450 | case NL80211_IFTYPE_ADHOC: | |
451 | wl->bss_type = BSS_TYPE_IBSS; | |
452 | break; | |
453 | default: | |
454 | ret = -EOPNOTSUPP; | |
455 | goto out; | |
456 | } | |
457 | ||
458 | if (memcmp(wl->mac_addr, conf->mac_addr, ETH_ALEN)) { | |
459 | memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN); | |
460 | SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); | |
461 | ret = wl12xx_acx_station_id(wl); | |
462 | if (ret < 0) | |
463 | goto out; | |
464 | } | |
465 | ||
466 | out: | |
467 | mutex_unlock(&wl->mutex); | |
468 | return ret; | |
469 | } | |
470 | ||
471 | static void wl12xx_op_remove_interface(struct ieee80211_hw *hw, | |
472 | struct ieee80211_if_init_conf *conf) | |
473 | { | |
474 | wl12xx_debug(DEBUG_MAC80211, "mac80211 remove interface"); | |
475 | } | |
476 | ||
477 | static int wl12xx_build_null_data(struct wl12xx *wl) | |
478 | { | |
479 | struct wl12xx_null_data_template template; | |
480 | ||
481 | if (!is_zero_ether_addr(wl->bssid)) { | |
482 | memcpy(template.header.da, wl->bssid, ETH_ALEN); | |
483 | memcpy(template.header.bssid, wl->bssid, ETH_ALEN); | |
484 | } else { | |
485 | memset(template.header.da, 0xff, ETH_ALEN); | |
486 | memset(template.header.bssid, 0xff, ETH_ALEN); | |
487 | } | |
488 | ||
489 | memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); | |
490 | template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | | |
491 | IEEE80211_STYPE_NULLFUNC); | |
492 | ||
493 | return wl12xx_cmd_template_set(wl, CMD_NULL_DATA, &template, | |
494 | sizeof(template)); | |
495 | ||
496 | } | |
497 | ||
498 | static int wl12xx_build_ps_poll(struct wl12xx *wl, u16 aid) | |
499 | { | |
500 | struct wl12xx_ps_poll_template template; | |
501 | ||
502 | memcpy(template.bssid, wl->bssid, ETH_ALEN); | |
503 | memcpy(template.ta, wl->mac_addr, ETH_ALEN); | |
504 | template.aid = aid; | |
505 | template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); | |
506 | ||
507 | return wl12xx_cmd_template_set(wl, CMD_PS_POLL, &template, | |
508 | sizeof(template)); | |
509 | ||
510 | } | |
511 | ||
512 | static int wl12xx_op_config(struct ieee80211_hw *hw, u32 changed) | |
513 | { | |
514 | struct wl12xx *wl = hw->priv; | |
515 | struct ieee80211_conf *conf = &hw->conf; | |
516 | int channel, ret = 0; | |
517 | ||
518 | channel = ieee80211_frequency_to_channel(conf->channel->center_freq); | |
519 | ||
520 | wl12xx_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", | |
521 | channel, | |
522 | conf->flags & IEEE80211_CONF_PS ? "on" : "off", | |
523 | conf->power_level); | |
524 | ||
525 | mutex_lock(&wl->mutex); | |
526 | ||
01d9cfbd KV |
527 | wl12xx_ps_elp_wakeup(wl); |
528 | ||
2f01a1f5 KV |
529 | if (channel != wl->channel) { |
530 | /* FIXME: use beacon interval provided by mac80211 */ | |
531 | ret = wl12xx_cmd_join(wl, wl->bss_type, 1, 100, 0); | |
532 | if (ret < 0) | |
533 | goto out; | |
534 | ||
535 | wl->channel = channel; | |
536 | } | |
537 | ||
538 | ret = wl12xx_build_null_data(wl); | |
539 | if (ret < 0) | |
540 | goto out; | |
541 | ||
542 | if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { | |
543 | wl12xx_info("psm enabled"); | |
544 | ||
545 | wl->psm_requested = true; | |
546 | ||
547 | /* | |
548 | * We enter PSM only if we're already associated. | |
549 | * If we're not, we'll enter it when joining an SSID, | |
550 | * through the bss_info_changed() hook. | |
551 | */ | |
552 | ret = wl12xx_ps_set_mode(wl, STATION_POWER_SAVE_MODE); | |
553 | } else if (!(conf->flags & IEEE80211_CONF_PS) && | |
554 | wl->psm_requested) { | |
555 | wl12xx_info("psm disabled"); | |
556 | ||
557 | wl->psm_requested = false; | |
558 | ||
559 | if (wl->psm) | |
560 | ret = wl12xx_ps_set_mode(wl, STATION_ACTIVE_MODE); | |
561 | } | |
562 | ||
563 | if (conf->power_level != wl->power_level) { | |
564 | ret = wl12xx_acx_tx_power(wl, conf->power_level); | |
565 | if (ret < 0) | |
566 | goto out; | |
567 | ||
568 | wl->power_level = conf->power_level; | |
569 | } | |
570 | ||
571 | out: | |
01d9cfbd | 572 | wl12xx_ps_elp_sleep(wl); |
2f01a1f5 KV |
573 | mutex_unlock(&wl->mutex); |
574 | return ret; | |
575 | } | |
576 | ||
577 | #define WL12XX_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ | |
578 | FIF_ALLMULTI | \ | |
579 | FIF_FCSFAIL | \ | |
580 | FIF_BCN_PRBRESP_PROMISC | \ | |
581 | FIF_CONTROL | \ | |
582 | FIF_OTHER_BSS) | |
583 | ||
584 | static void wl12xx_op_configure_filter(struct ieee80211_hw *hw, | |
585 | unsigned int changed, | |
586 | unsigned int *total, | |
587 | int mc_count, | |
588 | struct dev_addr_list *mc_list) | |
589 | { | |
590 | struct wl12xx *wl = hw->priv; | |
591 | ||
592 | wl12xx_debug(DEBUG_MAC80211, "mac80211 configure filter"); | |
593 | ||
594 | *total &= WL12XX_SUPPORTED_FILTERS; | |
595 | changed &= WL12XX_SUPPORTED_FILTERS; | |
596 | ||
597 | if (changed == 0) | |
598 | /* no filters which we support changed */ | |
599 | return; | |
600 | ||
601 | /* FIXME: wl->rx_config and wl->rx_filter are not protected */ | |
602 | ||
603 | wl->rx_config = WL12XX_DEFAULT_RX_CONFIG; | |
604 | wl->rx_filter = WL12XX_DEFAULT_RX_FILTER; | |
605 | ||
606 | if (*total & FIF_PROMISC_IN_BSS) { | |
607 | wl->rx_config |= CFG_BSSID_FILTER_EN; | |
608 | wl->rx_config |= CFG_RX_ALL_GOOD; | |
609 | } | |
610 | if (*total & FIF_ALLMULTI) | |
611 | /* | |
612 | * CFG_MC_FILTER_EN in rx_config needs to be 0 to receive | |
613 | * all multicast frames | |
614 | */ | |
615 | wl->rx_config &= ~CFG_MC_FILTER_EN; | |
616 | if (*total & FIF_FCSFAIL) | |
617 | wl->rx_filter |= CFG_RX_FCS_ERROR; | |
618 | if (*total & FIF_BCN_PRBRESP_PROMISC) { | |
619 | wl->rx_config &= ~CFG_BSSID_FILTER_EN; | |
620 | wl->rx_config &= ~CFG_SSID_FILTER_EN; | |
621 | } | |
622 | if (*total & FIF_CONTROL) | |
623 | wl->rx_filter |= CFG_RX_CTL_EN; | |
624 | if (*total & FIF_OTHER_BSS) | |
625 | wl->rx_filter &= ~CFG_BSSID_FILTER_EN; | |
626 | ||
627 | /* | |
628 | * FIXME: workqueues need to be properly cancelled on stop(), for | |
629 | * now let's just disable changing the filter settings. They will | |
630 | * be updated any on config(). | |
631 | */ | |
632 | /* schedule_work(&wl->filter_work); */ | |
633 | } | |
634 | ||
635 | /* HW encryption */ | |
ff25839b KV |
636 | static int wl12xx_set_key_type(struct wl12xx *wl, |
637 | struct wl12xx_cmd_set_keys *key, | |
2f01a1f5 KV |
638 | enum set_key_cmd cmd, |
639 | struct ieee80211_key_conf *mac80211_key, | |
640 | const u8 *addr) | |
641 | { | |
642 | switch (mac80211_key->alg) { | |
643 | case ALG_WEP: | |
644 | if (is_broadcast_ether_addr(addr)) | |
645 | key->key_type = KEY_WEP_DEFAULT; | |
646 | else | |
647 | key->key_type = KEY_WEP_ADDR; | |
648 | ||
649 | mac80211_key->hw_key_idx = mac80211_key->keyidx; | |
650 | break; | |
651 | case ALG_TKIP: | |
652 | if (is_broadcast_ether_addr(addr)) | |
653 | key->key_type = KEY_TKIP_MIC_GROUP; | |
654 | else | |
655 | key->key_type = KEY_TKIP_MIC_PAIRWISE; | |
656 | ||
657 | mac80211_key->hw_key_idx = mac80211_key->keyidx; | |
658 | break; | |
659 | case ALG_CCMP: | |
660 | if (is_broadcast_ether_addr(addr)) | |
661 | key->key_type = KEY_AES_GROUP; | |
662 | else | |
663 | key->key_type = KEY_AES_PAIRWISE; | |
664 | mac80211_key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | |
665 | break; | |
666 | default: | |
667 | wl12xx_error("Unknown key algo 0x%x", mac80211_key->alg); | |
668 | return -EOPNOTSUPP; | |
669 | } | |
670 | ||
671 | return 0; | |
672 | } | |
673 | ||
674 | static int wl12xx_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, | |
675 | struct ieee80211_vif *vif, | |
676 | struct ieee80211_sta *sta, | |
677 | struct ieee80211_key_conf *key) | |
678 | { | |
679 | struct wl12xx *wl = hw->priv; | |
ff25839b | 680 | struct wl12xx_cmd_set_keys *wl_cmd; |
2f01a1f5 KV |
681 | const u8 *addr; |
682 | int ret; | |
683 | ||
684 | static const u8 bcast_addr[ETH_ALEN] = | |
685 | { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | |
686 | ||
687 | wl12xx_debug(DEBUG_MAC80211, "mac80211 set key"); | |
688 | ||
ff25839b KV |
689 | wl_cmd = kzalloc(sizeof(*wl_cmd), GFP_KERNEL); |
690 | if (!wl_cmd) { | |
691 | ret = -ENOMEM; | |
692 | goto out; | |
693 | } | |
2f01a1f5 KV |
694 | |
695 | addr = sta ? sta->addr : bcast_addr; | |
696 | ||
697 | wl12xx_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd); | |
698 | wl12xx_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN); | |
699 | wl12xx_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", | |
700 | key->alg, key->keyidx, key->keylen, key->flags); | |
701 | wl12xx_dump(DEBUG_CRYPT, "KEY: ", key->key, key->keylen); | |
702 | ||
ff25839b KV |
703 | if (is_zero_ether_addr(addr)) { |
704 | /* We dont support TX only encryption */ | |
705 | ret = -EOPNOTSUPP; | |
706 | goto out; | |
707 | } | |
708 | ||
2f01a1f5 KV |
709 | mutex_lock(&wl->mutex); |
710 | ||
01d9cfbd KV |
711 | wl12xx_ps_elp_wakeup(wl); |
712 | ||
2f01a1f5 KV |
713 | switch (cmd) { |
714 | case SET_KEY: | |
ff25839b | 715 | wl_cmd->key_action = KEY_ADD_OR_REPLACE; |
2f01a1f5 KV |
716 | break; |
717 | case DISABLE_KEY: | |
ff25839b | 718 | wl_cmd->key_action = KEY_REMOVE; |
2f01a1f5 KV |
719 | break; |
720 | default: | |
721 | wl12xx_error("Unsupported key cmd 0x%x", cmd); | |
722 | break; | |
723 | } | |
724 | ||
ff25839b | 725 | ret = wl12xx_set_key_type(wl, wl_cmd, cmd, key, addr); |
2f01a1f5 KV |
726 | if (ret < 0) { |
727 | wl12xx_error("Set KEY type failed"); | |
ff25839b | 728 | goto out_unlock; |
2f01a1f5 KV |
729 | } |
730 | ||
ff25839b KV |
731 | if (wl_cmd->key_type != KEY_WEP_DEFAULT) |
732 | memcpy(wl_cmd->addr, addr, ETH_ALEN); | |
2f01a1f5 | 733 | |
ff25839b KV |
734 | if ((wl_cmd->key_type == KEY_TKIP_MIC_GROUP) || |
735 | (wl_cmd->key_type == KEY_TKIP_MIC_PAIRWISE)) { | |
2f01a1f5 KV |
736 | /* |
737 | * We get the key in the following form: | |
738 | * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) | |
739 | * but the target is expecting: | |
740 | * TKIP - RX MIC - TX MIC | |
741 | */ | |
ff25839b KV |
742 | memcpy(wl_cmd->key, key->key, 16); |
743 | memcpy(wl_cmd->key + 16, key->key + 24, 8); | |
744 | memcpy(wl_cmd->key + 24, key->key + 16, 8); | |
2f01a1f5 KV |
745 | |
746 | } else { | |
ff25839b | 747 | memcpy(wl_cmd->key, key->key, key->keylen); |
2f01a1f5 | 748 | } |
ff25839b | 749 | wl_cmd->key_size = key->keylen; |
2f01a1f5 | 750 | |
ff25839b KV |
751 | wl_cmd->id = key->keyidx; |
752 | wl_cmd->ssid_profile = 0; | |
2f01a1f5 | 753 | |
ff25839b | 754 | wl12xx_dump(DEBUG_CRYPT, "TARGET KEY: ", wl_cmd, sizeof(*wl_cmd)); |
2f01a1f5 | 755 | |
ff25839b KV |
756 | ret = wl12xx_cmd_send(wl, CMD_SET_KEYS, wl_cmd, sizeof(*wl_cmd)); |
757 | if (ret < 0) { | |
758 | wl12xx_warning("could not set keys"); | |
759 | goto out_unlock; | |
2f01a1f5 KV |
760 | } |
761 | ||
ff25839b | 762 | out_unlock: |
01d9cfbd | 763 | wl12xx_ps_elp_sleep(wl); |
2f01a1f5 | 764 | mutex_unlock(&wl->mutex); |
ff25839b KV |
765 | |
766 | out: | |
767 | kfree(wl_cmd); | |
768 | ||
2f01a1f5 KV |
769 | return ret; |
770 | } | |
771 | ||
772 | static int wl12xx_build_basic_rates(char *rates) | |
773 | { | |
774 | u8 index = 0; | |
775 | ||
776 | rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; | |
777 | rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; | |
778 | rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; | |
779 | rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; | |
780 | ||
781 | return index; | |
782 | } | |
783 | ||
784 | static int wl12xx_build_extended_rates(char *rates) | |
785 | { | |
786 | u8 index = 0; | |
787 | ||
788 | rates[index++] = IEEE80211_OFDM_RATE_6MB; | |
789 | rates[index++] = IEEE80211_OFDM_RATE_9MB; | |
790 | rates[index++] = IEEE80211_OFDM_RATE_12MB; | |
791 | rates[index++] = IEEE80211_OFDM_RATE_18MB; | |
792 | rates[index++] = IEEE80211_OFDM_RATE_24MB; | |
793 | rates[index++] = IEEE80211_OFDM_RATE_36MB; | |
794 | rates[index++] = IEEE80211_OFDM_RATE_48MB; | |
795 | rates[index++] = IEEE80211_OFDM_RATE_54MB; | |
796 | ||
797 | return index; | |
798 | } | |
799 | ||
800 | ||
801 | static int wl12xx_build_probe_req(struct wl12xx *wl, u8 *ssid, size_t ssid_len) | |
802 | { | |
803 | struct wl12xx_probe_req_template template; | |
804 | struct wl12xx_ie_rates *rates; | |
805 | char *ptr; | |
806 | u16 size; | |
807 | ||
808 | ptr = (char *)&template; | |
809 | size = sizeof(struct ieee80211_header); | |
810 | ||
811 | memset(template.header.da, 0xff, ETH_ALEN); | |
812 | memset(template.header.bssid, 0xff, ETH_ALEN); | |
813 | memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); | |
814 | template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); | |
815 | ||
816 | /* IEs */ | |
817 | /* SSID */ | |
818 | template.ssid.header.id = WLAN_EID_SSID; | |
819 | template.ssid.header.len = ssid_len; | |
820 | if (ssid_len && ssid) | |
821 | memcpy(template.ssid.ssid, ssid, ssid_len); | |
822 | size += sizeof(struct wl12xx_ie_header) + ssid_len; | |
823 | ptr += size; | |
824 | ||
825 | /* Basic Rates */ | |
826 | rates = (struct wl12xx_ie_rates *)ptr; | |
827 | rates->header.id = WLAN_EID_SUPP_RATES; | |
828 | rates->header.len = wl12xx_build_basic_rates(rates->rates); | |
829 | size += sizeof(struct wl12xx_ie_header) + rates->header.len; | |
830 | ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; | |
831 | ||
832 | /* Extended rates */ | |
833 | rates = (struct wl12xx_ie_rates *)ptr; | |
834 | rates->header.id = WLAN_EID_EXT_SUPP_RATES; | |
835 | rates->header.len = wl12xx_build_extended_rates(rates->rates); | |
836 | size += sizeof(struct wl12xx_ie_header) + rates->header.len; | |
837 | ||
838 | wl12xx_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); | |
839 | ||
840 | return wl12xx_cmd_template_set(wl, CMD_PROBE_REQ, &template, | |
841 | size); | |
842 | } | |
843 | ||
844 | static int wl12xx_hw_scan(struct wl12xx *wl, u8 *ssid, size_t len, | |
845 | u8 active_scan, u8 high_prio, u8 num_channels, | |
846 | u8 probe_requests) | |
847 | { | |
ff25839b KV |
848 | struct wl12xx_cmd_trigger_scan_to *trigger = NULL; |
849 | struct cmd_scan *params = NULL; | |
2f01a1f5 | 850 | int i, ret; |
2f01a1f5 | 851 | u16 scan_options = 0; |
2f01a1f5 KV |
852 | |
853 | if (wl->scanning) | |
854 | return -EINVAL; | |
855 | ||
856 | params = kzalloc(sizeof(*params), GFP_KERNEL); | |
857 | if (!params) | |
858 | return -ENOMEM; | |
859 | ||
860 | params->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); | |
861 | params->params.rx_filter_options = | |
862 | cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN); | |
863 | ||
864 | /* High priority scan */ | |
865 | if (!active_scan) | |
866 | scan_options |= SCAN_PASSIVE; | |
867 | if (high_prio) | |
868 | scan_options |= SCAN_PRIORITY_HIGH; | |
869 | params->params.scan_options = scan_options; | |
870 | ||
871 | params->params.num_channels = num_channels; | |
872 | params->params.num_probe_requests = probe_requests; | |
873 | params->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ | |
874 | params->params.tid_trigger = 0; | |
875 | ||
876 | for (i = 0; i < num_channels; i++) { | |
877 | params->channels[i].min_duration = cpu_to_le32(30000); | |
878 | params->channels[i].max_duration = cpu_to_le32(60000); | |
879 | memset(¶ms->channels[i].bssid_lsb, 0xff, 4); | |
880 | memset(¶ms->channels[i].bssid_msb, 0xff, 2); | |
881 | params->channels[i].early_termination = 0; | |
882 | params->channels[i].tx_power_att = 0; | |
883 | params->channels[i].channel = i + 1; | |
884 | memset(params->channels[i].pad, 0, 3); | |
885 | } | |
886 | ||
887 | for (i = num_channels; i < SCAN_MAX_NUM_OF_CHANNELS; i++) | |
888 | memset(¶ms->channels[i], 0, | |
889 | sizeof(struct basic_scan_channel_parameters)); | |
890 | ||
891 | if (len && ssid) { | |
892 | params->params.ssid_len = len; | |
893 | memcpy(params->params.ssid, ssid, len); | |
894 | } else { | |
895 | params->params.ssid_len = 0; | |
896 | memset(params->params.ssid, 0, 32); | |
897 | } | |
898 | ||
899 | ret = wl12xx_build_probe_req(wl, ssid, len); | |
900 | if (ret < 0) { | |
901 | wl12xx_error("PROBE request template failed"); | |
902 | goto out; | |
903 | } | |
904 | ||
ff25839b KV |
905 | trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); |
906 | if (!trigger) | |
907 | goto out; | |
908 | ||
909 | trigger->timeout = 0; | |
910 | ||
911 | ret = wl12xx_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, | |
912 | sizeof(*trigger)); | |
2f01a1f5 | 913 | if (ret < 0) { |
ff25839b | 914 | wl12xx_error("trigger scan to failed for hw scan"); |
2f01a1f5 KV |
915 | goto out; |
916 | } | |
917 | ||
918 | wl12xx_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); | |
919 | ||
920 | wl->scanning = true; | |
921 | ||
922 | ret = wl12xx_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); | |
923 | if (ret < 0) | |
924 | wl12xx_error("SCAN failed"); | |
925 | ||
926 | wl12xx_spi_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); | |
927 | ||
ff25839b | 928 | if (params->header.status != CMD_STATUS_SUCCESS) { |
2f01a1f5 | 929 | wl12xx_error("TEST command answer error: %d", |
ff25839b | 930 | params->header.status); |
2f01a1f5 KV |
931 | wl->scanning = false; |
932 | ret = -EIO; | |
933 | goto out; | |
934 | } | |
935 | ||
936 | out: | |
937 | kfree(params); | |
938 | return ret; | |
939 | ||
940 | } | |
941 | ||
942 | static int wl12xx_op_hw_scan(struct ieee80211_hw *hw, | |
943 | struct cfg80211_scan_request *req) | |
944 | { | |
945 | struct wl12xx *wl = hw->priv; | |
946 | int ret; | |
947 | u8 *ssid = NULL; | |
948 | size_t ssid_len = 0; | |
949 | ||
950 | wl12xx_debug(DEBUG_MAC80211, "mac80211 hw scan"); | |
951 | ||
952 | if (req->n_ssids) { | |
953 | ssid = req->ssids[0].ssid; | |
954 | ssid_len = req->ssids[0].ssid_len; | |
955 | } | |
956 | ||
957 | mutex_lock(&wl->mutex); | |
01d9cfbd KV |
958 | wl12xx_ps_elp_wakeup(wl); |
959 | ||
2f01a1f5 | 960 | ret = wl12xx_hw_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); |
01d9cfbd KV |
961 | |
962 | wl12xx_ps_elp_sleep(wl); | |
2f01a1f5 KV |
963 | mutex_unlock(&wl->mutex); |
964 | ||
965 | return ret; | |
966 | } | |
967 | ||
968 | static int wl12xx_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) | |
969 | { | |
970 | struct wl12xx *wl = hw->priv; | |
971 | int ret; | |
972 | ||
cee4fd27 KV |
973 | mutex_lock(&wl->mutex); |
974 | ||
01d9cfbd KV |
975 | wl12xx_ps_elp_wakeup(wl); |
976 | ||
2f01a1f5 KV |
977 | ret = wl12xx_acx_rts_threshold(wl, (u16) value); |
978 | ||
979 | if (ret < 0) | |
980 | wl12xx_warning("wl12xx_op_set_rts_threshold failed: %d", ret); | |
981 | ||
01d9cfbd KV |
982 | wl12xx_ps_elp_sleep(wl); |
983 | ||
cee4fd27 KV |
984 | mutex_unlock(&wl->mutex); |
985 | ||
2f01a1f5 KV |
986 | return ret; |
987 | } | |
988 | ||
989 | static void wl12xx_op_bss_info_changed(struct ieee80211_hw *hw, | |
990 | struct ieee80211_vif *vif, | |
991 | struct ieee80211_bss_conf *bss_conf, | |
992 | u32 changed) | |
993 | { | |
ff25839b | 994 | enum wl12xx_cmd_ps_mode mode; |
2f01a1f5 KV |
995 | struct wl12xx *wl = hw->priv; |
996 | struct sk_buff *beacon; | |
997 | int ret; | |
998 | ||
999 | wl12xx_debug(DEBUG_MAC80211, "mac80211 bss info changed"); | |
1000 | ||
1001 | mutex_lock(&wl->mutex); | |
1002 | ||
01d9cfbd KV |
1003 | wl12xx_ps_elp_wakeup(wl); |
1004 | ||
2f01a1f5 KV |
1005 | if (changed & BSS_CHANGED_ASSOC) { |
1006 | if (bss_conf->assoc) { | |
1007 | wl->aid = bss_conf->aid; | |
1008 | ||
1009 | ret = wl12xx_build_ps_poll(wl, wl->aid); | |
1010 | if (ret < 0) | |
1011 | goto out; | |
1012 | ||
1013 | ret = wl12xx_acx_aid(wl, wl->aid); | |
1014 | if (ret < 0) | |
1015 | goto out; | |
1016 | ||
1017 | /* If we want to go in PSM but we're not there yet */ | |
1018 | if (wl->psm_requested && !wl->psm) { | |
1019 | mode = STATION_POWER_SAVE_MODE; | |
1020 | ret = wl12xx_ps_set_mode(wl, mode); | |
1021 | if (ret < 0) | |
1022 | goto out; | |
1023 | } | |
1024 | } | |
1025 | } | |
1026 | if (changed & BSS_CHANGED_ERP_SLOT) { | |
1027 | if (bss_conf->use_short_slot) | |
1028 | ret = wl12xx_acx_slot(wl, SLOT_TIME_SHORT); | |
1029 | else | |
1030 | ret = wl12xx_acx_slot(wl, SLOT_TIME_LONG); | |
1031 | if (ret < 0) { | |
1032 | wl12xx_warning("Set slot time failed %d", ret); | |
1033 | goto out; | |
1034 | } | |
1035 | } | |
1036 | ||
1037 | if (changed & BSS_CHANGED_ERP_PREAMBLE) { | |
1038 | if (bss_conf->use_short_preamble) | |
1039 | wl12xx_acx_set_preamble(wl, ACX_PREAMBLE_SHORT); | |
1040 | else | |
1041 | wl12xx_acx_set_preamble(wl, ACX_PREAMBLE_LONG); | |
1042 | } | |
1043 | ||
1044 | if (changed & BSS_CHANGED_ERP_CTS_PROT) { | |
1045 | if (bss_conf->use_cts_prot) | |
1046 | ret = wl12xx_acx_cts_protect(wl, CTSPROTECT_ENABLE); | |
1047 | else | |
1048 | ret = wl12xx_acx_cts_protect(wl, CTSPROTECT_DISABLE); | |
1049 | if (ret < 0) { | |
1050 | wl12xx_warning("Set ctsprotect failed %d", ret); | |
1051 | goto out; | |
1052 | } | |
1053 | } | |
1054 | ||
1055 | if (changed & BSS_CHANGED_BSSID) { | |
1056 | memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); | |
1057 | ||
1058 | ret = wl12xx_build_null_data(wl); | |
1059 | if (ret < 0) | |
1060 | goto out; | |
1061 | ||
1062 | if (wl->bss_type != BSS_TYPE_IBSS) { | |
1063 | ret = wl12xx_cmd_join(wl, wl->bss_type, 5, 100, 1); | |
1064 | if (ret < 0) | |
1065 | goto out; | |
1066 | } | |
1067 | } | |
1068 | ||
1069 | if (changed & BSS_CHANGED_BEACON) { | |
1070 | beacon = ieee80211_beacon_get(hw, vif); | |
1071 | ret = wl12xx_cmd_template_set(wl, CMD_BEACON, beacon->data, | |
1072 | beacon->len); | |
1073 | ||
1074 | if (ret < 0) { | |
1075 | dev_kfree_skb(beacon); | |
1076 | goto out; | |
1077 | } | |
1078 | ||
1079 | ret = wl12xx_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data, | |
1080 | beacon->len); | |
1081 | ||
1082 | dev_kfree_skb(beacon); | |
1083 | ||
1084 | if (ret < 0) | |
1085 | goto out; | |
1086 | ||
1087 | ret = wl12xx_cmd_join(wl, wl->bss_type, 1, 100, 0); | |
1088 | ||
1089 | if (ret < 0) | |
1090 | goto out; | |
1091 | } | |
1092 | ||
1093 | out: | |
01d9cfbd | 1094 | wl12xx_ps_elp_sleep(wl); |
2f01a1f5 KV |
1095 | mutex_unlock(&wl->mutex); |
1096 | } | |
1097 | ||
1098 | ||
1099 | /* can't be const, mac80211 writes to this */ | |
1100 | static struct ieee80211_rate wl12xx_rates[] = { | |
1101 | { .bitrate = 10, | |
1102 | .hw_value = 0x1, | |
1103 | .hw_value_short = 0x1, }, | |
1104 | { .bitrate = 20, | |
1105 | .hw_value = 0x2, | |
1106 | .hw_value_short = 0x2, | |
1107 | .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | |
1108 | { .bitrate = 55, | |
1109 | .hw_value = 0x4, | |
1110 | .hw_value_short = 0x4, | |
1111 | .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | |
1112 | { .bitrate = 110, | |
1113 | .hw_value = 0x20, | |
1114 | .hw_value_short = 0x20, | |
1115 | .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | |
1116 | { .bitrate = 60, | |
1117 | .hw_value = 0x8, | |
1118 | .hw_value_short = 0x8, }, | |
1119 | { .bitrate = 90, | |
1120 | .hw_value = 0x10, | |
1121 | .hw_value_short = 0x10, }, | |
1122 | { .bitrate = 120, | |
1123 | .hw_value = 0x40, | |
1124 | .hw_value_short = 0x40, }, | |
1125 | { .bitrate = 180, | |
1126 | .hw_value = 0x80, | |
1127 | .hw_value_short = 0x80, }, | |
1128 | { .bitrate = 240, | |
1129 | .hw_value = 0x200, | |
1130 | .hw_value_short = 0x200, }, | |
1131 | { .bitrate = 360, | |
1132 | .hw_value = 0x400, | |
1133 | .hw_value_short = 0x400, }, | |
1134 | { .bitrate = 480, | |
1135 | .hw_value = 0x800, | |
1136 | .hw_value_short = 0x800, }, | |
1137 | { .bitrate = 540, | |
1138 | .hw_value = 0x1000, | |
1139 | .hw_value_short = 0x1000, }, | |
1140 | }; | |
1141 | ||
1142 | /* can't be const, mac80211 writes to this */ | |
1143 | static struct ieee80211_channel wl12xx_channels[] = { | |
1144 | { .hw_value = 1, .center_freq = 2412}, | |
1145 | { .hw_value = 2, .center_freq = 2417}, | |
1146 | { .hw_value = 3, .center_freq = 2422}, | |
1147 | { .hw_value = 4, .center_freq = 2427}, | |
1148 | { .hw_value = 5, .center_freq = 2432}, | |
1149 | { .hw_value = 6, .center_freq = 2437}, | |
1150 | { .hw_value = 7, .center_freq = 2442}, | |
1151 | { .hw_value = 8, .center_freq = 2447}, | |
1152 | { .hw_value = 9, .center_freq = 2452}, | |
1153 | { .hw_value = 10, .center_freq = 2457}, | |
1154 | { .hw_value = 11, .center_freq = 2462}, | |
1155 | { .hw_value = 12, .center_freq = 2467}, | |
1156 | { .hw_value = 13, .center_freq = 2472}, | |
1157 | }; | |
1158 | ||
1159 | /* can't be const, mac80211 writes to this */ | |
1160 | static struct ieee80211_supported_band wl12xx_band_2ghz = { | |
1161 | .channels = wl12xx_channels, | |
1162 | .n_channels = ARRAY_SIZE(wl12xx_channels), | |
1163 | .bitrates = wl12xx_rates, | |
1164 | .n_bitrates = ARRAY_SIZE(wl12xx_rates), | |
1165 | }; | |
1166 | ||
1167 | static const struct ieee80211_ops wl12xx_ops = { | |
1168 | .start = wl12xx_op_start, | |
1169 | .stop = wl12xx_op_stop, | |
1170 | .add_interface = wl12xx_op_add_interface, | |
1171 | .remove_interface = wl12xx_op_remove_interface, | |
1172 | .config = wl12xx_op_config, | |
1173 | .configure_filter = wl12xx_op_configure_filter, | |
1174 | .tx = wl12xx_op_tx, | |
1175 | .set_key = wl12xx_op_set_key, | |
1176 | .hw_scan = wl12xx_op_hw_scan, | |
1177 | .bss_info_changed = wl12xx_op_bss_info_changed, | |
1178 | .set_rts_threshold = wl12xx_op_set_rts_threshold, | |
1179 | }; | |
1180 | ||
1181 | static int wl12xx_register_hw(struct wl12xx *wl) | |
1182 | { | |
1183 | int ret; | |
1184 | ||
1185 | if (wl->mac80211_registered) | |
1186 | return 0; | |
1187 | ||
1188 | SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); | |
1189 | ||
1190 | ret = ieee80211_register_hw(wl->hw); | |
1191 | if (ret < 0) { | |
1192 | wl12xx_error("unable to register mac80211 hw: %d", ret); | |
1193 | return ret; | |
1194 | } | |
1195 | ||
1196 | wl->mac80211_registered = true; | |
1197 | ||
1198 | wl12xx_notice("loaded"); | |
1199 | ||
1200 | return 0; | |
1201 | } | |
1202 | ||
1203 | static int wl12xx_init_ieee80211(struct wl12xx *wl) | |
1204 | { | |
1205 | /* The tx descriptor buffer and the TKIP space */ | |
1206 | wl->hw->extra_tx_headroom = sizeof(struct tx_double_buffer_desc) | |
9f2ad4fb | 1207 | + WL1251_TKIP_IV_SPACE; |
2f01a1f5 KV |
1208 | |
1209 | /* unit us */ | |
1210 | /* FIXME: find a proper value */ | |
1211 | wl->hw->channel_change_time = 10000; | |
1212 | ||
1213 | wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | | |
1214 | IEEE80211_HW_NOISE_DBM; | |
1215 | ||
1216 | wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); | |
1217 | wl->hw->wiphy->max_scan_ssids = 1; | |
1218 | wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl12xx_band_2ghz; | |
1219 | ||
1220 | SET_IEEE80211_DEV(wl->hw, &wl->spi->dev); | |
1221 | ||
1222 | return 0; | |
1223 | } | |
1224 | ||
1225 | #define WL12XX_DEFAULT_CHANNEL 1 | |
1226 | static int __devinit wl12xx_probe(struct spi_device *spi) | |
1227 | { | |
1228 | struct wl12xx_platform_data *pdata; | |
1229 | struct ieee80211_hw *hw; | |
1230 | struct wl12xx *wl; | |
1231 | int ret, i; | |
1232 | static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; | |
1233 | ||
1234 | pdata = spi->dev.platform_data; | |
1235 | if (!pdata) { | |
1236 | wl12xx_error("no platform data"); | |
1237 | return -ENODEV; | |
1238 | } | |
1239 | ||
1240 | hw = ieee80211_alloc_hw(sizeof(*wl), &wl12xx_ops); | |
1241 | if (!hw) { | |
1242 | wl12xx_error("could not alloc ieee80211_hw"); | |
1243 | return -ENOMEM; | |
1244 | } | |
1245 | ||
1246 | wl = hw->priv; | |
1247 | memset(wl, 0, sizeof(*wl)); | |
1248 | ||
1249 | wl->hw = hw; | |
1250 | dev_set_drvdata(&spi->dev, wl); | |
1251 | wl->spi = spi; | |
1252 | ||
1253 | wl->data_in_count = 0; | |
1254 | ||
1255 | skb_queue_head_init(&wl->tx_queue); | |
1256 | ||
2f01a1f5 KV |
1257 | INIT_WORK(&wl->filter_work, wl12xx_filter_work); |
1258 | wl->channel = WL12XX_DEFAULT_CHANNEL; | |
1259 | wl->scanning = false; | |
1260 | wl->default_key = 0; | |
1261 | wl->listen_int = 1; | |
1262 | wl->rx_counter = 0; | |
1263 | wl->rx_handled = 0; | |
1264 | wl->rx_current_buffer = 0; | |
1265 | wl->rx_last_id = 0; | |
1266 | wl->rx_config = WL12XX_DEFAULT_RX_CONFIG; | |
1267 | wl->rx_filter = WL12XX_DEFAULT_RX_FILTER; | |
1268 | wl->elp = false; | |
1269 | wl->psm = 0; | |
1270 | wl->psm_requested = false; | |
1271 | wl->tx_queue_stopped = false; | |
1272 | wl->power_level = WL12XX_DEFAULT_POWER_LEVEL; | |
1273 | ||
1274 | /* We use the default power on sleep time until we know which chip | |
1275 | * we're using */ | |
1276 | wl->chip.power_on_sleep = WL12XX_DEFAULT_POWER_ON_SLEEP; | |
1277 | ||
1278 | for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) | |
1279 | wl->tx_frames[i] = NULL; | |
1280 | ||
1281 | wl->next_tx_complete = 0; | |
1282 | ||
1283 | /* | |
1284 | * In case our MAC address is not correctly set, | |
1285 | * we use a random but Nokia MAC. | |
1286 | */ | |
1287 | memcpy(wl->mac_addr, nokia_oui, 3); | |
1288 | get_random_bytes(wl->mac_addr + 3, 3); | |
1289 | ||
1290 | wl->state = WL12XX_STATE_OFF; | |
1291 | mutex_init(&wl->mutex); | |
1292 | ||
1293 | wl->tx_mgmt_frm_rate = DEFAULT_HW_GEN_TX_RATE; | |
1294 | wl->tx_mgmt_frm_mod = DEFAULT_HW_GEN_MODULATION_TYPE; | |
1295 | ||
4721213f KV |
1296 | wl->rx_descriptor = kmalloc(sizeof(*wl->rx_descriptor), GFP_KERNEL); |
1297 | if (!wl->rx_descriptor) { | |
1298 | wl12xx_error("could not allocate memory for rx descriptor"); | |
1299 | ret = -ENOMEM; | |
1300 | goto out_free; | |
1301 | } | |
1302 | ||
2f01a1f5 KV |
1303 | /* This is the only SPI value that we need to set here, the rest |
1304 | * comes from the board-peripherals file */ | |
1305 | spi->bits_per_word = 32; | |
1306 | ||
1307 | ret = spi_setup(spi); | |
1308 | if (ret < 0) { | |
1309 | wl12xx_error("spi_setup failed"); | |
1310 | goto out_free; | |
1311 | } | |
1312 | ||
1313 | wl->set_power = pdata->set_power; | |
1314 | if (!wl->set_power) { | |
1315 | wl12xx_error("set power function missing in platform data"); | |
c4f5c852 KV |
1316 | ret = -ENODEV; |
1317 | goto out_free; | |
2f01a1f5 KV |
1318 | } |
1319 | ||
1320 | wl->irq = spi->irq; | |
1321 | if (wl->irq < 0) { | |
1322 | wl12xx_error("irq missing in platform data"); | |
c4f5c852 KV |
1323 | ret = -ENODEV; |
1324 | goto out_free; | |
2f01a1f5 KV |
1325 | } |
1326 | ||
1327 | ret = request_irq(wl->irq, wl12xx_irq, 0, DRIVER_NAME, wl); | |
1328 | if (ret < 0) { | |
1329 | wl12xx_error("request_irq() failed: %d", ret); | |
1330 | goto out_free; | |
1331 | } | |
1332 | ||
1333 | set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); | |
1334 | ||
1335 | disable_irq(wl->irq); | |
1336 | ||
1337 | ret = wl12xx_init_ieee80211(wl); | |
1338 | if (ret) | |
1339 | goto out_irq; | |
1340 | ||
1341 | ret = wl12xx_register_hw(wl); | |
1342 | if (ret) | |
1343 | goto out_irq; | |
1344 | ||
1345 | wl12xx_debugfs_init(wl); | |
1346 | ||
1347 | wl12xx_notice("initialized"); | |
1348 | ||
1349 | return 0; | |
1350 | ||
1351 | out_irq: | |
1352 | free_irq(wl->irq, wl); | |
1353 | ||
1354 | out_free: | |
4721213f KV |
1355 | kfree(wl->rx_descriptor); |
1356 | wl->rx_descriptor = NULL; | |
1357 | ||
2f01a1f5 KV |
1358 | ieee80211_free_hw(hw); |
1359 | ||
1360 | return ret; | |
1361 | } | |
1362 | ||
1363 | static int __devexit wl12xx_remove(struct spi_device *spi) | |
1364 | { | |
1365 | struct wl12xx *wl = dev_get_drvdata(&spi->dev); | |
1366 | ||
1367 | ieee80211_unregister_hw(wl->hw); | |
1368 | ||
1369 | wl12xx_debugfs_exit(wl); | |
1370 | ||
1371 | free_irq(wl->irq, wl); | |
1372 | kfree(wl->target_mem_map); | |
1373 | kfree(wl->data_path); | |
1374 | kfree(wl->fw); | |
1375 | wl->fw = NULL; | |
1376 | kfree(wl->nvs); | |
1377 | wl->nvs = NULL; | |
4721213f KV |
1378 | |
1379 | kfree(wl->rx_descriptor); | |
1380 | wl->rx_descriptor = NULL; | |
1381 | ||
2f01a1f5 KV |
1382 | ieee80211_free_hw(wl->hw); |
1383 | ||
1384 | return 0; | |
1385 | } | |
1386 | ||
1387 | ||
1388 | static struct spi_driver wl12xx_spi_driver = { | |
1389 | .driver = { | |
1390 | .name = "wl12xx", | |
1391 | .bus = &spi_bus_type, | |
1392 | .owner = THIS_MODULE, | |
1393 | }, | |
1394 | ||
1395 | .probe = wl12xx_probe, | |
1396 | .remove = __devexit_p(wl12xx_remove), | |
1397 | }; | |
1398 | ||
1399 | static int __init wl12xx_init(void) | |
1400 | { | |
1401 | int ret; | |
1402 | ||
1403 | ret = spi_register_driver(&wl12xx_spi_driver); | |
1404 | if (ret < 0) { | |
1405 | wl12xx_error("failed to register spi driver: %d", ret); | |
1406 | goto out; | |
1407 | } | |
1408 | ||
1409 | out: | |
1410 | return ret; | |
1411 | } | |
1412 | ||
1413 | static void __exit wl12xx_exit(void) | |
1414 | { | |
1415 | spi_unregister_driver(&wl12xx_spi_driver); | |
1416 | ||
1417 | wl12xx_notice("unloaded"); | |
1418 | } | |
1419 | ||
1420 | module_init(wl12xx_init); | |
1421 | module_exit(wl12xx_exit); | |
1422 | ||
1423 | MODULE_LICENSE("GPL"); | |
1424 | MODULE_AUTHOR("Kalle Valo <Kalle.Valo@nokia.com>, " | |
1425 | "Luciano Coelho <luciano.coelho@nokia.com>"); |