diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/main.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/main.c | 353 |
1 files changed, 284 insertions, 69 deletions
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 8b3c8d196b03..0c69e959d0de 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/vmalloc.h> | 30 | #include <linux/vmalloc.h> |
31 | #include <linux/platform_device.h> | 31 | #include <linux/platform_device.h> |
32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
33 | #include <linux/wl12xx.h> | ||
33 | 34 | ||
34 | #include "wl12xx.h" | 35 | #include "wl12xx.h" |
35 | #include "wl12xx_80211.h" | 36 | #include "wl12xx_80211.h" |
@@ -54,7 +55,7 @@ static struct conf_drv_settings default_conf = { | |||
54 | [CONF_SG_BT_PER_THRESHOLD] = 7500, | 55 | [CONF_SG_BT_PER_THRESHOLD] = 7500, |
55 | [CONF_SG_HV3_MAX_OVERRIDE] = 0, | 56 | [CONF_SG_HV3_MAX_OVERRIDE] = 0, |
56 | [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, | 57 | [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, |
57 | [CONF_SG_BT_LOAD_RATIO] = 50, | 58 | [CONF_SG_BT_LOAD_RATIO] = 200, |
58 | [CONF_SG_AUTO_PS_MODE] = 1, | 59 | [CONF_SG_AUTO_PS_MODE] = 1, |
59 | [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, | 60 | [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, |
60 | [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, | 61 | [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, |
@@ -191,7 +192,8 @@ static struct conf_drv_settings default_conf = { | |||
191 | .long_retry_limit = 10, | 192 | .long_retry_limit = 10, |
192 | .aflags = 0, | 193 | .aflags = 0, |
193 | }, | 194 | }, |
194 | .ap_max_tx_retries = 100, | 195 | .max_tx_retries = 100, |
196 | .ap_aging_period = 300, | ||
195 | .tid_conf_count = 4, | 197 | .tid_conf_count = 4, |
196 | .tid_conf = { | 198 | .tid_conf = { |
197 | [CONF_TX_AC_BE] = { | 199 | [CONF_TX_AC_BE] = { |
@@ -254,7 +256,7 @@ static struct conf_drv_settings default_conf = { | |||
254 | .ps_poll_threshold = 10, | 256 | .ps_poll_threshold = 10, |
255 | .ps_poll_recovery_period = 700, | 257 | .ps_poll_recovery_period = 700, |
256 | .bet_enable = CONF_BET_MODE_ENABLE, | 258 | .bet_enable = CONF_BET_MODE_ENABLE, |
257 | .bet_max_consecutive = 10, | 259 | .bet_max_consecutive = 50, |
258 | .psm_entry_retries = 5, | 260 | .psm_entry_retries = 5, |
259 | .psm_exit_retries = 255, | 261 | .psm_exit_retries = 255, |
260 | .psm_entry_nullfunc_retries = 3, | 262 | .psm_entry_nullfunc_retries = 3, |
@@ -298,7 +300,7 @@ static struct conf_drv_settings default_conf = { | |||
298 | .tx_ba_win_size = 64, | 300 | .tx_ba_win_size = 64, |
299 | .inactivity_timeout = 10000, | 301 | .inactivity_timeout = 10000, |
300 | }, | 302 | }, |
301 | .mem = { | 303 | .mem_wl127x = { |
302 | .num_stations = 1, | 304 | .num_stations = 1, |
303 | .ssid_profiles = 1, | 305 | .ssid_profiles = 1, |
304 | .rx_block_num = 70, | 306 | .rx_block_num = 70, |
@@ -307,7 +309,18 @@ static struct conf_drv_settings default_conf = { | |||
307 | .min_req_tx_blocks = 100, | 309 | .min_req_tx_blocks = 100, |
308 | .min_req_rx_blocks = 22, | 310 | .min_req_rx_blocks = 22, |
309 | .tx_min = 27, | 311 | .tx_min = 27, |
310 | } | 312 | }, |
313 | .mem_wl128x = { | ||
314 | .num_stations = 1, | ||
315 | .ssid_profiles = 1, | ||
316 | .rx_block_num = 40, | ||
317 | .tx_min_block_num = 40, | ||
318 | .dynamic_memory = 1, | ||
319 | .min_req_tx_blocks = 45, | ||
320 | .min_req_rx_blocks = 22, | ||
321 | .tx_min = 27, | ||
322 | }, | ||
323 | .hci_io_ds = HCI_IO_DS_6MA, | ||
311 | }; | 324 | }; |
312 | 325 | ||
313 | static void __wl1271_op_remove_interface(struct wl1271 *wl); | 326 | static void __wl1271_op_remove_interface(struct wl1271 *wl); |
@@ -329,6 +342,7 @@ static struct platform_device wl1271_device = { | |||
329 | }, | 342 | }, |
330 | }; | 343 | }; |
331 | 344 | ||
345 | static DEFINE_MUTEX(wl_list_mutex); | ||
332 | static LIST_HEAD(wl_list); | 346 | static LIST_HEAD(wl_list); |
333 | 347 | ||
334 | static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, | 348 | static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, |
@@ -359,10 +373,12 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, | |||
359 | return NOTIFY_DONE; | 373 | return NOTIFY_DONE; |
360 | 374 | ||
361 | wl_temp = hw->priv; | 375 | wl_temp = hw->priv; |
376 | mutex_lock(&wl_list_mutex); | ||
362 | list_for_each_entry(wl, &wl_list, list) { | 377 | list_for_each_entry(wl, &wl_list, list) { |
363 | if (wl == wl_temp) | 378 | if (wl == wl_temp) |
364 | break; | 379 | break; |
365 | } | 380 | } |
381 | mutex_unlock(&wl_list_mutex); | ||
366 | if (wl != wl_temp) | 382 | if (wl != wl_temp) |
367 | return NOTIFY_DONE; | 383 | return NOTIFY_DONE; |
368 | 384 | ||
@@ -438,15 +454,30 @@ static int wl1271_plt_init(struct wl1271 *wl) | |||
438 | struct conf_tx_tid *conf_tid; | 454 | struct conf_tx_tid *conf_tid; |
439 | int ret, i; | 455 | int ret, i; |
440 | 456 | ||
441 | ret = wl1271_cmd_general_parms(wl); | 457 | if (wl->chip.id == CHIP_ID_1283_PG20) |
458 | ret = wl128x_cmd_general_parms(wl); | ||
459 | else | ||
460 | ret = wl1271_cmd_general_parms(wl); | ||
442 | if (ret < 0) | 461 | if (ret < 0) |
443 | return ret; | 462 | return ret; |
444 | 463 | ||
445 | ret = wl1271_cmd_radio_parms(wl); | 464 | if (wl->chip.id == CHIP_ID_1283_PG20) |
465 | ret = wl128x_cmd_radio_parms(wl); | ||
466 | else | ||
467 | ret = wl1271_cmd_radio_parms(wl); | ||
468 | if (ret < 0) | ||
469 | return ret; | ||
470 | |||
471 | if (wl->chip.id != CHIP_ID_1283_PG20) { | ||
472 | ret = wl1271_cmd_ext_radio_parms(wl); | ||
473 | if (ret < 0) | ||
474 | return ret; | ||
475 | } | ||
446 | if (ret < 0) | 476 | if (ret < 0) |
447 | return ret; | 477 | return ret; |
448 | 478 | ||
449 | ret = wl1271_cmd_ext_radio_parms(wl); | 479 | /* Chip-specific initializations */ |
480 | ret = wl1271_chip_specific_init(wl); | ||
450 | if (ret < 0) | 481 | if (ret < 0) |
451 | return ret; | 482 | return ret; |
452 | 483 | ||
@@ -593,15 +624,17 @@ static void wl1271_fw_status(struct wl1271 *wl, | |||
593 | { | 624 | { |
594 | struct wl1271_fw_common_status *status = &full_status->common; | 625 | struct wl1271_fw_common_status *status = &full_status->common; |
595 | struct timespec ts; | 626 | struct timespec ts; |
596 | u32 total = 0; | 627 | u32 old_tx_blk_count = wl->tx_blocks_available; |
628 | u32 freed_blocks = 0; | ||
597 | int i; | 629 | int i; |
598 | 630 | ||
599 | if (wl->bss_type == BSS_TYPE_AP_BSS) | 631 | if (wl->bss_type == BSS_TYPE_AP_BSS) { |
600 | wl1271_raw_read(wl, FW_STATUS_ADDR, status, | 632 | wl1271_raw_read(wl, FW_STATUS_ADDR, status, |
601 | sizeof(struct wl1271_fw_ap_status), false); | 633 | sizeof(struct wl1271_fw_ap_status), false); |
602 | else | 634 | } else { |
603 | wl1271_raw_read(wl, FW_STATUS_ADDR, status, | 635 | wl1271_raw_read(wl, FW_STATUS_ADDR, status, |
604 | sizeof(struct wl1271_fw_sta_status), false); | 636 | sizeof(struct wl1271_fw_sta_status), false); |
637 | } | ||
605 | 638 | ||
606 | wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " | 639 | wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " |
607 | "drv_rx_counter = %d, tx_results_counter = %d)", | 640 | "drv_rx_counter = %d, tx_results_counter = %d)", |
@@ -612,22 +645,37 @@ static void wl1271_fw_status(struct wl1271 *wl, | |||
612 | 645 | ||
613 | /* update number of available TX blocks */ | 646 | /* update number of available TX blocks */ |
614 | for (i = 0; i < NUM_TX_QUEUES; i++) { | 647 | for (i = 0; i < NUM_TX_QUEUES; i++) { |
615 | u32 cnt = le32_to_cpu(status->tx_released_blks[i]) - | 648 | freed_blocks += le32_to_cpu(status->tx_released_blks[i]) - |
616 | wl->tx_blocks_freed[i]; | 649 | wl->tx_blocks_freed[i]; |
617 | 650 | ||
618 | wl->tx_blocks_freed[i] = | 651 | wl->tx_blocks_freed[i] = |
619 | le32_to_cpu(status->tx_released_blks[i]); | 652 | le32_to_cpu(status->tx_released_blks[i]); |
620 | wl->tx_blocks_available += cnt; | ||
621 | total += cnt; | ||
622 | } | 653 | } |
623 | 654 | ||
624 | /* if more blocks are available now, tx work can be scheduled */ | 655 | wl->tx_allocated_blocks -= freed_blocks; |
625 | if (total) | ||
626 | clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); | ||
627 | 656 | ||
628 | /* for AP update num of allocated TX blocks per link and ps status */ | 657 | if (wl->bss_type == BSS_TYPE_AP_BSS) { |
629 | if (wl->bss_type == BSS_TYPE_AP_BSS) | 658 | /* Update num of allocated TX blocks per link and ps status */ |
630 | wl1271_irq_update_links_status(wl, &full_status->ap); | 659 | wl1271_irq_update_links_status(wl, &full_status->ap); |
660 | wl->tx_blocks_available += freed_blocks; | ||
661 | } else { | ||
662 | int avail = full_status->sta.tx_total - wl->tx_allocated_blocks; | ||
663 | |||
664 | /* | ||
665 | * The FW might change the total number of TX memblocks before | ||
666 | * we get a notification about blocks being released. Thus, the | ||
667 | * available blocks calculation might yield a temporary result | ||
668 | * which is lower than the actual available blocks. Keeping in | ||
669 | * mind that only blocks that were allocated can be moved from | ||
670 | * TX to RX, tx_blocks_available should never decrease here. | ||
671 | */ | ||
672 | wl->tx_blocks_available = max((int)wl->tx_blocks_available, | ||
673 | avail); | ||
674 | } | ||
675 | |||
676 | /* if more blocks are available now, tx work can be scheduled */ | ||
677 | if (wl->tx_blocks_available > old_tx_blk_count) | ||
678 | clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); | ||
631 | 679 | ||
632 | /* update the host-chipset time offset */ | 680 | /* update the host-chipset time offset */ |
633 | getnstimeofday(&ts); | 681 | getnstimeofday(&ts); |
@@ -674,6 +722,13 @@ irqreturn_t wl1271_irq(int irq, void *cookie) | |||
674 | set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); | 722 | set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); |
675 | cancel_work_sync(&wl->tx_work); | 723 | cancel_work_sync(&wl->tx_work); |
676 | 724 | ||
725 | /* | ||
726 | * In case edge triggered interrupt must be used, we cannot iterate | ||
727 | * more than once without introducing race conditions with the hardirq. | ||
728 | */ | ||
729 | if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) | ||
730 | loopcount = 1; | ||
731 | |||
677 | mutex_lock(&wl->mutex); | 732 | mutex_lock(&wl->mutex); |
678 | 733 | ||
679 | wl1271_debug(DEBUG_IRQ, "IRQ work"); | 734 | wl1271_debug(DEBUG_IRQ, "IRQ work"); |
@@ -785,11 +840,17 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) | |||
785 | 840 | ||
786 | switch (wl->bss_type) { | 841 | switch (wl->bss_type) { |
787 | case BSS_TYPE_AP_BSS: | 842 | case BSS_TYPE_AP_BSS: |
788 | fw_name = WL1271_AP_FW_NAME; | 843 | if (wl->chip.id == CHIP_ID_1283_PG20) |
844 | fw_name = WL128X_AP_FW_NAME; | ||
845 | else | ||
846 | fw_name = WL127X_AP_FW_NAME; | ||
789 | break; | 847 | break; |
790 | case BSS_TYPE_IBSS: | 848 | case BSS_TYPE_IBSS: |
791 | case BSS_TYPE_STA_BSS: | 849 | case BSS_TYPE_STA_BSS: |
792 | fw_name = WL1271_FW_NAME; | 850 | if (wl->chip.id == CHIP_ID_1283_PG20) |
851 | fw_name = WL128X_FW_NAME; | ||
852 | else | ||
853 | fw_name = WL1271_FW_NAME; | ||
793 | break; | 854 | break; |
794 | default: | 855 | default: |
795 | wl1271_error("no compatible firmware for bss_type %d", | 856 | wl1271_error("no compatible firmware for bss_type %d", |
@@ -838,14 +899,14 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) | |||
838 | const struct firmware *fw; | 899 | const struct firmware *fw; |
839 | int ret; | 900 | int ret; |
840 | 901 | ||
841 | ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl)); | 902 | ret = request_firmware(&fw, WL12XX_NVS_NAME, wl1271_wl_to_dev(wl)); |
842 | 903 | ||
843 | if (ret < 0) { | 904 | if (ret < 0) { |
844 | wl1271_error("could not get nvs file: %d", ret); | 905 | wl1271_error("could not get nvs file: %d", ret); |
845 | return ret; | 906 | return ret; |
846 | } | 907 | } |
847 | 908 | ||
848 | wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL); | 909 | wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL); |
849 | 910 | ||
850 | if (!wl->nvs) { | 911 | if (!wl->nvs) { |
851 | wl1271_error("could not allocate memory for the nvs file"); | 912 | wl1271_error("could not allocate memory for the nvs file"); |
@@ -954,6 +1015,17 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) | |||
954 | if (ret < 0) | 1015 | if (ret < 0) |
955 | goto out; | 1016 | goto out; |
956 | break; | 1017 | break; |
1018 | case CHIP_ID_1283_PG20: | ||
1019 | wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)", | ||
1020 | wl->chip.id); | ||
1021 | |||
1022 | ret = wl1271_setup(wl); | ||
1023 | if (ret < 0) | ||
1024 | goto out; | ||
1025 | if (wl1271_set_block_size(wl)) | ||
1026 | wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT; | ||
1027 | break; | ||
1028 | case CHIP_ID_1283_PG10: | ||
957 | default: | 1029 | default: |
958 | wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); | 1030 | wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); |
959 | ret = -ENODEV; | 1031 | ret = -ENODEV; |
@@ -978,6 +1050,24 @@ out: | |||
978 | return ret; | 1050 | return ret; |
979 | } | 1051 | } |
980 | 1052 | ||
1053 | static unsigned int wl1271_get_fw_ver_quirks(struct wl1271 *wl) | ||
1054 | { | ||
1055 | unsigned int quirks = 0; | ||
1056 | unsigned int *fw_ver = wl->chip.fw_ver; | ||
1057 | |||
1058 | /* Only for wl127x */ | ||
1059 | if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) && | ||
1060 | /* Check STA version */ | ||
1061 | (((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) && | ||
1062 | (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) || | ||
1063 | /* Check AP version */ | ||
1064 | ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) && | ||
1065 | (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN)))) | ||
1066 | quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS; | ||
1067 | |||
1068 | return quirks; | ||
1069 | } | ||
1070 | |||
981 | int wl1271_plt_start(struct wl1271 *wl) | 1071 | int wl1271_plt_start(struct wl1271 *wl) |
982 | { | 1072 | { |
983 | int retries = WL1271_BOOT_RETRIES; | 1073 | int retries = WL1271_BOOT_RETRIES; |
@@ -1013,6 +1103,9 @@ int wl1271_plt_start(struct wl1271 *wl) | |||
1013 | wl->state = WL1271_STATE_PLT; | 1103 | wl->state = WL1271_STATE_PLT; |
1014 | wl1271_notice("firmware booted in PLT mode (%s)", | 1104 | wl1271_notice("firmware booted in PLT mode (%s)", |
1015 | wl->chip.fw_ver_str); | 1105 | wl->chip.fw_ver_str); |
1106 | |||
1107 | /* Check if any quirks are needed with older fw versions */ | ||
1108 | wl->quirks |= wl1271_get_fw_ver_quirks(wl); | ||
1016 | goto out; | 1109 | goto out; |
1017 | 1110 | ||
1018 | irq_disable: | 1111 | irq_disable: |
@@ -1040,7 +1133,7 @@ out: | |||
1040 | return ret; | 1133 | return ret; |
1041 | } | 1134 | } |
1042 | 1135 | ||
1043 | int __wl1271_plt_stop(struct wl1271 *wl) | 1136 | static int __wl1271_plt_stop(struct wl1271 *wl) |
1044 | { | 1137 | { |
1045 | int ret = 0; | 1138 | int ret = 0; |
1046 | 1139 | ||
@@ -1124,6 +1217,69 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
1124 | spin_unlock_irqrestore(&wl->wl_lock, flags); | 1217 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
1125 | } | 1218 | } |
1126 | 1219 | ||
1220 | int wl1271_tx_dummy_packet(struct wl1271 *wl) | ||
1221 | { | ||
1222 | unsigned long flags; | ||
1223 | |||
1224 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
1225 | set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); | ||
1226 | wl->tx_queue_count++; | ||
1227 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
1228 | |||
1229 | /* The FW is low on RX memory blocks, so send the dummy packet asap */ | ||
1230 | if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) | ||
1231 | wl1271_tx_work_locked(wl); | ||
1232 | |||
1233 | /* | ||
1234 | * If the FW TX is busy, TX work will be scheduled by the threaded | ||
1235 | * interrupt handler function | ||
1236 | */ | ||
1237 | return 0; | ||
1238 | } | ||
1239 | |||
1240 | /* | ||
1241 | * The size of the dummy packet should be at least 1400 bytes. However, in | ||
1242 | * order to minimize the number of bus transactions, aligning it to 512 bytes | ||
1243 | * boundaries could be beneficial, performance wise | ||
1244 | */ | ||
1245 | #define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512)) | ||
1246 | |||
1247 | static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) | ||
1248 | { | ||
1249 | struct sk_buff *skb; | ||
1250 | struct ieee80211_hdr_3addr *hdr; | ||
1251 | unsigned int dummy_packet_size; | ||
1252 | |||
1253 | dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE - | ||
1254 | sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr); | ||
1255 | |||
1256 | skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE); | ||
1257 | if (!skb) { | ||
1258 | wl1271_warning("Failed to allocate a dummy packet skb"); | ||
1259 | return NULL; | ||
1260 | } | ||
1261 | |||
1262 | skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr)); | ||
1263 | |||
1264 | hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr)); | ||
1265 | memset(hdr, 0, sizeof(*hdr)); | ||
1266 | hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||
1267 | IEEE80211_STYPE_NULLFUNC | | ||
1268 | IEEE80211_FCTL_TODS); | ||
1269 | |||
1270 | memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size); | ||
1271 | |||
1272 | /* Dummy packets require the TID to be management */ | ||
1273 | skb->priority = WL1271_TID_MGMT; | ||
1274 | |||
1275 | /* Initialize all fields that might be used */ | ||
1276 | skb->queue_mapping = 0; | ||
1277 | memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); | ||
1278 | |||
1279 | return skb; | ||
1280 | } | ||
1281 | |||
1282 | |||
1127 | static struct notifier_block wl1271_dev_notifier = { | 1283 | static struct notifier_block wl1271_dev_notifier = { |
1128 | .notifier_call = wl1271_dev_notify, | 1284 | .notifier_call = wl1271_dev_notify, |
1129 | }; | 1285 | }; |
@@ -1174,6 +1330,16 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, | |||
1174 | goto out; | 1330 | goto out; |
1175 | } | 1331 | } |
1176 | 1332 | ||
1333 | /* | ||
1334 | * in some very corner case HW recovery scenarios its possible to | ||
1335 | * get here before __wl1271_op_remove_interface is complete, so | ||
1336 | * opt out if that is the case. | ||
1337 | */ | ||
1338 | if (test_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags)) { | ||
1339 | ret = -EBUSY; | ||
1340 | goto out; | ||
1341 | } | ||
1342 | |||
1177 | switch (vif->type) { | 1343 | switch (vif->type) { |
1178 | case NL80211_IFTYPE_STATION: | 1344 | case NL80211_IFTYPE_STATION: |
1179 | wl->bss_type = BSS_TYPE_STA_BSS; | 1345 | wl->bss_type = BSS_TYPE_STA_BSS; |
@@ -1242,6 +1408,7 @@ power_off: | |||
1242 | 1408 | ||
1243 | wl->vif = vif; | 1409 | wl->vif = vif; |
1244 | wl->state = WL1271_STATE_ON; | 1410 | wl->state = WL1271_STATE_ON; |
1411 | set_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags); | ||
1245 | wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str); | 1412 | wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str); |
1246 | 1413 | ||
1247 | /* update hw/fw version info in wiphy struct */ | 1414 | /* update hw/fw version info in wiphy struct */ |
@@ -1249,6 +1416,9 @@ power_off: | |||
1249 | strncpy(wiphy->fw_version, wl->chip.fw_ver_str, | 1416 | strncpy(wiphy->fw_version, wl->chip.fw_ver_str, |
1250 | sizeof(wiphy->fw_version)); | 1417 | sizeof(wiphy->fw_version)); |
1251 | 1418 | ||
1419 | /* Check if any quirks are needed with older fw versions */ | ||
1420 | wl->quirks |= wl1271_get_fw_ver_quirks(wl); | ||
1421 | |||
1252 | /* | 1422 | /* |
1253 | * Now we know if 11a is supported (info from the NVS), so disable | 1423 | * Now we know if 11a is supported (info from the NVS), so disable |
1254 | * 11a channels if not supported | 1424 | * 11a channels if not supported |
@@ -1262,8 +1432,10 @@ power_off: | |||
1262 | out: | 1432 | out: |
1263 | mutex_unlock(&wl->mutex); | 1433 | mutex_unlock(&wl->mutex); |
1264 | 1434 | ||
1435 | mutex_lock(&wl_list_mutex); | ||
1265 | if (!ret) | 1436 | if (!ret) |
1266 | list_add(&wl->list, &wl_list); | 1437 | list_add(&wl->list, &wl_list); |
1438 | mutex_unlock(&wl_list_mutex); | ||
1267 | 1439 | ||
1268 | return ret; | 1440 | return ret; |
1269 | } | 1441 | } |
@@ -1274,11 +1446,15 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) | |||
1274 | 1446 | ||
1275 | wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); | 1447 | wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); |
1276 | 1448 | ||
1449 | /* because of hardware recovery, we may get here twice */ | ||
1450 | if (wl->state != WL1271_STATE_ON) | ||
1451 | return; | ||
1452 | |||
1277 | wl1271_info("down"); | 1453 | wl1271_info("down"); |
1278 | 1454 | ||
1455 | mutex_lock(&wl_list_mutex); | ||
1279 | list_del(&wl->list); | 1456 | list_del(&wl->list); |
1280 | 1457 | mutex_unlock(&wl_list_mutex); | |
1281 | WARN_ON(wl->state != WL1271_STATE_ON); | ||
1282 | 1458 | ||
1283 | /* enable dyn ps just in case (if left on due to fw crash etc) */ | 1459 | /* enable dyn ps just in case (if left on due to fw crash etc) */ |
1284 | if (wl->bss_type == BSS_TYPE_STA_BSS) | 1460 | if (wl->bss_type == BSS_TYPE_STA_BSS) |
@@ -1286,12 +1462,15 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) | |||
1286 | 1462 | ||
1287 | if (wl->scan.state != WL1271_SCAN_STATE_IDLE) { | 1463 | if (wl->scan.state != WL1271_SCAN_STATE_IDLE) { |
1288 | wl->scan.state = WL1271_SCAN_STATE_IDLE; | 1464 | wl->scan.state = WL1271_SCAN_STATE_IDLE; |
1289 | kfree(wl->scan.scanned_ch); | 1465 | memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); |
1290 | wl->scan.scanned_ch = NULL; | ||
1291 | wl->scan.req = NULL; | 1466 | wl->scan.req = NULL; |
1292 | ieee80211_scan_completed(wl->hw, true); | 1467 | ieee80211_scan_completed(wl->hw, true); |
1293 | } | 1468 | } |
1294 | 1469 | ||
1470 | /* | ||
1471 | * this must be before the cancel_work calls below, so that the work | ||
1472 | * functions don't perform further work. | ||
1473 | */ | ||
1295 | wl->state = WL1271_STATE_OFF; | 1474 | wl->state = WL1271_STATE_OFF; |
1296 | 1475 | ||
1297 | mutex_unlock(&wl->mutex); | 1476 | mutex_unlock(&wl->mutex); |
@@ -1321,6 +1500,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) | |||
1321 | wl->psm_entry_retry = 0; | 1500 | wl->psm_entry_retry = 0; |
1322 | wl->power_level = WL1271_DEFAULT_POWER_LEVEL; | 1501 | wl->power_level = WL1271_DEFAULT_POWER_LEVEL; |
1323 | wl->tx_blocks_available = 0; | 1502 | wl->tx_blocks_available = 0; |
1503 | wl->tx_allocated_blocks = 0; | ||
1324 | wl->tx_results_count = 0; | 1504 | wl->tx_results_count = 0; |
1325 | wl->tx_packets_count = 0; | 1505 | wl->tx_packets_count = 0; |
1326 | wl->tx_security_last_seq = 0; | 1506 | wl->tx_security_last_seq = 0; |
@@ -1328,7 +1508,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) | |||
1328 | wl->time_offset = 0; | 1508 | wl->time_offset = 0; |
1329 | wl->session_counter = 0; | 1509 | wl->session_counter = 0; |
1330 | wl->rate_set = CONF_TX_RATE_MASK_BASIC; | 1510 | wl->rate_set = CONF_TX_RATE_MASK_BASIC; |
1331 | wl->flags = 0; | ||
1332 | wl->vif = NULL; | 1511 | wl->vif = NULL; |
1333 | wl->filters = 0; | 1512 | wl->filters = 0; |
1334 | wl1271_free_ap_keys(wl); | 1513 | wl1271_free_ap_keys(wl); |
@@ -1336,6 +1515,13 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) | |||
1336 | wl->ap_fw_ps_map = 0; | 1515 | wl->ap_fw_ps_map = 0; |
1337 | wl->ap_ps_map = 0; | 1516 | wl->ap_ps_map = 0; |
1338 | 1517 | ||
1518 | /* | ||
1519 | * this is performed after the cancel_work calls and the associated | ||
1520 | * mutex_lock, so that wl1271_op_add_interface does not accidentally | ||
1521 | * get executed before all these vars have been reset. | ||
1522 | */ | ||
1523 | wl->flags = 0; | ||
1524 | |||
1339 | for (i = 0; i < NUM_TX_QUEUES; i++) | 1525 | for (i = 0; i < NUM_TX_QUEUES; i++) |
1340 | wl->tx_blocks_freed[i] = 0; | 1526 | wl->tx_blocks_freed[i] = 0; |
1341 | 1527 | ||
@@ -1368,7 +1554,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, | |||
1368 | cancel_work_sync(&wl->recovery_work); | 1554 | cancel_work_sync(&wl->recovery_work); |
1369 | } | 1555 | } |
1370 | 1556 | ||
1371 | static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters) | 1557 | void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters) |
1372 | { | 1558 | { |
1373 | wl1271_set_default_filters(wl); | 1559 | wl1271_set_default_filters(wl); |
1374 | 1560 | ||
@@ -1431,10 +1617,10 @@ static int wl1271_join(struct wl1271 *wl, bool set_assoc) | |||
1431 | * One of the side effects of the JOIN command is that is clears | 1617 | * One of the side effects of the JOIN command is that is clears |
1432 | * WPA/WPA2 keys from the chipset. Performing a JOIN while associated | 1618 | * WPA/WPA2 keys from the chipset. Performing a JOIN while associated |
1433 | * to a WPA/WPA2 access point will therefore kill the data-path. | 1619 | * to a WPA/WPA2 access point will therefore kill the data-path. |
1434 | * Currently there is no supported scenario for JOIN during | 1620 | * Currently the only valid scenario for JOIN during association |
1435 | * association - if it becomes a supported scenario, the WPA/WPA2 keys | 1621 | * is on roaming, in which case we will also be given new keys. |
1436 | * must be handled somehow. | 1622 | * Keep the below message for now, unless it starts bothering |
1437 | * | 1623 | * users who really like to roam a lot :) |
1438 | */ | 1624 | */ |
1439 | if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) | 1625 | if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) |
1440 | wl1271_info("JOIN while associated."); | 1626 | wl1271_info("JOIN while associated."); |
@@ -1490,7 +1676,7 @@ static int wl1271_unjoin(struct wl1271 *wl) | |||
1490 | clear_bit(WL1271_FLAG_JOINED, &wl->flags); | 1676 | clear_bit(WL1271_FLAG_JOINED, &wl->flags); |
1491 | memset(wl->bssid, 0, ETH_ALEN); | 1677 | memset(wl->bssid, 0, ETH_ALEN); |
1492 | 1678 | ||
1493 | /* stop filterting packets based on bssid */ | 1679 | /* stop filtering packets based on bssid */ |
1494 | wl1271_configure_filters(wl, FIF_OTHER_BSS); | 1680 | wl1271_configure_filters(wl, FIF_OTHER_BSS); |
1495 | 1681 | ||
1496 | out: | 1682 | out: |
@@ -1569,7 +1755,12 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) | |||
1569 | mutex_lock(&wl->mutex); | 1755 | mutex_lock(&wl->mutex); |
1570 | 1756 | ||
1571 | if (unlikely(wl->state == WL1271_STATE_OFF)) { | 1757 | if (unlikely(wl->state == WL1271_STATE_OFF)) { |
1572 | ret = -EAGAIN; | 1758 | /* we support configuring the channel and band while off */ |
1759 | if ((changed & IEEE80211_CONF_CHANGE_CHANNEL)) { | ||
1760 | wl->band = conf->channel->band; | ||
1761 | wl->channel = channel; | ||
1762 | } | ||
1763 | |||
1573 | goto out; | 1764 | goto out; |
1574 | } | 1765 | } |
1575 | 1766 | ||
@@ -2650,32 +2841,31 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, | |||
2650 | conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY; | 2841 | conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY; |
2651 | conf_tid->apsd_conf[0] = 0; | 2842 | conf_tid->apsd_conf[0] = 0; |
2652 | conf_tid->apsd_conf[1] = 0; | 2843 | conf_tid->apsd_conf[1] = 0; |
2653 | } else { | 2844 | goto out; |
2654 | ret = wl1271_ps_elp_wakeup(wl); | 2845 | } |
2655 | if (ret < 0) | ||
2656 | goto out; | ||
2657 | 2846 | ||
2658 | /* | 2847 | ret = wl1271_ps_elp_wakeup(wl); |
2659 | * the txop is confed in units of 32us by the mac80211, | 2848 | if (ret < 0) |
2660 | * we need us | 2849 | goto out; |
2661 | */ | ||
2662 | ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue), | ||
2663 | params->cw_min, params->cw_max, | ||
2664 | params->aifs, params->txop << 5); | ||
2665 | if (ret < 0) | ||
2666 | goto out_sleep; | ||
2667 | 2850 | ||
2668 | ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), | 2851 | /* |
2669 | CONF_CHANNEL_TYPE_EDCF, | 2852 | * the txop is confed in units of 32us by the mac80211, |
2670 | wl1271_tx_get_queue(queue), | 2853 | * we need us |
2671 | ps_scheme, CONF_ACK_POLICY_LEGACY, | 2854 | */ |
2672 | 0, 0); | 2855 | ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue), |
2673 | if (ret < 0) | 2856 | params->cw_min, params->cw_max, |
2674 | goto out_sleep; | 2857 | params->aifs, params->txop << 5); |
2858 | if (ret < 0) | ||
2859 | goto out_sleep; | ||
2860 | |||
2861 | ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), | ||
2862 | CONF_CHANNEL_TYPE_EDCF, | ||
2863 | wl1271_tx_get_queue(queue), | ||
2864 | ps_scheme, CONF_ACK_POLICY_LEGACY, | ||
2865 | 0, 0); | ||
2675 | 2866 | ||
2676 | out_sleep: | 2867 | out_sleep: |
2677 | wl1271_ps_elp_sleep(wl); | 2868 | wl1271_ps_elp_sleep(wl); |
2678 | } | ||
2679 | 2869 | ||
2680 | out: | 2870 | out: |
2681 | mutex_unlock(&wl->mutex); | 2871 | mutex_unlock(&wl->mutex); |
@@ -2764,6 +2954,12 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) | |||
2764 | __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); | 2954 | __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); |
2765 | } | 2955 | } |
2766 | 2956 | ||
2957 | bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid) | ||
2958 | { | ||
2959 | int id = hlid - WL1271_AP_STA_HLID_START; | ||
2960 | return test_bit(id, wl->ap_hlid_map); | ||
2961 | } | ||
2962 | |||
2767 | static int wl1271_op_sta_add(struct ieee80211_hw *hw, | 2963 | static int wl1271_op_sta_add(struct ieee80211_hw *hw, |
2768 | struct ieee80211_vif *vif, | 2964 | struct ieee80211_vif *vif, |
2769 | struct ieee80211_sta *sta) | 2965 | struct ieee80211_sta *sta) |
@@ -2847,10 +3043,11 @@ out: | |||
2847 | return ret; | 3043 | return ret; |
2848 | } | 3044 | } |
2849 | 3045 | ||
2850 | int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | 3046 | static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, |
2851 | enum ieee80211_ampdu_mlme_action action, | 3047 | struct ieee80211_vif *vif, |
2852 | struct ieee80211_sta *sta, u16 tid, u16 *ssn, | 3048 | enum ieee80211_ampdu_mlme_action action, |
2853 | u8 buf_size) | 3049 | struct ieee80211_sta *sta, u16 tid, u16 *ssn, |
3050 | u8 buf_size) | ||
2854 | { | 3051 | { |
2855 | struct wl1271 *wl = hw->priv; | 3052 | struct wl1271 *wl = hw->priv; |
2856 | int ret; | 3053 | int ret; |
@@ -3003,7 +3200,8 @@ static const u8 wl1271_rate_to_idx_2ghz[] = { | |||
3003 | 3200 | ||
3004 | #ifdef CONFIG_WL12XX_HT | 3201 | #ifdef CONFIG_WL12XX_HT |
3005 | #define WL12XX_HT_CAP { \ | 3202 | #define WL12XX_HT_CAP { \ |
3006 | .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20, \ | 3203 | .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | \ |
3204 | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \ | ||
3007 | .ht_supported = true, \ | 3205 | .ht_supported = true, \ |
3008 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \ | 3206 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \ |
3009 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \ | 3207 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \ |
@@ -3207,8 +3405,7 @@ static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, | |||
3207 | unsigned long res; | 3405 | unsigned long res; |
3208 | int ret; | 3406 | int ret; |
3209 | 3407 | ||
3210 | ret = strict_strtoul(buf, 10, &res); | 3408 | ret = kstrtoul(buf, 10, &res); |
3211 | |||
3212 | if (ret < 0) { | 3409 | if (ret < 0) { |
3213 | wl1271_warning("incorrect value written to bt_coex_mode"); | 3410 | wl1271_warning("incorrect value written to bt_coex_mode"); |
3214 | return count; | 3411 | return count; |
@@ -3273,7 +3470,11 @@ int wl1271_register_hw(struct wl1271 *wl) | |||
3273 | 3470 | ||
3274 | ret = wl1271_fetch_nvs(wl); | 3471 | ret = wl1271_fetch_nvs(wl); |
3275 | if (ret == 0) { | 3472 | if (ret == 0) { |
3276 | u8 *nvs_ptr = (u8 *)wl->nvs->nvs; | 3473 | /* NOTE: The wl->nvs->nvs element must be first, in |
3474 | * order to simplify the casting, we assume it is at | ||
3475 | * the beginning of the wl->nvs structure. | ||
3476 | */ | ||
3477 | u8 *nvs_ptr = (u8 *)wl->nvs; | ||
3277 | 3478 | ||
3278 | wl->mac_addr[0] = nvs_ptr[11]; | 3479 | wl->mac_addr[0] = nvs_ptr[11]; |
3279 | wl->mac_addr[1] = nvs_ptr[10]; | 3480 | wl->mac_addr[1] = nvs_ptr[10]; |
@@ -3341,7 +3542,6 @@ int wl1271_init_ieee80211(struct wl1271 *wl) | |||
3341 | IEEE80211_HW_HAS_RATE_CONTROL | | 3542 | IEEE80211_HW_HAS_RATE_CONTROL | |
3342 | IEEE80211_HW_CONNECTION_MONITOR | | 3543 | IEEE80211_HW_CONNECTION_MONITOR | |
3343 | IEEE80211_HW_SUPPORTS_CQM_RSSI | | 3544 | IEEE80211_HW_SUPPORTS_CQM_RSSI | |
3344 | IEEE80211_HW_REPORTS_TX_ACK_STATUS | | ||
3345 | IEEE80211_HW_AP_LINK_PS; | 3545 | IEEE80211_HW_AP_LINK_PS; |
3346 | 3546 | ||
3347 | wl->hw->wiphy->cipher_suites = cipher_suites; | 3547 | wl->hw->wiphy->cipher_suites = cipher_suites; |
@@ -3358,6 +3558,10 @@ int wl1271_init_ieee80211(struct wl1271 *wl) | |||
3358 | wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - | 3558 | wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - |
3359 | sizeof(struct ieee80211_header); | 3559 | sizeof(struct ieee80211_header); |
3360 | 3560 | ||
3561 | /* make sure all our channels fit in the scanned_ch bitmask */ | ||
3562 | BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + | ||
3563 | ARRAY_SIZE(wl1271_channels_5ghz) > | ||
3564 | WL1271_MAX_CHANNELS); | ||
3361 | /* | 3565 | /* |
3362 | * We keep local copies of the band structs because we need to | 3566 | * We keep local copies of the band structs because we need to |
3363 | * modify them on a per-device basis. | 3567 | * modify them on a per-device basis. |
@@ -3458,6 +3662,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
3458 | wl->ap_ps_map = 0; | 3662 | wl->ap_ps_map = 0; |
3459 | wl->ap_fw_ps_map = 0; | 3663 | wl->ap_fw_ps_map = 0; |
3460 | wl->quirks = 0; | 3664 | wl->quirks = 0; |
3665 | wl->platform_quirks = 0; | ||
3461 | 3666 | ||
3462 | memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); | 3667 | memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); |
3463 | for (i = 0; i < ACX_TX_DESCRIPTORS; i++) | 3668 | for (i = 0; i < ACX_TX_DESCRIPTORS; i++) |
@@ -3478,11 +3683,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
3478 | goto err_hw; | 3683 | goto err_hw; |
3479 | } | 3684 | } |
3480 | 3685 | ||
3686 | wl->dummy_packet = wl12xx_alloc_dummy_packet(wl); | ||
3687 | if (!wl->dummy_packet) { | ||
3688 | ret = -ENOMEM; | ||
3689 | goto err_aggr; | ||
3690 | } | ||
3691 | |||
3481 | /* Register platform device */ | 3692 | /* Register platform device */ |
3482 | ret = platform_device_register(wl->plat_dev); | 3693 | ret = platform_device_register(wl->plat_dev); |
3483 | if (ret) { | 3694 | if (ret) { |
3484 | wl1271_error("couldn't register platform device"); | 3695 | wl1271_error("couldn't register platform device"); |
3485 | goto err_aggr; | 3696 | goto err_dummy_packet; |
3486 | } | 3697 | } |
3487 | dev_set_drvdata(&wl->plat_dev->dev, wl); | 3698 | dev_set_drvdata(&wl->plat_dev->dev, wl); |
3488 | 3699 | ||
@@ -3508,6 +3719,9 @@ err_bt_coex_state: | |||
3508 | err_platform: | 3719 | err_platform: |
3509 | platform_device_unregister(wl->plat_dev); | 3720 | platform_device_unregister(wl->plat_dev); |
3510 | 3721 | ||
3722 | err_dummy_packet: | ||
3723 | dev_kfree_skb(wl->dummy_packet); | ||
3724 | |||
3511 | err_aggr: | 3725 | err_aggr: |
3512 | free_pages((unsigned long)wl->aggr_buf, order); | 3726 | free_pages((unsigned long)wl->aggr_buf, order); |
3513 | 3727 | ||
@@ -3527,6 +3741,7 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); | |||
3527 | int wl1271_free_hw(struct wl1271 *wl) | 3741 | int wl1271_free_hw(struct wl1271 *wl) |
3528 | { | 3742 | { |
3529 | platform_device_unregister(wl->plat_dev); | 3743 | platform_device_unregister(wl->plat_dev); |
3744 | dev_kfree_skb(wl->dummy_packet); | ||
3530 | free_pages((unsigned long)wl->aggr_buf, | 3745 | free_pages((unsigned long)wl->aggr_buf, |
3531 | get_order(WL1271_AGGR_BUFFER_SIZE)); | 3746 | get_order(WL1271_AGGR_BUFFER_SIZE)); |
3532 | kfree(wl->plat_dev); | 3747 | kfree(wl->plat_dev); |