diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /drivers/net/wireless/wl12xx/wl1251_main.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/net/wireless/wl12xx/wl1251_main.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1251_main.c | 559 |
1 files changed, 245 insertions, 314 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 1103256ad989..1c8226eee409 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <linux/irq.h> | 28 | #include <linux/irq.h> |
29 | #include <linux/crc32.h> | 29 | #include <linux/crc32.h> |
30 | #include <linux/etherdevice.h> | 30 | #include <linux/etherdevice.h> |
31 | #include <linux/vmalloc.h> | ||
32 | #include <linux/slab.h> | ||
31 | 33 | ||
32 | #include "wl1251.h" | 34 | #include "wl1251.h" |
33 | #include "wl12xx_80211.h" | 35 | #include "wl12xx_80211.h" |
@@ -83,7 +85,7 @@ static int wl1251_fetch_firmware(struct wl1251 *wl) | |||
83 | } | 85 | } |
84 | 86 | ||
85 | wl->fw_len = fw->size; | 87 | wl->fw_len = fw->size; |
86 | wl->fw = kmalloc(wl->fw_len, GFP_KERNEL); | 88 | wl->fw = vmalloc(wl->fw_len); |
87 | 89 | ||
88 | if (!wl->fw) { | 90 | if (!wl->fw) { |
89 | wl1251_error("could not allocate memory for the firmware"); | 91 | wl1251_error("could not allocate memory for the firmware"); |
@@ -183,8 +185,11 @@ static int wl1251_chip_wakeup(struct wl1251 *wl) | |||
183 | wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)", | 185 | wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)", |
184 | wl->chip_id); | 186 | wl->chip_id); |
185 | break; | 187 | break; |
186 | case CHIP_ID_1251_PG10: | ||
187 | case CHIP_ID_1251_PG11: | 188 | case CHIP_ID_1251_PG11: |
189 | wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG11)", | ||
190 | wl->chip_id); | ||
191 | break; | ||
192 | case CHIP_ID_1251_PG10: | ||
188 | default: | 193 | default: |
189 | wl1251_error("unsupported chip id: 0x%x", wl->chip_id); | 194 | wl1251_error("unsupported chip id: 0x%x", wl->chip_id); |
190 | ret = -ENODEV; | 195 | ret = -ENODEV; |
@@ -208,9 +213,10 @@ out: | |||
208 | return ret; | 213 | return ret; |
209 | } | 214 | } |
210 | 215 | ||
216 | #define WL1251_IRQ_LOOP_COUNT 10 | ||
211 | static void wl1251_irq_work(struct work_struct *work) | 217 | static void wl1251_irq_work(struct work_struct *work) |
212 | { | 218 | { |
213 | u32 intr; | 219 | u32 intr, ctr = WL1251_IRQ_LOOP_COUNT; |
214 | struct wl1251 *wl = | 220 | struct wl1251 *wl = |
215 | container_of(work, struct wl1251, irq_work); | 221 | container_of(work, struct wl1251, irq_work); |
216 | int ret; | 222 | int ret; |
@@ -231,78 +237,86 @@ static void wl1251_irq_work(struct work_struct *work) | |||
231 | intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); | 237 | intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); |
232 | wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr); | 238 | wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr); |
233 | 239 | ||
234 | if (wl->data_path) { | 240 | do { |
235 | wl->rx_counter = | 241 | if (wl->data_path) { |
236 | wl1251_mem_read32(wl, wl->data_path->rx_control_addr); | 242 | wl->rx_counter = wl1251_mem_read32( |
237 | 243 | wl, wl->data_path->rx_control_addr); | |
238 | /* We handle a frmware bug here */ | 244 | |
239 | switch ((wl->rx_counter - wl->rx_handled) & 0xf) { | 245 | /* We handle a frmware bug here */ |
240 | case 0: | 246 | switch ((wl->rx_counter - wl->rx_handled) & 0xf) { |
241 | wl1251_debug(DEBUG_IRQ, "RX: FW and host in sync"); | 247 | case 0: |
242 | intr &= ~WL1251_ACX_INTR_RX0_DATA; | 248 | wl1251_debug(DEBUG_IRQ, |
243 | intr &= ~WL1251_ACX_INTR_RX1_DATA; | 249 | "RX: FW and host in sync"); |
244 | break; | 250 | intr &= ~WL1251_ACX_INTR_RX0_DATA; |
245 | case 1: | 251 | intr &= ~WL1251_ACX_INTR_RX1_DATA; |
246 | wl1251_debug(DEBUG_IRQ, "RX: FW +1"); | 252 | break; |
247 | intr |= WL1251_ACX_INTR_RX0_DATA; | 253 | case 1: |
248 | intr &= ~WL1251_ACX_INTR_RX1_DATA; | 254 | wl1251_debug(DEBUG_IRQ, "RX: FW +1"); |
249 | break; | 255 | intr |= WL1251_ACX_INTR_RX0_DATA; |
250 | case 2: | 256 | intr &= ~WL1251_ACX_INTR_RX1_DATA; |
251 | wl1251_debug(DEBUG_IRQ, "RX: FW +2"); | 257 | break; |
252 | intr |= WL1251_ACX_INTR_RX0_DATA; | 258 | case 2: |
253 | intr |= WL1251_ACX_INTR_RX1_DATA; | 259 | wl1251_debug(DEBUG_IRQ, "RX: FW +2"); |
254 | break; | 260 | intr |= WL1251_ACX_INTR_RX0_DATA; |
255 | default: | 261 | intr |= WL1251_ACX_INTR_RX1_DATA; |
256 | wl1251_warning("RX: FW and host out of sync: %d", | 262 | break; |
257 | wl->rx_counter - wl->rx_handled); | 263 | default: |
258 | break; | 264 | wl1251_warning( |
259 | } | 265 | "RX: FW and host out of sync: %d", |
260 | 266 | wl->rx_counter - wl->rx_handled); | |
261 | wl->rx_handled = wl->rx_counter; | 267 | break; |
268 | } | ||
262 | 269 | ||
270 | wl->rx_handled = wl->rx_counter; | ||
263 | 271 | ||
264 | wl1251_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter); | 272 | wl1251_debug(DEBUG_IRQ, "RX counter: %d", |
265 | } | 273 | wl->rx_counter); |
274 | } | ||
266 | 275 | ||
267 | intr &= wl->intr_mask; | 276 | intr &= wl->intr_mask; |
268 | 277 | ||
269 | if (intr == 0) { | 278 | if (intr == 0) { |
270 | wl1251_debug(DEBUG_IRQ, "INTR is 0"); | 279 | wl1251_debug(DEBUG_IRQ, "INTR is 0"); |
271 | wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, | 280 | goto out_sleep; |
272 | ~(wl->intr_mask)); | 281 | } |
273 | 282 | ||
274 | goto out_sleep; | 283 | if (intr & WL1251_ACX_INTR_RX0_DATA) { |
275 | } | 284 | wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); |
285 | wl1251_rx(wl); | ||
286 | } | ||
276 | 287 | ||
277 | if (intr & WL1251_ACX_INTR_RX0_DATA) { | 288 | if (intr & WL1251_ACX_INTR_RX1_DATA) { |
278 | wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); | 289 | wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); |
279 | wl1251_rx(wl); | 290 | wl1251_rx(wl); |
280 | } | 291 | } |
281 | 292 | ||
282 | if (intr & WL1251_ACX_INTR_RX1_DATA) { | 293 | if (intr & WL1251_ACX_INTR_TX_RESULT) { |
283 | wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); | 294 | wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); |
284 | wl1251_rx(wl); | 295 | wl1251_tx_complete(wl); |
285 | } | 296 | } |
286 | 297 | ||
287 | if (intr & WL1251_ACX_INTR_TX_RESULT) { | 298 | if (intr & (WL1251_ACX_INTR_EVENT_A | |
288 | wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); | 299 | WL1251_ACX_INTR_EVENT_B)) { |
289 | wl1251_tx_complete(wl); | 300 | wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", |
290 | } | 301 | intr); |
302 | if (intr & WL1251_ACX_INTR_EVENT_A) | ||
303 | wl1251_event_handle(wl, 0); | ||
304 | else | ||
305 | wl1251_event_handle(wl, 1); | ||
306 | } | ||
291 | 307 | ||
292 | if (intr & (WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B)) { | 308 | if (intr & WL1251_ACX_INTR_INIT_COMPLETE) |
293 | wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", intr); | 309 | wl1251_debug(DEBUG_IRQ, |
294 | if (intr & WL1251_ACX_INTR_EVENT_A) | 310 | "WL1251_ACX_INTR_INIT_COMPLETE"); |
295 | wl1251_event_handle(wl, 0); | ||
296 | else | ||
297 | wl1251_event_handle(wl, 1); | ||
298 | } | ||
299 | 311 | ||
300 | if (intr & WL1251_ACX_INTR_INIT_COMPLETE) | 312 | if (--ctr == 0) |
301 | wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); | 313 | break; |
302 | 314 | ||
303 | wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); | 315 | intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); |
316 | } while (intr); | ||
304 | 317 | ||
305 | out_sleep: | 318 | out_sleep: |
319 | wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); | ||
306 | wl1251_ps_elp_sleep(wl); | 320 | wl1251_ps_elp_sleep(wl); |
307 | 321 | ||
308 | out: | 322 | out: |
@@ -382,6 +396,7 @@ static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
382 | * the queue here, otherwise the queue will get too long. | 396 | * the queue here, otherwise the queue will get too long. |
383 | */ | 397 | */ |
384 | if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_MAX_LENGTH) { | 398 | if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_MAX_LENGTH) { |
399 | wl1251_debug(DEBUG_TX, "op_tx: tx_queue full, stop queues"); | ||
385 | ieee80211_stop_queues(wl->hw); | 400 | ieee80211_stop_queues(wl->hw); |
386 | 401 | ||
387 | /* | 402 | /* |
@@ -497,17 +512,23 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) | |||
497 | } | 512 | } |
498 | 513 | ||
499 | static int wl1251_op_add_interface(struct ieee80211_hw *hw, | 514 | static int wl1251_op_add_interface(struct ieee80211_hw *hw, |
500 | struct ieee80211_if_init_conf *conf) | 515 | struct ieee80211_vif *vif) |
501 | { | 516 | { |
502 | struct wl1251 *wl = hw->priv; | 517 | struct wl1251 *wl = hw->priv; |
503 | int ret = 0; | 518 | int ret = 0; |
504 | 519 | ||
505 | wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", | 520 | wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", |
506 | conf->type, conf->mac_addr); | 521 | vif->type, vif->addr); |
507 | 522 | ||
508 | mutex_lock(&wl->mutex); | 523 | mutex_lock(&wl->mutex); |
524 | if (wl->vif) { | ||
525 | ret = -EBUSY; | ||
526 | goto out; | ||
527 | } | ||
528 | |||
529 | wl->vif = vif; | ||
509 | 530 | ||
510 | switch (conf->type) { | 531 | switch (vif->type) { |
511 | case NL80211_IFTYPE_STATION: | 532 | case NL80211_IFTYPE_STATION: |
512 | wl->bss_type = BSS_TYPE_STA_BSS; | 533 | wl->bss_type = BSS_TYPE_STA_BSS; |
513 | break; | 534 | break; |
@@ -519,8 +540,8 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, | |||
519 | goto out; | 540 | goto out; |
520 | } | 541 | } |
521 | 542 | ||
522 | if (memcmp(wl->mac_addr, conf->mac_addr, ETH_ALEN)) { | 543 | if (memcmp(wl->mac_addr, vif->addr, ETH_ALEN)) { |
523 | memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN); | 544 | memcpy(wl->mac_addr, vif->addr, ETH_ALEN); |
524 | SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); | 545 | SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); |
525 | ret = wl1251_acx_station_id(wl); | 546 | ret = wl1251_acx_station_id(wl); |
526 | if (ret < 0) | 547 | if (ret < 0) |
@@ -533,44 +554,35 @@ out: | |||
533 | } | 554 | } |
534 | 555 | ||
535 | static void wl1251_op_remove_interface(struct ieee80211_hw *hw, | 556 | static void wl1251_op_remove_interface(struct ieee80211_hw *hw, |
536 | struct ieee80211_if_init_conf *conf) | 557 | struct ieee80211_vif *vif) |
537 | { | 558 | { |
559 | struct wl1251 *wl = hw->priv; | ||
560 | |||
561 | mutex_lock(&wl->mutex); | ||
538 | wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface"); | 562 | wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface"); |
563 | wl->vif = NULL; | ||
564 | mutex_unlock(&wl->mutex); | ||
539 | } | 565 | } |
540 | 566 | ||
541 | static int wl1251_build_null_data(struct wl1251 *wl) | 567 | static int wl1251_build_qos_null_data(struct wl1251 *wl) |
542 | { | 568 | { |
543 | struct wl12xx_null_data_template template; | 569 | struct ieee80211_qos_hdr template; |
544 | 570 | ||
545 | if (!is_zero_ether_addr(wl->bssid)) { | 571 | memset(&template, 0, sizeof(template)); |
546 | memcpy(template.header.da, wl->bssid, ETH_ALEN); | ||
547 | memcpy(template.header.bssid, wl->bssid, ETH_ALEN); | ||
548 | } else { | ||
549 | memset(template.header.da, 0xff, ETH_ALEN); | ||
550 | memset(template.header.bssid, 0xff, ETH_ALEN); | ||
551 | } | ||
552 | |||
553 | memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); | ||
554 | template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||
555 | IEEE80211_STYPE_NULLFUNC); | ||
556 | 572 | ||
557 | return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template, | 573 | memcpy(template.addr1, wl->bssid, ETH_ALEN); |
558 | sizeof(template)); | 574 | memcpy(template.addr2, wl->mac_addr, ETH_ALEN); |
559 | 575 | memcpy(template.addr3, wl->bssid, ETH_ALEN); | |
560 | } | ||
561 | 576 | ||
562 | static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid) | 577 | template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | |
563 | { | 578 | IEEE80211_STYPE_QOS_NULLFUNC | |
564 | struct wl12xx_ps_poll_template template; | 579 | IEEE80211_FCTL_TODS); |
565 | 580 | ||
566 | memcpy(template.bssid, wl->bssid, ETH_ALEN); | 581 | /* FIXME: not sure what priority to use here */ |
567 | memcpy(template.ta, wl->mac_addr, ETH_ALEN); | 582 | template.qos_ctrl = cpu_to_le16(0); |
568 | template.aid = aid; | ||
569 | template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); | ||
570 | 583 | ||
571 | return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template, | 584 | return wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, &template, |
572 | sizeof(template)); | 585 | sizeof(template)); |
573 | |||
574 | } | 586 | } |
575 | 587 | ||
576 | static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) | 588 | static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) |
@@ -601,35 +613,39 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) | |||
601 | goto out_sleep; | 613 | goto out_sleep; |
602 | } | 614 | } |
603 | 615 | ||
604 | ret = wl1251_build_null_data(wl); | ||
605 | if (ret < 0) | ||
606 | goto out_sleep; | ||
607 | |||
608 | if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { | 616 | if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { |
609 | wl1251_debug(DEBUG_PSM, "psm enabled"); | 617 | wl1251_debug(DEBUG_PSM, "psm enabled"); |
610 | 618 | ||
611 | wl->psm_requested = true; | 619 | wl->psm_requested = true; |
612 | 620 | ||
621 | wl->dtim_period = conf->ps_dtim_period; | ||
622 | |||
623 | ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, | ||
624 | wl->dtim_period); | ||
625 | |||
613 | /* | 626 | /* |
614 | * We enter PSM only if we're already associated. | 627 | * mac80211 enables PSM only if we're already associated. |
615 | * If we're not, we'll enter it when joining an SSID, | ||
616 | * through the bss_info_changed() hook. | ||
617 | */ | 628 | */ |
618 | ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); | 629 | ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); |
630 | if (ret < 0) | ||
631 | goto out_sleep; | ||
619 | } else if (!(conf->flags & IEEE80211_CONF_PS) && | 632 | } else if (!(conf->flags & IEEE80211_CONF_PS) && |
620 | wl->psm_requested) { | 633 | wl->psm_requested) { |
621 | wl1251_debug(DEBUG_PSM, "psm disabled"); | 634 | wl1251_debug(DEBUG_PSM, "psm disabled"); |
622 | 635 | ||
623 | wl->psm_requested = false; | 636 | wl->psm_requested = false; |
624 | 637 | ||
625 | if (wl->psm) | 638 | if (wl->psm) { |
626 | ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); | 639 | ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); |
640 | if (ret < 0) | ||
641 | goto out_sleep; | ||
642 | } | ||
627 | } | 643 | } |
628 | 644 | ||
629 | if (conf->power_level != wl->power_level) { | 645 | if (conf->power_level != wl->power_level) { |
630 | ret = wl1251_acx_tx_power(wl, conf->power_level); | 646 | ret = wl1251_acx_tx_power(wl, conf->power_level); |
631 | if (ret < 0) | 647 | if (ret < 0) |
632 | goto out; | 648 | goto out_sleep; |
633 | 649 | ||
634 | wl->power_level = conf->power_level; | 650 | wl->power_level = conf->power_level; |
635 | } | 651 | } |
@@ -840,199 +856,61 @@ out: | |||
840 | return ret; | 856 | return ret; |
841 | } | 857 | } |
842 | 858 | ||
843 | static int wl1251_build_basic_rates(char *rates) | 859 | static int wl1251_op_hw_scan(struct ieee80211_hw *hw, |
844 | { | 860 | struct cfg80211_scan_request *req) |
845 | u8 index = 0; | ||
846 | |||
847 | rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; | ||
848 | rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; | ||
849 | rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; | ||
850 | rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; | ||
851 | |||
852 | return index; | ||
853 | } | ||
854 | |||
855 | static int wl1251_build_extended_rates(char *rates) | ||
856 | { | 861 | { |
857 | u8 index = 0; | 862 | struct wl1251 *wl = hw->priv; |
858 | 863 | struct sk_buff *skb; | |
859 | rates[index++] = IEEE80211_OFDM_RATE_6MB; | 864 | size_t ssid_len = 0; |
860 | rates[index++] = IEEE80211_OFDM_RATE_9MB; | 865 | u8 *ssid = NULL; |
861 | rates[index++] = IEEE80211_OFDM_RATE_12MB; | 866 | int ret; |
862 | rates[index++] = IEEE80211_OFDM_RATE_18MB; | ||
863 | rates[index++] = IEEE80211_OFDM_RATE_24MB; | ||
864 | rates[index++] = IEEE80211_OFDM_RATE_36MB; | ||
865 | rates[index++] = IEEE80211_OFDM_RATE_48MB; | ||
866 | rates[index++] = IEEE80211_OFDM_RATE_54MB; | ||
867 | |||
868 | return index; | ||
869 | } | ||
870 | |||
871 | 867 | ||
872 | static int wl1251_build_probe_req(struct wl1251 *wl, u8 *ssid, size_t ssid_len) | 868 | wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); |
873 | { | ||
874 | struct wl12xx_probe_req_template template; | ||
875 | struct wl12xx_ie_rates *rates; | ||
876 | char *ptr; | ||
877 | u16 size; | ||
878 | |||
879 | ptr = (char *)&template; | ||
880 | size = sizeof(struct ieee80211_header); | ||
881 | |||
882 | memset(template.header.da, 0xff, ETH_ALEN); | ||
883 | memset(template.header.bssid, 0xff, ETH_ALEN); | ||
884 | memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); | ||
885 | template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); | ||
886 | |||
887 | /* IEs */ | ||
888 | /* SSID */ | ||
889 | template.ssid.header.id = WLAN_EID_SSID; | ||
890 | template.ssid.header.len = ssid_len; | ||
891 | if (ssid_len && ssid) | ||
892 | memcpy(template.ssid.ssid, ssid, ssid_len); | ||
893 | size += sizeof(struct wl12xx_ie_header) + ssid_len; | ||
894 | ptr += size; | ||
895 | |||
896 | /* Basic Rates */ | ||
897 | rates = (struct wl12xx_ie_rates *)ptr; | ||
898 | rates->header.id = WLAN_EID_SUPP_RATES; | ||
899 | rates->header.len = wl1251_build_basic_rates(rates->rates); | ||
900 | size += sizeof(struct wl12xx_ie_header) + rates->header.len; | ||
901 | ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; | ||
902 | |||
903 | /* Extended rates */ | ||
904 | rates = (struct wl12xx_ie_rates *)ptr; | ||
905 | rates->header.id = WLAN_EID_EXT_SUPP_RATES; | ||
906 | rates->header.len = wl1251_build_extended_rates(rates->rates); | ||
907 | size += sizeof(struct wl12xx_ie_header) + rates->header.len; | ||
908 | |||
909 | wl1251_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); | ||
910 | |||
911 | return wl1251_cmd_template_set(wl, CMD_PROBE_REQ, &template, | ||
912 | size); | ||
913 | } | ||
914 | 869 | ||
915 | static int wl1251_hw_scan(struct wl1251 *wl, u8 *ssid, size_t len, | 870 | if (req->n_ssids) { |
916 | u8 active_scan, u8 high_prio, u8 num_channels, | 871 | ssid = req->ssids[0].ssid; |
917 | u8 probe_requests) | 872 | ssid_len = req->ssids[0].ssid_len; |
918 | { | ||
919 | struct wl1251_cmd_trigger_scan_to *trigger = NULL; | ||
920 | struct cmd_scan *params = NULL; | ||
921 | int i, ret; | ||
922 | u16 scan_options = 0; | ||
923 | |||
924 | if (wl->scanning) | ||
925 | return -EINVAL; | ||
926 | |||
927 | params = kzalloc(sizeof(*params), GFP_KERNEL); | ||
928 | if (!params) | ||
929 | return -ENOMEM; | ||
930 | |||
931 | params->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); | ||
932 | params->params.rx_filter_options = | ||
933 | cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN); | ||
934 | |||
935 | /* High priority scan */ | ||
936 | if (!active_scan) | ||
937 | scan_options |= SCAN_PASSIVE; | ||
938 | if (high_prio) | ||
939 | scan_options |= SCAN_PRIORITY_HIGH; | ||
940 | params->params.scan_options = scan_options; | ||
941 | |||
942 | params->params.num_channels = num_channels; | ||
943 | params->params.num_probe_requests = probe_requests; | ||
944 | params->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ | ||
945 | params->params.tid_trigger = 0; | ||
946 | |||
947 | for (i = 0; i < num_channels; i++) { | ||
948 | params->channels[i].min_duration = cpu_to_le32(30000); | ||
949 | params->channels[i].max_duration = cpu_to_le32(60000); | ||
950 | memset(¶ms->channels[i].bssid_lsb, 0xff, 4); | ||
951 | memset(¶ms->channels[i].bssid_msb, 0xff, 2); | ||
952 | params->channels[i].early_termination = 0; | ||
953 | params->channels[i].tx_power_att = 0; | ||
954 | params->channels[i].channel = i + 1; | ||
955 | memset(params->channels[i].pad, 0, 3); | ||
956 | } | 873 | } |
957 | 874 | ||
958 | for (i = num_channels; i < SCAN_MAX_NUM_OF_CHANNELS; i++) | 875 | mutex_lock(&wl->mutex); |
959 | memset(¶ms->channels[i], 0, | ||
960 | sizeof(struct basic_scan_channel_parameters)); | ||
961 | |||
962 | if (len && ssid) { | ||
963 | params->params.ssid_len = len; | ||
964 | memcpy(params->params.ssid, ssid, len); | ||
965 | } else { | ||
966 | params->params.ssid_len = 0; | ||
967 | memset(params->params.ssid, 0, 32); | ||
968 | } | ||
969 | 876 | ||
970 | ret = wl1251_build_probe_req(wl, ssid, len); | 877 | if (wl->scanning) { |
971 | if (ret < 0) { | 878 | wl1251_debug(DEBUG_SCAN, "scan already in progress"); |
972 | wl1251_error("PROBE request template failed"); | 879 | ret = -EINVAL; |
973 | goto out; | 880 | goto out; |
974 | } | 881 | } |
975 | 882 | ||
976 | trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); | 883 | ret = wl1251_ps_elp_wakeup(wl); |
977 | if (!trigger) | 884 | if (ret < 0) |
978 | goto out; | 885 | goto out; |
979 | 886 | ||
980 | trigger->timeout = 0; | 887 | skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, |
981 | 888 | req->ie, req->ie_len); | |
982 | ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, | 889 | if (!skb) { |
983 | sizeof(*trigger)); | 890 | ret = -ENOMEM; |
984 | if (ret < 0) { | ||
985 | wl1251_error("trigger scan to failed for hw scan"); | ||
986 | goto out; | 891 | goto out; |
987 | } | 892 | } |
988 | 893 | ||
989 | wl1251_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); | 894 | ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data, |
990 | 895 | skb->len); | |
991 | wl->scanning = true; | 896 | dev_kfree_skb(skb); |
897 | if (ret < 0) | ||
898 | goto out_sleep; | ||
992 | 899 | ||
993 | ret = wl1251_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); | 900 | ret = wl1251_cmd_trigger_scan_to(wl, 0); |
994 | if (ret < 0) | 901 | if (ret < 0) |
995 | wl1251_error("SCAN failed"); | 902 | goto out_sleep; |
996 | 903 | ||
997 | wl1251_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); | 904 | wl->scanning = true; |
998 | 905 | ||
999 | if (params->header.status != CMD_STATUS_SUCCESS) { | 906 | ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels, |
1000 | wl1251_error("TEST command answer error: %d", | 907 | req->n_channels, WL1251_SCAN_NUM_PROBES); |
1001 | params->header.status); | 908 | if (ret < 0) { |
1002 | wl->scanning = false; | 909 | wl->scanning = false; |
1003 | ret = -EIO; | 910 | goto out_sleep; |
1004 | goto out; | ||
1005 | } | ||
1006 | |||
1007 | out: | ||
1008 | kfree(params); | ||
1009 | return ret; | ||
1010 | |||
1011 | } | ||
1012 | |||
1013 | static int wl1251_op_hw_scan(struct ieee80211_hw *hw, | ||
1014 | struct cfg80211_scan_request *req) | ||
1015 | { | ||
1016 | struct wl1251 *wl = hw->priv; | ||
1017 | int ret; | ||
1018 | u8 *ssid = NULL; | ||
1019 | size_t ssid_len = 0; | ||
1020 | |||
1021 | wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); | ||
1022 | |||
1023 | if (req->n_ssids) { | ||
1024 | ssid = req->ssids[0].ssid; | ||
1025 | ssid_len = req->ssids[0].ssid_len; | ||
1026 | } | 911 | } |
1027 | 912 | ||
1028 | mutex_lock(&wl->mutex); | 913 | out_sleep: |
1029 | |||
1030 | ret = wl1251_ps_elp_wakeup(wl); | ||
1031 | if (ret < 0) | ||
1032 | goto out; | ||
1033 | |||
1034 | ret = wl1251_hw_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); | ||
1035 | |||
1036 | wl1251_ps_elp_sleep(wl); | 914 | wl1251_ps_elp_sleep(wl); |
1037 | 915 | ||
1038 | out: | 916 | out: |
@@ -1069,9 +947,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, | |||
1069 | struct ieee80211_bss_conf *bss_conf, | 947 | struct ieee80211_bss_conf *bss_conf, |
1070 | u32 changed) | 948 | u32 changed) |
1071 | { | 949 | { |
1072 | enum wl1251_cmd_ps_mode mode; | ||
1073 | struct wl1251 *wl = hw->priv; | 950 | struct wl1251 *wl = hw->priv; |
1074 | struct sk_buff *beacon; | 951 | struct sk_buff *beacon, *skb; |
1075 | int ret; | 952 | int ret; |
1076 | 953 | ||
1077 | wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); | 954 | wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); |
@@ -1082,30 +959,49 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, | |||
1082 | if (ret < 0) | 959 | if (ret < 0) |
1083 | goto out; | 960 | goto out; |
1084 | 961 | ||
962 | if (changed & BSS_CHANGED_BSSID) { | ||
963 | memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); | ||
964 | |||
965 | skb = ieee80211_nullfunc_get(wl->hw, wl->vif); | ||
966 | if (!skb) | ||
967 | goto out_sleep; | ||
968 | |||
969 | ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, | ||
970 | skb->data, skb->len); | ||
971 | dev_kfree_skb(skb); | ||
972 | if (ret < 0) | ||
973 | goto out_sleep; | ||
974 | |||
975 | ret = wl1251_build_qos_null_data(wl); | ||
976 | if (ret < 0) | ||
977 | goto out; | ||
978 | |||
979 | if (wl->bss_type != BSS_TYPE_IBSS) { | ||
980 | ret = wl1251_join(wl, wl->bss_type, wl->channel, | ||
981 | wl->beacon_int, wl->dtim_period); | ||
982 | if (ret < 0) | ||
983 | goto out_sleep; | ||
984 | } | ||
985 | } | ||
986 | |||
1085 | if (changed & BSS_CHANGED_ASSOC) { | 987 | if (changed & BSS_CHANGED_ASSOC) { |
1086 | if (bss_conf->assoc) { | 988 | if (bss_conf->assoc) { |
1087 | wl->beacon_int = bss_conf->beacon_int; | 989 | wl->beacon_int = bss_conf->beacon_int; |
1088 | wl->dtim_period = bss_conf->dtim_period; | ||
1089 | |||
1090 | /* FIXME: call join */ | ||
1091 | 990 | ||
1092 | wl->aid = bss_conf->aid; | 991 | skb = ieee80211_pspoll_get(wl->hw, wl->vif); |
992 | if (!skb) | ||
993 | goto out_sleep; | ||
1093 | 994 | ||
1094 | ret = wl1251_build_ps_poll(wl, wl->aid); | 995 | ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, |
996 | skb->data, | ||
997 | skb->len); | ||
998 | dev_kfree_skb(skb); | ||
1095 | if (ret < 0) | 999 | if (ret < 0) |
1096 | goto out_sleep; | 1000 | goto out_sleep; |
1097 | 1001 | ||
1098 | ret = wl1251_acx_aid(wl, wl->aid); | 1002 | ret = wl1251_acx_aid(wl, bss_conf->aid); |
1099 | if (ret < 0) | 1003 | if (ret < 0) |
1100 | goto out_sleep; | 1004 | goto out_sleep; |
1101 | |||
1102 | /* If we want to go in PSM but we're not there yet */ | ||
1103 | if (wl->psm_requested && !wl->psm) { | ||
1104 | mode = STATION_POWER_SAVE_MODE; | ||
1105 | ret = wl1251_ps_set_mode(wl, mode); | ||
1106 | if (ret < 0) | ||
1107 | goto out_sleep; | ||
1108 | } | ||
1109 | } else { | 1005 | } else { |
1110 | /* use defaults when not associated */ | 1006 | /* use defaults when not associated */ |
1111 | wl->beacon_int = WL1251_DEFAULT_BEACON_INT; | 1007 | wl->beacon_int = WL1251_DEFAULT_BEACON_INT; |
@@ -1137,23 +1033,6 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, | |||
1137 | ret = wl1251_acx_cts_protect(wl, CTSPROTECT_DISABLE); | 1033 | ret = wl1251_acx_cts_protect(wl, CTSPROTECT_DISABLE); |
1138 | if (ret < 0) { | 1034 | if (ret < 0) { |
1139 | wl1251_warning("Set ctsprotect failed %d", ret); | 1035 | wl1251_warning("Set ctsprotect failed %d", ret); |
1140 | goto out; | ||
1141 | } | ||
1142 | } | ||
1143 | |||
1144 | if (changed & BSS_CHANGED_BSSID) { | ||
1145 | memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); | ||
1146 | |||
1147 | ret = wl1251_build_null_data(wl); | ||
1148 | if (ret < 0) | ||
1149 | goto out; | ||
1150 | |||
1151 | if (wl->bss_type != BSS_TYPE_IBSS) { | ||
1152 | ret = wl1251_join(wl, wl->bss_type, wl->channel, | ||
1153 | wl->beacon_int, wl->dtim_period); | ||
1154 | if (ret < 0) | ||
1155 | goto out_sleep; | ||
1156 | wl1251_warning("Set ctsprotect failed %d", ret); | ||
1157 | goto out_sleep; | 1036 | goto out_sleep; |
1158 | } | 1037 | } |
1159 | } | 1038 | } |
@@ -1165,7 +1044,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, | |||
1165 | 1044 | ||
1166 | if (ret < 0) { | 1045 | if (ret < 0) { |
1167 | dev_kfree_skb(beacon); | 1046 | dev_kfree_skb(beacon); |
1168 | goto out; | 1047 | goto out_sleep; |
1169 | } | 1048 | } |
1170 | 1049 | ||
1171 | ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data, | 1050 | ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data, |
@@ -1174,13 +1053,13 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, | |||
1174 | dev_kfree_skb(beacon); | 1053 | dev_kfree_skb(beacon); |
1175 | 1054 | ||
1176 | if (ret < 0) | 1055 | if (ret < 0) |
1177 | goto out; | 1056 | goto out_sleep; |
1178 | 1057 | ||
1179 | ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, | 1058 | ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, |
1180 | wl->channel, wl->dtim_period); | 1059 | wl->channel, wl->dtim_period); |
1181 | 1060 | ||
1182 | if (ret < 0) | 1061 | if (ret < 0) |
1183 | goto out; | 1062 | goto out_sleep; |
1184 | } | 1063 | } |
1185 | 1064 | ||
1186 | out_sleep: | 1065 | out_sleep: |
@@ -1251,6 +1130,49 @@ static struct ieee80211_channel wl1251_channels[] = { | |||
1251 | { .hw_value = 13, .center_freq = 2472}, | 1130 | { .hw_value = 13, .center_freq = 2472}, |
1252 | }; | 1131 | }; |
1253 | 1132 | ||
1133 | static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue, | ||
1134 | const struct ieee80211_tx_queue_params *params) | ||
1135 | { | ||
1136 | enum wl1251_acx_ps_scheme ps_scheme; | ||
1137 | struct wl1251 *wl = hw->priv; | ||
1138 | int ret; | ||
1139 | |||
1140 | mutex_lock(&wl->mutex); | ||
1141 | |||
1142 | wl1251_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue); | ||
1143 | |||
1144 | ret = wl1251_ps_elp_wakeup(wl); | ||
1145 | if (ret < 0) | ||
1146 | goto out; | ||
1147 | |||
1148 | /* mac80211 uses units of 32 usec */ | ||
1149 | ret = wl1251_acx_ac_cfg(wl, wl1251_tx_get_queue(queue), | ||
1150 | params->cw_min, params->cw_max, | ||
1151 | params->aifs, params->txop * 32); | ||
1152 | if (ret < 0) | ||
1153 | goto out_sleep; | ||
1154 | |||
1155 | if (params->uapsd) | ||
1156 | ps_scheme = WL1251_ACX_PS_SCHEME_UPSD_TRIGGER; | ||
1157 | else | ||
1158 | ps_scheme = WL1251_ACX_PS_SCHEME_LEGACY; | ||
1159 | |||
1160 | ret = wl1251_acx_tid_cfg(wl, wl1251_tx_get_queue(queue), | ||
1161 | CHANNEL_TYPE_EDCF, | ||
1162 | wl1251_tx_get_queue(queue), ps_scheme, | ||
1163 | WL1251_ACX_ACK_POLICY_LEGACY); | ||
1164 | if (ret < 0) | ||
1165 | goto out_sleep; | ||
1166 | |||
1167 | out_sleep: | ||
1168 | wl1251_ps_elp_sleep(wl); | ||
1169 | |||
1170 | out: | ||
1171 | mutex_unlock(&wl->mutex); | ||
1172 | |||
1173 | return ret; | ||
1174 | } | ||
1175 | |||
1254 | /* can't be const, mac80211 writes to this */ | 1176 | /* can't be const, mac80211 writes to this */ |
1255 | static struct ieee80211_supported_band wl1251_band_2ghz = { | 1177 | static struct ieee80211_supported_band wl1251_band_2ghz = { |
1256 | .channels = wl1251_channels, | 1178 | .channels = wl1251_channels, |
@@ -1271,6 +1193,7 @@ static const struct ieee80211_ops wl1251_ops = { | |||
1271 | .hw_scan = wl1251_op_hw_scan, | 1193 | .hw_scan = wl1251_op_hw_scan, |
1272 | .bss_info_changed = wl1251_op_bss_info_changed, | 1194 | .bss_info_changed = wl1251_op_bss_info_changed, |
1273 | .set_rts_threshold = wl1251_op_set_rts_threshold, | 1195 | .set_rts_threshold = wl1251_op_set_rts_threshold, |
1196 | .conf_tx = wl1251_op_conf_tx, | ||
1274 | }; | 1197 | }; |
1275 | 1198 | ||
1276 | static int wl1251_register_hw(struct wl1251 *wl) | 1199 | static int wl1251_register_hw(struct wl1251 *wl) |
@@ -1308,12 +1231,17 @@ int wl1251_init_ieee80211(struct wl1251 *wl) | |||
1308 | wl->hw->channel_change_time = 10000; | 1231 | wl->hw->channel_change_time = 10000; |
1309 | 1232 | ||
1310 | wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | | 1233 | wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | |
1311 | IEEE80211_HW_NOISE_DBM; | 1234 | IEEE80211_HW_NOISE_DBM | |
1235 | IEEE80211_HW_SUPPORTS_PS | | ||
1236 | IEEE80211_HW_BEACON_FILTER | | ||
1237 | IEEE80211_HW_SUPPORTS_UAPSD; | ||
1312 | 1238 | ||
1313 | wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); | 1239 | wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); |
1314 | wl->hw->wiphy->max_scan_ssids = 1; | 1240 | wl->hw->wiphy->max_scan_ssids = 1; |
1315 | wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1251_band_2ghz; | 1241 | wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1251_band_2ghz; |
1316 | 1242 | ||
1243 | wl->hw->queues = 4; | ||
1244 | |||
1317 | ret = wl1251_register_hw(wl); | 1245 | ret = wl1251_register_hw(wl); |
1318 | if (ret) | 1246 | if (ret) |
1319 | goto out; | 1247 | goto out; |
@@ -1351,6 +1279,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) | |||
1351 | skb_queue_head_init(&wl->tx_queue); | 1279 | skb_queue_head_init(&wl->tx_queue); |
1352 | 1280 | ||
1353 | INIT_WORK(&wl->filter_work, wl1251_filter_work); | 1281 | INIT_WORK(&wl->filter_work, wl1251_filter_work); |
1282 | INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work); | ||
1354 | wl->channel = WL1251_DEFAULT_CHANNEL; | 1283 | wl->channel = WL1251_DEFAULT_CHANNEL; |
1355 | wl->scanning = false; | 1284 | wl->scanning = false; |
1356 | wl->default_key = 0; | 1285 | wl->default_key = 0; |
@@ -1368,6 +1297,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) | |||
1368 | wl->power_level = WL1251_DEFAULT_POWER_LEVEL; | 1297 | wl->power_level = WL1251_DEFAULT_POWER_LEVEL; |
1369 | wl->beacon_int = WL1251_DEFAULT_BEACON_INT; | 1298 | wl->beacon_int = WL1251_DEFAULT_BEACON_INT; |
1370 | wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; | 1299 | wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; |
1300 | wl->vif = NULL; | ||
1371 | 1301 | ||
1372 | for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) | 1302 | for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) |
1373 | wl->tx_frames[i] = NULL; | 1303 | wl->tx_frames[i] = NULL; |
@@ -1409,7 +1339,7 @@ int wl1251_free_hw(struct wl1251 *wl) | |||
1409 | 1339 | ||
1410 | kfree(wl->target_mem_map); | 1340 | kfree(wl->target_mem_map); |
1411 | kfree(wl->data_path); | 1341 | kfree(wl->data_path); |
1412 | kfree(wl->fw); | 1342 | vfree(wl->fw); |
1413 | wl->fw = NULL; | 1343 | wl->fw = NULL; |
1414 | kfree(wl->nvs); | 1344 | kfree(wl->nvs); |
1415 | wl->nvs = NULL; | 1345 | wl->nvs = NULL; |
@@ -1426,4 +1356,5 @@ EXPORT_SYMBOL_GPL(wl1251_free_hw); | |||
1426 | MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core"); | 1356 | MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core"); |
1427 | MODULE_LICENSE("GPL"); | 1357 | MODULE_LICENSE("GPL"); |
1428 | MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); | 1358 | MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); |
1429 | MODULE_ALIAS("spi:wl12xx"); | 1359 | MODULE_ALIAS("spi:wl1251"); |
1360 | MODULE_FIRMWARE(WL1251_FW_NAME); | ||