diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-07-06 15:03:16 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-07-06 15:03:16 -0400 |
commit | 333c0dbfcd7847d02d75a2d2a80d2f5e1cdc51dc (patch) | |
tree | fa00cbe82993c2aa8cb9f52d172a89eaaefb0e0f /drivers | |
parent | 115f9450babbf2ed530db04e16a99df28cec85dd (diff) | |
parent | 95dac04f881322b510c45e5ae83f0dbee4f823a2 (diff) |
Merge branch 'for-linville' of git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx
Diffstat (limited to 'drivers')
24 files changed, 1176 insertions, 244 deletions
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 35ce7b0f4a60..07bcb1548d8b 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig | |||
@@ -11,7 +11,6 @@ config WL12XX | |||
11 | depends on WL12XX_MENU && GENERIC_HARDIRQS | 11 | depends on WL12XX_MENU && GENERIC_HARDIRQS |
12 | depends on INET | 12 | depends on INET |
13 | select FW_LOADER | 13 | select FW_LOADER |
14 | select CRC7 | ||
15 | ---help--- | 14 | ---help--- |
16 | This module adds support for wireless adapters based on TI wl1271 and | 15 | This module adds support for wireless adapters based on TI wl1271 and |
17 | TI wl1273 chipsets. This module does *not* include support for wl1251. | 16 | TI wl1273 chipsets. This module does *not* include support for wl1251. |
@@ -33,6 +32,7 @@ config WL12XX_HT | |||
33 | config WL12XX_SPI | 32 | config WL12XX_SPI |
34 | tristate "TI wl12xx SPI support" | 33 | tristate "TI wl12xx SPI support" |
35 | depends on WL12XX && SPI_MASTER | 34 | depends on WL12XX && SPI_MASTER |
35 | select CRC7 | ||
36 | ---help--- | 36 | ---help--- |
37 | This module adds support for the SPI interface of adapters using | 37 | This module adds support for the SPI interface of adapters using |
38 | TI wl12xx chipsets. Select this if your platform is using | 38 | TI wl12xx chipsets. Select this if your platform is using |
diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index c6ee530e5bf7..87caa94fd815 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c | |||
@@ -25,7 +25,6 @@ | |||
25 | 25 | ||
26 | #include <linux/module.h> | 26 | #include <linux/module.h> |
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/crc7.h> | ||
29 | #include <linux/spi/spi.h> | 28 | #include <linux/spi/spi.h> |
30 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
31 | 30 | ||
@@ -1068,6 +1067,7 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) | |||
1068 | mem_conf->tx_free_req = mem->min_req_tx_blocks; | 1067 | mem_conf->tx_free_req = mem->min_req_tx_blocks; |
1069 | mem_conf->rx_free_req = mem->min_req_rx_blocks; | 1068 | mem_conf->rx_free_req = mem->min_req_rx_blocks; |
1070 | mem_conf->tx_min = mem->tx_min; | 1069 | mem_conf->tx_min = mem->tx_min; |
1070 | mem_conf->fwlog_blocks = wl->conf.fwlog.mem_blocks; | ||
1071 | 1071 | ||
1072 | ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, | 1072 | ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, |
1073 | sizeof(*mem_conf)); | 1073 | sizeof(*mem_conf)); |
@@ -1577,6 +1577,53 @@ out: | |||
1577 | return ret; | 1577 | return ret; |
1578 | } | 1578 | } |
1579 | 1579 | ||
1580 | int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable) | ||
1581 | { | ||
1582 | struct wl1271_acx_ps_rx_streaming *rx_streaming; | ||
1583 | u32 conf_queues, enable_queues; | ||
1584 | int i, ret = 0; | ||
1585 | |||
1586 | wl1271_debug(DEBUG_ACX, "acx ps rx streaming"); | ||
1587 | |||
1588 | rx_streaming = kzalloc(sizeof(*rx_streaming), GFP_KERNEL); | ||
1589 | if (!rx_streaming) { | ||
1590 | ret = -ENOMEM; | ||
1591 | goto out; | ||
1592 | } | ||
1593 | |||
1594 | conf_queues = wl->conf.rx_streaming.queues; | ||
1595 | if (enable) | ||
1596 | enable_queues = conf_queues; | ||
1597 | else | ||
1598 | enable_queues = 0; | ||
1599 | |||
1600 | for (i = 0; i < 8; i++) { | ||
1601 | /* | ||
1602 | * Skip non-changed queues, to avoid redundant acxs. | ||
1603 | * this check assumes conf.rx_streaming.queues can't | ||
1604 | * be changed while rx_streaming is enabled. | ||
1605 | */ | ||
1606 | if (!(conf_queues & BIT(i))) | ||
1607 | continue; | ||
1608 | |||
1609 | rx_streaming->tid = i; | ||
1610 | rx_streaming->enable = enable_queues & BIT(i); | ||
1611 | rx_streaming->period = wl->conf.rx_streaming.interval; | ||
1612 | rx_streaming->timeout = wl->conf.rx_streaming.interval; | ||
1613 | |||
1614 | ret = wl1271_cmd_configure(wl, ACX_PS_RX_STREAMING, | ||
1615 | rx_streaming, | ||
1616 | sizeof(*rx_streaming)); | ||
1617 | if (ret < 0) { | ||
1618 | wl1271_warning("acx ps rx streaming failed: %d", ret); | ||
1619 | goto out; | ||
1620 | } | ||
1621 | } | ||
1622 | out: | ||
1623 | kfree(rx_streaming); | ||
1624 | return ret; | ||
1625 | } | ||
1626 | |||
1580 | int wl1271_acx_max_tx_retry(struct wl1271 *wl) | 1627 | int wl1271_acx_max_tx_retry(struct wl1271 *wl) |
1581 | { | 1628 | { |
1582 | struct wl1271_acx_max_tx_retry *acx = NULL; | 1629 | struct wl1271_acx_max_tx_retry *acx = NULL; |
diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 9a895e3cc613..d303265f163a 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h | |||
@@ -828,6 +828,8 @@ struct wl1271_acx_sta_config_memory { | |||
828 | u8 tx_free_req; | 828 | u8 tx_free_req; |
829 | u8 rx_free_req; | 829 | u8 rx_free_req; |
830 | u8 tx_min; | 830 | u8 tx_min; |
831 | u8 fwlog_blocks; | ||
832 | u8 padding[3]; | ||
831 | } __packed; | 833 | } __packed; |
832 | 834 | ||
833 | struct wl1271_acx_mem_map { | 835 | struct wl1271_acx_mem_map { |
@@ -1153,6 +1155,19 @@ struct wl1271_acx_fw_tsf_information { | |||
1153 | u8 padding[3]; | 1155 | u8 padding[3]; |
1154 | } __packed; | 1156 | } __packed; |
1155 | 1157 | ||
1158 | struct wl1271_acx_ps_rx_streaming { | ||
1159 | struct acx_header header; | ||
1160 | |||
1161 | u8 tid; | ||
1162 | u8 enable; | ||
1163 | |||
1164 | /* interval between triggers (10-100 msec) */ | ||
1165 | u8 period; | ||
1166 | |||
1167 | /* timeout before first trigger (0-200 msec) */ | ||
1168 | u8 timeout; | ||
1169 | } __packed; | ||
1170 | |||
1156 | struct wl1271_acx_max_tx_retry { | 1171 | struct wl1271_acx_max_tx_retry { |
1157 | struct acx_header header; | 1172 | struct acx_header header; |
1158 | 1173 | ||
@@ -1384,6 +1399,7 @@ int wl1271_acx_set_ba_session(struct wl1271 *wl, | |||
1384 | int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, | 1399 | int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, |
1385 | bool enable); | 1400 | bool enable); |
1386 | int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); | 1401 | int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); |
1402 | int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable); | ||
1387 | int wl1271_acx_max_tx_retry(struct wl1271 *wl); | 1403 | int wl1271_acx_max_tx_retry(struct wl1271 *wl); |
1388 | int wl1271_acx_config_ps(struct wl1271 *wl); | 1404 | int wl1271_acx_config_ps(struct wl1271 *wl); |
1389 | int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); | 1405 | int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); |
diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 7ccec07a600c..101f7e0f6329 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c | |||
@@ -102,6 +102,33 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) | |||
102 | wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); | 102 | wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); |
103 | } | 103 | } |
104 | 104 | ||
105 | static unsigned int wl12xx_get_fw_ver_quirks(struct wl1271 *wl) | ||
106 | { | ||
107 | unsigned int quirks = 0; | ||
108 | unsigned int *fw_ver = wl->chip.fw_ver; | ||
109 | |||
110 | /* Only for wl127x */ | ||
111 | if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) && | ||
112 | /* Check STA version */ | ||
113 | (((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) && | ||
114 | (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) || | ||
115 | /* Check AP version */ | ||
116 | ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) && | ||
117 | (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN)))) | ||
118 | quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS; | ||
119 | |||
120 | /* Only new station firmwares support routing fw logs to the host */ | ||
121 | if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) && | ||
122 | (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN)) | ||
123 | quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED; | ||
124 | |||
125 | /* This feature is not yet supported for AP mode */ | ||
126 | if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) | ||
127 | quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED; | ||
128 | |||
129 | return quirks; | ||
130 | } | ||
131 | |||
105 | static void wl1271_parse_fw_ver(struct wl1271 *wl) | 132 | static void wl1271_parse_fw_ver(struct wl1271 *wl) |
106 | { | 133 | { |
107 | int ret; | 134 | int ret; |
@@ -116,6 +143,9 @@ static void wl1271_parse_fw_ver(struct wl1271 *wl) | |||
116 | memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver)); | 143 | memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver)); |
117 | return; | 144 | return; |
118 | } | 145 | } |
146 | |||
147 | /* Check if any quirks are needed with older fw versions */ | ||
148 | wl->quirks |= wl12xx_get_fw_ver_quirks(wl); | ||
119 | } | 149 | } |
120 | 150 | ||
121 | static void wl1271_boot_fw_version(struct wl1271 *wl) | 151 | static void wl1271_boot_fw_version(struct wl1271 *wl) |
@@ -749,6 +779,9 @@ int wl1271_load_firmware(struct wl1271 *wl) | |||
749 | clk |= (wl->ref_clock << 1) << 4; | 779 | clk |= (wl->ref_clock << 1) << 4; |
750 | } | 780 | } |
751 | 781 | ||
782 | if (wl->quirks & WL12XX_QUIRK_LPD_MODE) | ||
783 | clk |= SCRATCH_ENABLE_LPD; | ||
784 | |||
752 | wl1271_write32(wl, DRPW_SCRATCH_START, clk); | 785 | wl1271_write32(wl, DRPW_SCRATCH_START, clk); |
753 | 786 | ||
754 | wl1271_set_partition(wl, &part_table[PART_WORK]); | 787 | wl1271_set_partition(wl, &part_table[PART_WORK]); |
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 5d0ad2d93cb3..68972cbc68b4 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c | |||
@@ -23,7 +23,6 @@ | |||
23 | 23 | ||
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
26 | #include <linux/crc7.h> | ||
27 | #include <linux/spi/spi.h> | 26 | #include <linux/spi/spi.h> |
28 | #include <linux/etherdevice.h> | 27 | #include <linux/etherdevice.h> |
29 | #include <linux/ieee80211.h> | 28 | #include <linux/ieee80211.h> |
@@ -106,7 +105,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, | |||
106 | 105 | ||
107 | fail: | 106 | fail: |
108 | WARN_ON(1); | 107 | WARN_ON(1); |
109 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | 108 | wl12xx_queue_recovery_work(wl); |
110 | return ret; | 109 | return ret; |
111 | } | 110 | } |
112 | 111 | ||
@@ -135,6 +134,11 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) | |||
135 | /* Override the REF CLK from the NVS with the one from platform data */ | 134 | /* Override the REF CLK from the NVS with the one from platform data */ |
136 | gen_parms->general_params.ref_clock = wl->ref_clock; | 135 | gen_parms->general_params.ref_clock = wl->ref_clock; |
137 | 136 | ||
137 | /* LPD mode enable (bits 6-7) in WL1271 AP mode only */ | ||
138 | if (wl->quirks & WL12XX_QUIRK_LPD_MODE) | ||
139 | gen_parms->general_params.general_settings |= | ||
140 | GENERAL_SETTINGS_DRPW_LPD; | ||
141 | |||
138 | ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); | 142 | ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); |
139 | if (ret < 0) { | 143 | if (ret < 0) { |
140 | wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); | 144 | wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); |
@@ -352,7 +356,7 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) | |||
352 | 356 | ||
353 | ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask); | 357 | ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask); |
354 | if (ret != 0) { | 358 | if (ret != 0) { |
355 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | 359 | wl12xx_queue_recovery_work(wl); |
356 | return ret; | 360 | return ret; |
357 | } | 361 | } |
358 | 362 | ||
@@ -1223,3 +1227,87 @@ out_free: | |||
1223 | out: | 1227 | out: |
1224 | return ret; | 1228 | return ret; |
1225 | } | 1229 | } |
1230 | |||
1231 | int wl12xx_cmd_config_fwlog(struct wl1271 *wl) | ||
1232 | { | ||
1233 | struct wl12xx_cmd_config_fwlog *cmd; | ||
1234 | int ret = 0; | ||
1235 | |||
1236 | wl1271_debug(DEBUG_CMD, "cmd config firmware logger"); | ||
1237 | |||
1238 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
1239 | if (!cmd) { | ||
1240 | ret = -ENOMEM; | ||
1241 | goto out; | ||
1242 | } | ||
1243 | |||
1244 | cmd->logger_mode = wl->conf.fwlog.mode; | ||
1245 | cmd->log_severity = wl->conf.fwlog.severity; | ||
1246 | cmd->timestamp = wl->conf.fwlog.timestamp; | ||
1247 | cmd->output = wl->conf.fwlog.output; | ||
1248 | cmd->threshold = wl->conf.fwlog.threshold; | ||
1249 | |||
1250 | ret = wl1271_cmd_send(wl, CMD_CONFIG_FWLOGGER, cmd, sizeof(*cmd), 0); | ||
1251 | if (ret < 0) { | ||
1252 | wl1271_error("failed to send config firmware logger command"); | ||
1253 | goto out_free; | ||
1254 | } | ||
1255 | |||
1256 | out_free: | ||
1257 | kfree(cmd); | ||
1258 | |||
1259 | out: | ||
1260 | return ret; | ||
1261 | } | ||
1262 | |||
1263 | int wl12xx_cmd_start_fwlog(struct wl1271 *wl) | ||
1264 | { | ||
1265 | struct wl12xx_cmd_start_fwlog *cmd; | ||
1266 | int ret = 0; | ||
1267 | |||
1268 | wl1271_debug(DEBUG_CMD, "cmd start firmware logger"); | ||
1269 | |||
1270 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
1271 | if (!cmd) { | ||
1272 | ret = -ENOMEM; | ||
1273 | goto out; | ||
1274 | } | ||
1275 | |||
1276 | ret = wl1271_cmd_send(wl, CMD_START_FWLOGGER, cmd, sizeof(*cmd), 0); | ||
1277 | if (ret < 0) { | ||
1278 | wl1271_error("failed to send start firmware logger command"); | ||
1279 | goto out_free; | ||
1280 | } | ||
1281 | |||
1282 | out_free: | ||
1283 | kfree(cmd); | ||
1284 | |||
1285 | out: | ||
1286 | return ret; | ||
1287 | } | ||
1288 | |||
1289 | int wl12xx_cmd_stop_fwlog(struct wl1271 *wl) | ||
1290 | { | ||
1291 | struct wl12xx_cmd_stop_fwlog *cmd; | ||
1292 | int ret = 0; | ||
1293 | |||
1294 | wl1271_debug(DEBUG_CMD, "cmd stop firmware logger"); | ||
1295 | |||
1296 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
1297 | if (!cmd) { | ||
1298 | ret = -ENOMEM; | ||
1299 | goto out; | ||
1300 | } | ||
1301 | |||
1302 | ret = wl1271_cmd_send(wl, CMD_STOP_FWLOGGER, cmd, sizeof(*cmd), 0); | ||
1303 | if (ret < 0) { | ||
1304 | wl1271_error("failed to send stop firmware logger command"); | ||
1305 | goto out_free; | ||
1306 | } | ||
1307 | |||
1308 | out_free: | ||
1309 | kfree(cmd); | ||
1310 | |||
1311 | out: | ||
1312 | return ret; | ||
1313 | } | ||
diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 5cac95d9480c..1f7037292c15 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h | |||
@@ -70,6 +70,9 @@ int wl1271_cmd_start_bss(struct wl1271 *wl); | |||
70 | int wl1271_cmd_stop_bss(struct wl1271 *wl); | 70 | int wl1271_cmd_stop_bss(struct wl1271 *wl); |
71 | int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid); | 71 | int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid); |
72 | int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid); | 72 | int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid); |
73 | int wl12xx_cmd_config_fwlog(struct wl1271 *wl); | ||
74 | int wl12xx_cmd_start_fwlog(struct wl1271 *wl); | ||
75 | int wl12xx_cmd_stop_fwlog(struct wl1271 *wl); | ||
73 | 76 | ||
74 | enum wl1271_commands { | 77 | enum wl1271_commands { |
75 | CMD_INTERROGATE = 1, /*use this to read information elements*/ | 78 | CMD_INTERROGATE = 1, /*use this to read information elements*/ |
@@ -107,6 +110,9 @@ enum wl1271_commands { | |||
107 | CMD_START_PERIODIC_SCAN = 50, | 110 | CMD_START_PERIODIC_SCAN = 50, |
108 | CMD_STOP_PERIODIC_SCAN = 51, | 111 | CMD_STOP_PERIODIC_SCAN = 51, |
109 | CMD_SET_STA_STATE = 52, | 112 | CMD_SET_STA_STATE = 52, |
113 | CMD_CONFIG_FWLOGGER = 53, | ||
114 | CMD_START_FWLOGGER = 54, | ||
115 | CMD_STOP_FWLOGGER = 55, | ||
110 | 116 | ||
111 | /* AP mode commands */ | 117 | /* AP mode commands */ |
112 | CMD_BSS_START = 60, | 118 | CMD_BSS_START = 60, |
@@ -575,4 +581,60 @@ struct wl1271_cmd_remove_sta { | |||
575 | u8 padding1; | 581 | u8 padding1; |
576 | } __packed; | 582 | } __packed; |
577 | 583 | ||
584 | /* | ||
585 | * Continuous mode - packets are transferred to the host periodically | ||
586 | * via the data path. | ||
587 | * On demand - Log messages are stored in a cyclic buffer in the | ||
588 | * firmware, and only transferred to the host when explicitly requested | ||
589 | */ | ||
590 | enum wl12xx_fwlogger_log_mode { | ||
591 | WL12XX_FWLOG_CONTINUOUS, | ||
592 | WL12XX_FWLOG_ON_DEMAND | ||
593 | }; | ||
594 | |||
595 | /* Include/exclude timestamps from the log messages */ | ||
596 | enum wl12xx_fwlogger_timestamp { | ||
597 | WL12XX_FWLOG_TIMESTAMP_DISABLED, | ||
598 | WL12XX_FWLOG_TIMESTAMP_ENABLED | ||
599 | }; | ||
600 | |||
601 | /* | ||
602 | * Logs can be routed to the debug pinouts (where available), to the host bus | ||
603 | * (SDIO/SPI), or dropped | ||
604 | */ | ||
605 | enum wl12xx_fwlogger_output { | ||
606 | WL12XX_FWLOG_OUTPUT_NONE, | ||
607 | WL12XX_FWLOG_OUTPUT_DBG_PINS, | ||
608 | WL12XX_FWLOG_OUTPUT_HOST, | ||
609 | }; | ||
610 | |||
611 | struct wl12xx_cmd_config_fwlog { | ||
612 | struct wl1271_cmd_header header; | ||
613 | |||
614 | /* See enum wl12xx_fwlogger_log_mode */ | ||
615 | u8 logger_mode; | ||
616 | |||
617 | /* Minimum log level threshold */ | ||
618 | u8 log_severity; | ||
619 | |||
620 | /* Include/exclude timestamps from the log messages */ | ||
621 | u8 timestamp; | ||
622 | |||
623 | /* See enum wl1271_fwlogger_output */ | ||
624 | u8 output; | ||
625 | |||
626 | /* Regulates the frequency of log messages */ | ||
627 | u8 threshold; | ||
628 | |||
629 | u8 padding[3]; | ||
630 | } __packed; | ||
631 | |||
632 | struct wl12xx_cmd_start_fwlog { | ||
633 | struct wl1271_cmd_header header; | ||
634 | } __packed; | ||
635 | |||
636 | struct wl12xx_cmd_stop_fwlog { | ||
637 | struct wl1271_cmd_header header; | ||
638 | } __packed; | ||
639 | |||
578 | #endif /* __WL1271_CMD_H__ */ | 640 | #endif /* __WL1271_CMD_H__ */ |
diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index c83fefb6662f..b5a7b30afda3 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h | |||
@@ -1248,6 +1248,59 @@ struct conf_fm_coex { | |||
1248 | u8 swallow_clk_diff; | 1248 | u8 swallow_clk_diff; |
1249 | }; | 1249 | }; |
1250 | 1250 | ||
1251 | struct conf_rx_streaming_settings { | ||
1252 | /* | ||
1253 | * RX Streaming duration (in msec) from last tx/rx | ||
1254 | * | ||
1255 | * Range: u32 | ||
1256 | */ | ||
1257 | u32 duration; | ||
1258 | |||
1259 | /* | ||
1260 | * Bitmap of tids to be polled during RX streaming. | ||
1261 | * (Note: it doesn't look like it really matters) | ||
1262 | * | ||
1263 | * Range: 0x1-0xff | ||
1264 | */ | ||
1265 | u8 queues; | ||
1266 | |||
1267 | /* | ||
1268 | * RX Streaming interval. | ||
1269 | * (Note:this value is also used as the rx streaming timeout) | ||
1270 | * Range: 0 (disabled), 10 - 100 | ||
1271 | */ | ||
1272 | u8 interval; | ||
1273 | |||
1274 | /* | ||
1275 | * enable rx streaming also when there is no coex activity | ||
1276 | */ | ||
1277 | u8 always; | ||
1278 | }; | ||
1279 | |||
1280 | struct conf_fwlog { | ||
1281 | /* Continuous or on-demand */ | ||
1282 | u8 mode; | ||
1283 | |||
1284 | /* | ||
1285 | * Number of memory blocks dedicated for the FW logger | ||
1286 | * | ||
1287 | * Range: 1-3, or 0 to disable the FW logger | ||
1288 | */ | ||
1289 | u8 mem_blocks; | ||
1290 | |||
1291 | /* Minimum log level threshold */ | ||
1292 | u8 severity; | ||
1293 | |||
1294 | /* Include/exclude timestamps from the log messages */ | ||
1295 | u8 timestamp; | ||
1296 | |||
1297 | /* See enum wl1271_fwlogger_output */ | ||
1298 | u8 output; | ||
1299 | |||
1300 | /* Regulates the frequency of log messages */ | ||
1301 | u8 threshold; | ||
1302 | }; | ||
1303 | |||
1251 | struct conf_drv_settings { | 1304 | struct conf_drv_settings { |
1252 | struct conf_sg_settings sg; | 1305 | struct conf_sg_settings sg; |
1253 | struct conf_rx_settings rx; | 1306 | struct conf_rx_settings rx; |
@@ -1263,6 +1316,8 @@ struct conf_drv_settings { | |||
1263 | struct conf_memory_settings mem_wl127x; | 1316 | struct conf_memory_settings mem_wl127x; |
1264 | struct conf_memory_settings mem_wl128x; | 1317 | struct conf_memory_settings mem_wl128x; |
1265 | struct conf_fm_coex fm_coex; | 1318 | struct conf_fm_coex fm_coex; |
1319 | struct conf_rx_streaming_settings rx_streaming; | ||
1320 | struct conf_fwlog fwlog; | ||
1266 | u8 hci_io_ds; | 1321 | u8 hci_io_ds; |
1267 | }; | 1322 | }; |
1268 | 1323 | ||
diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index f1f8df9b6cd7..da2127018300 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c | |||
@@ -71,6 +71,14 @@ static const struct file_operations name## _ops = { \ | |||
71 | if (!entry || IS_ERR(entry)) \ | 71 | if (!entry || IS_ERR(entry)) \ |
72 | goto err; \ | 72 | goto err; \ |
73 | 73 | ||
74 | #define DEBUGFS_ADD_PREFIX(prefix, name, parent) \ | ||
75 | do { \ | ||
76 | entry = debugfs_create_file(#name, 0400, parent, \ | ||
77 | wl, &prefix## _## name## _ops); \ | ||
78 | if (!entry || IS_ERR(entry)) \ | ||
79 | goto err; \ | ||
80 | } while (0); | ||
81 | |||
74 | #define DEBUGFS_FWSTATS_FILE(sub, name, fmt) \ | 82 | #define DEBUGFS_FWSTATS_FILE(sub, name, fmt) \ |
75 | static ssize_t sub## _ ##name## _read(struct file *file, \ | 83 | static ssize_t sub## _ ##name## _read(struct file *file, \ |
76 | char __user *userbuf, \ | 84 | char __user *userbuf, \ |
@@ -298,7 +306,7 @@ static ssize_t start_recovery_write(struct file *file, | |||
298 | struct wl1271 *wl = file->private_data; | 306 | struct wl1271 *wl = file->private_data; |
299 | 307 | ||
300 | mutex_lock(&wl->mutex); | 308 | mutex_lock(&wl->mutex); |
301 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | 309 | wl12xx_queue_recovery_work(wl); |
302 | mutex_unlock(&wl->mutex); | 310 | mutex_unlock(&wl->mutex); |
303 | 311 | ||
304 | return count; | 312 | return count; |
@@ -527,11 +535,129 @@ static const struct file_operations beacon_interval_ops = { | |||
527 | .llseek = default_llseek, | 535 | .llseek = default_llseek, |
528 | }; | 536 | }; |
529 | 537 | ||
538 | static ssize_t rx_streaming_interval_write(struct file *file, | ||
539 | const char __user *user_buf, | ||
540 | size_t count, loff_t *ppos) | ||
541 | { | ||
542 | struct wl1271 *wl = file->private_data; | ||
543 | char buf[10]; | ||
544 | size_t len; | ||
545 | unsigned long value; | ||
546 | int ret; | ||
547 | |||
548 | len = min(count, sizeof(buf) - 1); | ||
549 | if (copy_from_user(buf, user_buf, len)) | ||
550 | return -EFAULT; | ||
551 | buf[len] = '\0'; | ||
552 | |||
553 | ret = kstrtoul(buf, 0, &value); | ||
554 | if (ret < 0) { | ||
555 | wl1271_warning("illegal value in rx_streaming_interval!"); | ||
556 | return -EINVAL; | ||
557 | } | ||
558 | |||
559 | /* valid values: 0, 10-100 */ | ||
560 | if (value && (value < 10 || value > 100)) { | ||
561 | wl1271_warning("value is not in range!"); | ||
562 | return -ERANGE; | ||
563 | } | ||
564 | |||
565 | mutex_lock(&wl->mutex); | ||
566 | |||
567 | wl->conf.rx_streaming.interval = value; | ||
568 | |||
569 | ret = wl1271_ps_elp_wakeup(wl); | ||
570 | if (ret < 0) | ||
571 | goto out; | ||
572 | |||
573 | wl1271_recalc_rx_streaming(wl); | ||
574 | |||
575 | wl1271_ps_elp_sleep(wl); | ||
576 | out: | ||
577 | mutex_unlock(&wl->mutex); | ||
578 | return count; | ||
579 | } | ||
580 | |||
581 | static ssize_t rx_streaming_interval_read(struct file *file, | ||
582 | char __user *userbuf, | ||
583 | size_t count, loff_t *ppos) | ||
584 | { | ||
585 | struct wl1271 *wl = file->private_data; | ||
586 | return wl1271_format_buffer(userbuf, count, ppos, | ||
587 | "%d\n", wl->conf.rx_streaming.interval); | ||
588 | } | ||
589 | |||
590 | static const struct file_operations rx_streaming_interval_ops = { | ||
591 | .read = rx_streaming_interval_read, | ||
592 | .write = rx_streaming_interval_write, | ||
593 | .open = wl1271_open_file_generic, | ||
594 | .llseek = default_llseek, | ||
595 | }; | ||
596 | |||
597 | static ssize_t rx_streaming_always_write(struct file *file, | ||
598 | const char __user *user_buf, | ||
599 | size_t count, loff_t *ppos) | ||
600 | { | ||
601 | struct wl1271 *wl = file->private_data; | ||
602 | char buf[10]; | ||
603 | size_t len; | ||
604 | unsigned long value; | ||
605 | int ret; | ||
606 | |||
607 | len = min(count, sizeof(buf) - 1); | ||
608 | if (copy_from_user(buf, user_buf, len)) | ||
609 | return -EFAULT; | ||
610 | buf[len] = '\0'; | ||
611 | |||
612 | ret = kstrtoul(buf, 0, &value); | ||
613 | if (ret < 0) { | ||
614 | wl1271_warning("illegal value in rx_streaming_write!"); | ||
615 | return -EINVAL; | ||
616 | } | ||
617 | |||
618 | /* valid values: 0, 10-100 */ | ||
619 | if (!(value == 0 || value == 1)) { | ||
620 | wl1271_warning("value is not in valid!"); | ||
621 | return -EINVAL; | ||
622 | } | ||
623 | |||
624 | mutex_lock(&wl->mutex); | ||
625 | |||
626 | wl->conf.rx_streaming.always = value; | ||
627 | |||
628 | ret = wl1271_ps_elp_wakeup(wl); | ||
629 | if (ret < 0) | ||
630 | goto out; | ||
631 | |||
632 | wl1271_recalc_rx_streaming(wl); | ||
633 | |||
634 | wl1271_ps_elp_sleep(wl); | ||
635 | out: | ||
636 | mutex_unlock(&wl->mutex); | ||
637 | return count; | ||
638 | } | ||
639 | |||
640 | static ssize_t rx_streaming_always_read(struct file *file, | ||
641 | char __user *userbuf, | ||
642 | size_t count, loff_t *ppos) | ||
643 | { | ||
644 | struct wl1271 *wl = file->private_data; | ||
645 | return wl1271_format_buffer(userbuf, count, ppos, | ||
646 | "%d\n", wl->conf.rx_streaming.always); | ||
647 | } | ||
648 | |||
649 | static const struct file_operations rx_streaming_always_ops = { | ||
650 | .read = rx_streaming_always_read, | ||
651 | .write = rx_streaming_always_write, | ||
652 | .open = wl1271_open_file_generic, | ||
653 | .llseek = default_llseek, | ||
654 | }; | ||
655 | |||
530 | static int wl1271_debugfs_add_files(struct wl1271 *wl, | 656 | static int wl1271_debugfs_add_files(struct wl1271 *wl, |
531 | struct dentry *rootdir) | 657 | struct dentry *rootdir) |
532 | { | 658 | { |
533 | int ret = 0; | 659 | int ret = 0; |
534 | struct dentry *entry, *stats; | 660 | struct dentry *entry, *stats, *streaming; |
535 | 661 | ||
536 | stats = debugfs_create_dir("fw-statistics", rootdir); | 662 | stats = debugfs_create_dir("fw-statistics", rootdir); |
537 | if (!stats || IS_ERR(stats)) { | 663 | if (!stats || IS_ERR(stats)) { |
@@ -640,6 +766,14 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, | |||
640 | DEBUGFS_ADD(dtim_interval, rootdir); | 766 | DEBUGFS_ADD(dtim_interval, rootdir); |
641 | DEBUGFS_ADD(beacon_interval, rootdir); | 767 | DEBUGFS_ADD(beacon_interval, rootdir); |
642 | 768 | ||
769 | streaming = debugfs_create_dir("rx_streaming", rootdir); | ||
770 | if (!streaming || IS_ERR(streaming)) | ||
771 | goto err; | ||
772 | |||
773 | DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming); | ||
774 | DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming); | ||
775 | |||
776 | |||
643 | return 0; | 777 | return 0; |
644 | 778 | ||
645 | err: | 779 | err: |
diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 94bbd00ec31b..a16dee58a664 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c | |||
@@ -133,10 +133,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl, | |||
133 | if (ret < 0) | 133 | if (ret < 0) |
134 | break; | 134 | break; |
135 | 135 | ||
136 | /* enable beacon early termination */ | 136 | /* |
137 | ret = wl1271_acx_bet_enable(wl, true); | 137 | * BET has only a minor effect in 5GHz and masks |
138 | if (ret < 0) | 138 | * channel switch IEs, so we only enable BET on 2.4GHz |
139 | break; | 139 | */ |
140 | if (wl->band == IEEE80211_BAND_2GHZ) | ||
141 | /* enable beacon early termination */ | ||
142 | ret = wl1271_acx_bet_enable(wl, true); | ||
140 | 143 | ||
141 | if (wl->ps_compl) { | 144 | if (wl->ps_compl) { |
142 | complete(wl->ps_compl); | 145 | complete(wl->ps_compl); |
@@ -183,6 +186,21 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, u8 ba_allowed) | |||
183 | ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap, wl->bssid); | 186 | ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap, wl->bssid); |
184 | } | 187 | } |
185 | 188 | ||
189 | static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, | ||
190 | u8 enable) | ||
191 | { | ||
192 | if (enable) { | ||
193 | /* disable dynamic PS when requested by the firmware */ | ||
194 | ieee80211_disable_dyn_ps(wl->vif); | ||
195 | set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); | ||
196 | } else { | ||
197 | ieee80211_enable_dyn_ps(wl->vif); | ||
198 | clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); | ||
199 | wl1271_recalc_rx_streaming(wl); | ||
200 | } | ||
201 | |||
202 | } | ||
203 | |||
186 | static void wl1271_event_mbox_dump(struct event_mailbox *mbox) | 204 | static void wl1271_event_mbox_dump(struct event_mailbox *mbox) |
187 | { | 205 | { |
188 | wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); | 206 | wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); |
@@ -226,14 +244,10 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) | |||
226 | } | 244 | } |
227 | } | 245 | } |
228 | 246 | ||
229 | /* disable dynamic PS when requested by the firmware */ | ||
230 | if (vector & SOFT_GEMINI_SENSE_EVENT_ID && | 247 | if (vector & SOFT_GEMINI_SENSE_EVENT_ID && |
231 | wl->bss_type == BSS_TYPE_STA_BSS) { | 248 | wl->bss_type == BSS_TYPE_STA_BSS) |
232 | if (mbox->soft_gemini_sense_info) | 249 | wl12xx_event_soft_gemini_sense(wl, |
233 | ieee80211_disable_dyn_ps(wl->vif); | 250 | mbox->soft_gemini_sense_info); |
234 | else | ||
235 | ieee80211_enable_dyn_ps(wl->vif); | ||
236 | } | ||
237 | 251 | ||
238 | /* | 252 | /* |
239 | * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon | 253 | * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon |
diff --git a/drivers/net/wireless/wl12xx/ini.h b/drivers/net/wireless/wl12xx/ini.h index 1420c842b8f1..4cf9ecc56212 100644 --- a/drivers/net/wireless/wl12xx/ini.h +++ b/drivers/net/wireless/wl12xx/ini.h | |||
@@ -24,6 +24,9 @@ | |||
24 | #ifndef __INI_H__ | 24 | #ifndef __INI_H__ |
25 | #define __INI_H__ | 25 | #define __INI_H__ |
26 | 26 | ||
27 | #define GENERAL_SETTINGS_DRPW_LPD 0xc0 | ||
28 | #define SCRATCH_ENABLE_LPD BIT(25) | ||
29 | |||
27 | #define WL1271_INI_MAX_SMART_REFLEX_PARAM 16 | 30 | #define WL1271_INI_MAX_SMART_REFLEX_PARAM 16 |
28 | 31 | ||
29 | struct wl1271_ini_general_params { | 32 | struct wl1271_ini_general_params { |
diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index f5c2c9e6f84b..cf40ac93cead 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c | |||
@@ -321,6 +321,20 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl) | |||
321 | return 0; | 321 | return 0; |
322 | } | 322 | } |
323 | 323 | ||
324 | static int wl12xx_init_fwlog(struct wl1271 *wl) | ||
325 | { | ||
326 | int ret; | ||
327 | |||
328 | if (wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED) | ||
329 | return 0; | ||
330 | |||
331 | ret = wl12xx_cmd_config_fwlog(wl); | ||
332 | if (ret < 0) | ||
333 | return ret; | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
324 | static int wl1271_sta_hw_init(struct wl1271 *wl) | 338 | static int wl1271_sta_hw_init(struct wl1271 *wl) |
325 | { | 339 | { |
326 | int ret; | 340 | int ret; |
@@ -382,6 +396,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) | |||
382 | if (ret < 0) | 396 | if (ret < 0) |
383 | return ret; | 397 | return ret; |
384 | 398 | ||
399 | /* Configure the FW logger */ | ||
400 | ret = wl12xx_init_fwlog(wl); | ||
401 | if (ret < 0) | ||
402 | return ret; | ||
403 | |||
385 | return 0; | 404 | return 0; |
386 | } | 405 | } |
387 | 406 | ||
diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c index da5c1ad942a4..c2da66f45046 100644 --- a/drivers/net/wireless/wl12xx/io.c +++ b/drivers/net/wireless/wl12xx/io.c | |||
@@ -23,7 +23,6 @@ | |||
23 | 23 | ||
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
26 | #include <linux/crc7.h> | ||
27 | #include <linux/spi/spi.h> | 26 | #include <linux/spi/spi.h> |
28 | 27 | ||
29 | #include "wl12xx.h" | 28 | #include "wl12xx.h" |
@@ -128,12 +127,14 @@ EXPORT_SYMBOL_GPL(wl1271_set_partition); | |||
128 | 127 | ||
129 | void wl1271_io_reset(struct wl1271 *wl) | 128 | void wl1271_io_reset(struct wl1271 *wl) |
130 | { | 129 | { |
131 | wl->if_ops->reset(wl); | 130 | if (wl->if_ops->reset) |
131 | wl->if_ops->reset(wl); | ||
132 | } | 132 | } |
133 | 133 | ||
134 | void wl1271_io_init(struct wl1271 *wl) | 134 | void wl1271_io_init(struct wl1271 *wl) |
135 | { | 135 | { |
136 | wl->if_ops->init(wl); | 136 | if (wl->if_ops->init) |
137 | wl->if_ops->init(wl); | ||
137 | } | 138 | } |
138 | 139 | ||
139 | void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) | 140 | void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) |
diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h index beed621a8ae0..cfb3588a4ddf 100644 --- a/drivers/net/wireless/wl12xx/io.h +++ b/drivers/net/wireless/wl12xx/io.h | |||
@@ -128,6 +128,20 @@ static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf, | |||
128 | wl1271_raw_write(wl, physical, buf, len, fixed); | 128 | wl1271_raw_write(wl, physical, buf, len, fixed); |
129 | } | 129 | } |
130 | 130 | ||
131 | static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr, | ||
132 | void *buf, size_t len, bool fixed) | ||
133 | { | ||
134 | int physical; | ||
135 | int addr; | ||
136 | |||
137 | /* Addresses are stored internally as addresses to 32 bytes blocks */ | ||
138 | addr = hwaddr << 5; | ||
139 | |||
140 | physical = wl1271_translate_addr(wl, addr); | ||
141 | |||
142 | wl1271_raw_read(wl, physical, buf, len, fixed); | ||
143 | } | ||
144 | |||
131 | static inline u32 wl1271_read32(struct wl1271 *wl, int addr) | 145 | static inline u32 wl1271_read32(struct wl1271 *wl, int addr) |
132 | { | 146 | { |
133 | return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); | 147 | return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); |
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index f37f0b873c73..a3734bdf5119 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c | |||
@@ -31,6 +31,7 @@ | |||
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 | #include <linux/wl12xx.h> |
34 | #include <linux/sched.h> | ||
34 | 35 | ||
35 | #include "wl12xx.h" | 36 | #include "wl12xx.h" |
36 | #include "wl12xx_80211.h" | 37 | #include "wl12xx_80211.h" |
@@ -362,9 +363,25 @@ static struct conf_drv_settings default_conf = { | |||
362 | .fm_disturbed_band_margin = 0xff, /* default */ | 363 | .fm_disturbed_band_margin = 0xff, /* default */ |
363 | .swallow_clk_diff = 0xff, /* default */ | 364 | .swallow_clk_diff = 0xff, /* default */ |
364 | }, | 365 | }, |
366 | .rx_streaming = { | ||
367 | .duration = 150, | ||
368 | .queues = 0x1, | ||
369 | .interval = 20, | ||
370 | .always = 0, | ||
371 | }, | ||
372 | .fwlog = { | ||
373 | .mode = WL12XX_FWLOG_ON_DEMAND, | ||
374 | .mem_blocks = 2, | ||
375 | .severity = 0, | ||
376 | .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, | ||
377 | .output = WL12XX_FWLOG_OUTPUT_HOST, | ||
378 | .threshold = 0, | ||
379 | }, | ||
365 | .hci_io_ds = HCI_IO_DS_6MA, | 380 | .hci_io_ds = HCI_IO_DS_6MA, |
366 | }; | 381 | }; |
367 | 382 | ||
383 | static char *fwlog_param; | ||
384 | |||
368 | static void __wl1271_op_remove_interface(struct wl1271 *wl, | 385 | static void __wl1271_op_remove_interface(struct wl1271 *wl, |
369 | bool reset_tx_queues); | 386 | bool reset_tx_queues); |
370 | static void wl1271_free_ap_keys(struct wl1271 *wl); | 387 | static void wl1271_free_ap_keys(struct wl1271 *wl); |
@@ -388,6 +405,22 @@ static struct platform_device wl1271_device = { | |||
388 | static DEFINE_MUTEX(wl_list_mutex); | 405 | static DEFINE_MUTEX(wl_list_mutex); |
389 | static LIST_HEAD(wl_list); | 406 | static LIST_HEAD(wl_list); |
390 | 407 | ||
408 | static int wl1271_check_operstate(struct wl1271 *wl, unsigned char operstate) | ||
409 | { | ||
410 | int ret; | ||
411 | if (operstate != IF_OPER_UP) | ||
412 | return 0; | ||
413 | |||
414 | if (test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) | ||
415 | return 0; | ||
416 | |||
417 | ret = wl1271_cmd_set_sta_state(wl); | ||
418 | if (ret < 0) | ||
419 | return ret; | ||
420 | |||
421 | wl1271_info("Association completed."); | ||
422 | return 0; | ||
423 | } | ||
391 | static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, | 424 | static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, |
392 | void *arg) | 425 | void *arg) |
393 | { | 426 | { |
@@ -437,11 +470,7 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, | |||
437 | if (ret < 0) | 470 | if (ret < 0) |
438 | goto out; | 471 | goto out; |
439 | 472 | ||
440 | if ((dev->operstate == IF_OPER_UP) && | 473 | wl1271_check_operstate(wl, dev->operstate); |
441 | !test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) { | ||
442 | wl1271_cmd_set_sta_state(wl); | ||
443 | wl1271_info("Association completed."); | ||
444 | } | ||
445 | 474 | ||
446 | wl1271_ps_elp_sleep(wl); | 475 | wl1271_ps_elp_sleep(wl); |
447 | 476 | ||
@@ -473,6 +502,117 @@ static int wl1271_reg_notify(struct wiphy *wiphy, | |||
473 | return 0; | 502 | return 0; |
474 | } | 503 | } |
475 | 504 | ||
505 | static int wl1271_set_rx_streaming(struct wl1271 *wl, bool enable) | ||
506 | { | ||
507 | int ret = 0; | ||
508 | |||
509 | /* we should hold wl->mutex */ | ||
510 | ret = wl1271_acx_ps_rx_streaming(wl, enable); | ||
511 | if (ret < 0) | ||
512 | goto out; | ||
513 | |||
514 | if (enable) | ||
515 | set_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags); | ||
516 | else | ||
517 | clear_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags); | ||
518 | out: | ||
519 | return ret; | ||
520 | } | ||
521 | |||
522 | /* | ||
523 | * this function is being called when the rx_streaming interval | ||
524 | * has beed changed or rx_streaming should be disabled | ||
525 | */ | ||
526 | int wl1271_recalc_rx_streaming(struct wl1271 *wl) | ||
527 | { | ||
528 | int ret = 0; | ||
529 | int period = wl->conf.rx_streaming.interval; | ||
530 | |||
531 | /* don't reconfigure if rx_streaming is disabled */ | ||
532 | if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags)) | ||
533 | goto out; | ||
534 | |||
535 | /* reconfigure/disable according to new streaming_period */ | ||
536 | if (period && | ||
537 | test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) && | ||
538 | (wl->conf.rx_streaming.always || | ||
539 | test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) | ||
540 | ret = wl1271_set_rx_streaming(wl, true); | ||
541 | else { | ||
542 | ret = wl1271_set_rx_streaming(wl, false); | ||
543 | /* don't cancel_work_sync since we might deadlock */ | ||
544 | del_timer_sync(&wl->rx_streaming_timer); | ||
545 | } | ||
546 | out: | ||
547 | return ret; | ||
548 | } | ||
549 | |||
550 | static void wl1271_rx_streaming_enable_work(struct work_struct *work) | ||
551 | { | ||
552 | int ret; | ||
553 | struct wl1271 *wl = | ||
554 | container_of(work, struct wl1271, rx_streaming_enable_work); | ||
555 | |||
556 | mutex_lock(&wl->mutex); | ||
557 | |||
558 | if (test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags) || | ||
559 | !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) || | ||
560 | (!wl->conf.rx_streaming.always && | ||
561 | !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) | ||
562 | goto out; | ||
563 | |||
564 | if (!wl->conf.rx_streaming.interval) | ||
565 | goto out; | ||
566 | |||
567 | ret = wl1271_ps_elp_wakeup(wl); | ||
568 | if (ret < 0) | ||
569 | goto out; | ||
570 | |||
571 | ret = wl1271_set_rx_streaming(wl, true); | ||
572 | if (ret < 0) | ||
573 | goto out_sleep; | ||
574 | |||
575 | /* stop it after some time of inactivity */ | ||
576 | mod_timer(&wl->rx_streaming_timer, | ||
577 | jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration)); | ||
578 | |||
579 | out_sleep: | ||
580 | wl1271_ps_elp_sleep(wl); | ||
581 | out: | ||
582 | mutex_unlock(&wl->mutex); | ||
583 | } | ||
584 | |||
585 | static void wl1271_rx_streaming_disable_work(struct work_struct *work) | ||
586 | { | ||
587 | int ret; | ||
588 | struct wl1271 *wl = | ||
589 | container_of(work, struct wl1271, rx_streaming_disable_work); | ||
590 | |||
591 | mutex_lock(&wl->mutex); | ||
592 | |||
593 | if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags)) | ||
594 | goto out; | ||
595 | |||
596 | ret = wl1271_ps_elp_wakeup(wl); | ||
597 | if (ret < 0) | ||
598 | goto out; | ||
599 | |||
600 | ret = wl1271_set_rx_streaming(wl, false); | ||
601 | if (ret) | ||
602 | goto out_sleep; | ||
603 | |||
604 | out_sleep: | ||
605 | wl1271_ps_elp_sleep(wl); | ||
606 | out: | ||
607 | mutex_unlock(&wl->mutex); | ||
608 | } | ||
609 | |||
610 | static void wl1271_rx_streaming_timer(unsigned long data) | ||
611 | { | ||
612 | struct wl1271 *wl = (struct wl1271 *)data; | ||
613 | ieee80211_queue_work(wl->hw, &wl->rx_streaming_disable_work); | ||
614 | } | ||
615 | |||
476 | static void wl1271_conf_init(struct wl1271 *wl) | 616 | static void wl1271_conf_init(struct wl1271 *wl) |
477 | { | 617 | { |
478 | 618 | ||
@@ -488,8 +628,24 @@ static void wl1271_conf_init(struct wl1271 *wl) | |||
488 | 628 | ||
489 | /* apply driver default configuration */ | 629 | /* apply driver default configuration */ |
490 | memcpy(&wl->conf, &default_conf, sizeof(default_conf)); | 630 | memcpy(&wl->conf, &default_conf, sizeof(default_conf)); |
491 | } | ||
492 | 631 | ||
632 | /* Adjust settings according to optional module parameters */ | ||
633 | if (fwlog_param) { | ||
634 | if (!strcmp(fwlog_param, "continuous")) { | ||
635 | wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; | ||
636 | } else if (!strcmp(fwlog_param, "ondemand")) { | ||
637 | wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND; | ||
638 | } else if (!strcmp(fwlog_param, "dbgpins")) { | ||
639 | wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; | ||
640 | wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS; | ||
641 | } else if (!strcmp(fwlog_param, "disable")) { | ||
642 | wl->conf.fwlog.mem_blocks = 0; | ||
643 | wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_NONE; | ||
644 | } else { | ||
645 | wl1271_error("Unknown fwlog parameter %s", fwlog_param); | ||
646 | } | ||
647 | } | ||
648 | } | ||
493 | 649 | ||
494 | static int wl1271_plt_init(struct wl1271 *wl) | 650 | static int wl1271_plt_init(struct wl1271 *wl) |
495 | { | 651 | { |
@@ -741,7 +897,7 @@ static void wl1271_flush_deferred_work(struct wl1271 *wl) | |||
741 | 897 | ||
742 | /* Return sent skbs to the network stack */ | 898 | /* Return sent skbs to the network stack */ |
743 | while ((skb = skb_dequeue(&wl->deferred_tx_queue))) | 899 | while ((skb = skb_dequeue(&wl->deferred_tx_queue))) |
744 | ieee80211_tx_status(wl->hw, skb); | 900 | ieee80211_tx_status_ni(wl->hw, skb); |
745 | } | 901 | } |
746 | 902 | ||
747 | static void wl1271_netstack_work(struct work_struct *work) | 903 | static void wl1271_netstack_work(struct work_struct *work) |
@@ -808,7 +964,7 @@ irqreturn_t wl1271_irq(int irq, void *cookie) | |||
808 | if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) { | 964 | if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) { |
809 | wl1271_error("watchdog interrupt received! " | 965 | wl1271_error("watchdog interrupt received! " |
810 | "starting recovery."); | 966 | "starting recovery."); |
811 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | 967 | wl12xx_queue_recovery_work(wl); |
812 | 968 | ||
813 | /* restarting the chip. ignore any other interrupt. */ | 969 | /* restarting the chip. ignore any other interrupt. */ |
814 | goto out; | 970 | goto out; |
@@ -970,6 +1126,89 @@ out: | |||
970 | return ret; | 1126 | return ret; |
971 | } | 1127 | } |
972 | 1128 | ||
1129 | void wl12xx_queue_recovery_work(struct wl1271 *wl) | ||
1130 | { | ||
1131 | if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) | ||
1132 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | ||
1133 | } | ||
1134 | |||
1135 | size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) | ||
1136 | { | ||
1137 | size_t len = 0; | ||
1138 | |||
1139 | /* The FW log is a length-value list, find where the log end */ | ||
1140 | while (len < maxlen) { | ||
1141 | if (memblock[len] == 0) | ||
1142 | break; | ||
1143 | if (len + memblock[len] + 1 > maxlen) | ||
1144 | break; | ||
1145 | len += memblock[len] + 1; | ||
1146 | } | ||
1147 | |||
1148 | /* Make sure we have enough room */ | ||
1149 | len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size)); | ||
1150 | |||
1151 | /* Fill the FW log file, consumed by the sysfs fwlog entry */ | ||
1152 | memcpy(wl->fwlog + wl->fwlog_size, memblock, len); | ||
1153 | wl->fwlog_size += len; | ||
1154 | |||
1155 | return len; | ||
1156 | } | ||
1157 | |||
1158 | static void wl12xx_read_fwlog_panic(struct wl1271 *wl) | ||
1159 | { | ||
1160 | u32 addr; | ||
1161 | u32 first_addr; | ||
1162 | u8 *block; | ||
1163 | |||
1164 | if ((wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED) || | ||
1165 | (wl->conf.fwlog.mode != WL12XX_FWLOG_ON_DEMAND) || | ||
1166 | (wl->conf.fwlog.mem_blocks == 0)) | ||
1167 | return; | ||
1168 | |||
1169 | wl1271_info("Reading FW panic log"); | ||
1170 | |||
1171 | block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL); | ||
1172 | if (!block) | ||
1173 | return; | ||
1174 | |||
1175 | /* | ||
1176 | * Make sure the chip is awake and the logger isn't active. | ||
1177 | * This might fail if the firmware hanged. | ||
1178 | */ | ||
1179 | if (!wl1271_ps_elp_wakeup(wl)) | ||
1180 | wl12xx_cmd_stop_fwlog(wl); | ||
1181 | |||
1182 | /* Read the first memory block address */ | ||
1183 | wl1271_fw_status(wl, wl->fw_status); | ||
1184 | first_addr = __le32_to_cpu(wl->fw_status->sta.log_start_addr); | ||
1185 | if (!first_addr) | ||
1186 | goto out; | ||
1187 | |||
1188 | /* Traverse the memory blocks linked list */ | ||
1189 | addr = first_addr; | ||
1190 | do { | ||
1191 | memset(block, 0, WL12XX_HW_BLOCK_SIZE); | ||
1192 | wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE, | ||
1193 | false); | ||
1194 | |||
1195 | /* | ||
1196 | * Memory blocks are linked to one another. The first 4 bytes | ||
1197 | * of each memory block hold the hardware address of the next | ||
1198 | * one. The last memory block points to the first one. | ||
1199 | */ | ||
1200 | addr = __le32_to_cpup((__le32 *)block); | ||
1201 | if (!wl12xx_copy_fwlog(wl, block + sizeof(addr), | ||
1202 | WL12XX_HW_BLOCK_SIZE - sizeof(addr))) | ||
1203 | break; | ||
1204 | } while (addr && (addr != first_addr)); | ||
1205 | |||
1206 | wake_up_interruptible(&wl->fwlog_waitq); | ||
1207 | |||
1208 | out: | ||
1209 | kfree(block); | ||
1210 | } | ||
1211 | |||
973 | static void wl1271_recovery_work(struct work_struct *work) | 1212 | static void wl1271_recovery_work(struct work_struct *work) |
974 | { | 1213 | { |
975 | struct wl1271 *wl = | 1214 | struct wl1271 *wl = |
@@ -980,6 +1219,11 @@ static void wl1271_recovery_work(struct work_struct *work) | |||
980 | if (wl->state != WL1271_STATE_ON) | 1219 | if (wl->state != WL1271_STATE_ON) |
981 | goto out; | 1220 | goto out; |
982 | 1221 | ||
1222 | /* Avoid a recursive recovery */ | ||
1223 | set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); | ||
1224 | |||
1225 | wl12xx_read_fwlog_panic(wl); | ||
1226 | |||
983 | wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", | 1227 | wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", |
984 | wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4)); | 1228 | wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4)); |
985 | 1229 | ||
@@ -996,6 +1240,9 @@ static void wl1271_recovery_work(struct work_struct *work) | |||
996 | 1240 | ||
997 | /* reboot the chipset */ | 1241 | /* reboot the chipset */ |
998 | __wl1271_op_remove_interface(wl, false); | 1242 | __wl1271_op_remove_interface(wl, false); |
1243 | |||
1244 | clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); | ||
1245 | |||
999 | ieee80211_restart_hw(wl->hw); | 1246 | ieee80211_restart_hw(wl->hw); |
1000 | 1247 | ||
1001 | /* | 1248 | /* |
@@ -1074,9 +1321,13 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) | |||
1074 | wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", | 1321 | wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", |
1075 | wl->chip.id); | 1322 | wl->chip.id); |
1076 | 1323 | ||
1077 | /* end-of-transaction flag should be set in wl127x AP mode */ | 1324 | /* |
1325 | * 'end-of-transaction flag' and 'LPD mode flag' | ||
1326 | * should be set in wl127x AP mode only | ||
1327 | */ | ||
1078 | if (wl->bss_type == BSS_TYPE_AP_BSS) | 1328 | if (wl->bss_type == BSS_TYPE_AP_BSS) |
1079 | wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; | 1329 | wl->quirks |= (WL12XX_QUIRK_END_OF_TRANSACTION | |
1330 | WL12XX_QUIRK_LPD_MODE); | ||
1080 | 1331 | ||
1081 | ret = wl1271_setup(wl); | 1332 | ret = wl1271_setup(wl); |
1082 | if (ret < 0) | 1333 | if (ret < 0) |
@@ -1089,6 +1340,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) | |||
1089 | ret = wl1271_setup(wl); | 1340 | ret = wl1271_setup(wl); |
1090 | if (ret < 0) | 1341 | if (ret < 0) |
1091 | goto out; | 1342 | goto out; |
1343 | |||
1092 | if (wl1271_set_block_size(wl)) | 1344 | if (wl1271_set_block_size(wl)) |
1093 | wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT; | 1345 | wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT; |
1094 | break; | 1346 | break; |
@@ -1117,24 +1369,6 @@ out: | |||
1117 | return ret; | 1369 | return ret; |
1118 | } | 1370 | } |
1119 | 1371 | ||
1120 | static unsigned int wl1271_get_fw_ver_quirks(struct wl1271 *wl) | ||
1121 | { | ||
1122 | unsigned int quirks = 0; | ||
1123 | unsigned int *fw_ver = wl->chip.fw_ver; | ||
1124 | |||
1125 | /* Only for wl127x */ | ||
1126 | if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) && | ||
1127 | /* Check STA version */ | ||
1128 | (((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) && | ||
1129 | (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) || | ||
1130 | /* Check AP version */ | ||
1131 | ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) && | ||
1132 | (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN)))) | ||
1133 | quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS; | ||
1134 | |||
1135 | return quirks; | ||
1136 | } | ||
1137 | |||
1138 | int wl1271_plt_start(struct wl1271 *wl) | 1372 | int wl1271_plt_start(struct wl1271 *wl) |
1139 | { | 1373 | { |
1140 | int retries = WL1271_BOOT_RETRIES; | 1374 | int retries = WL1271_BOOT_RETRIES; |
@@ -1171,8 +1405,6 @@ int wl1271_plt_start(struct wl1271 *wl) | |||
1171 | wl1271_notice("firmware booted in PLT mode (%s)", | 1405 | wl1271_notice("firmware booted in PLT mode (%s)", |
1172 | wl->chip.fw_ver_str); | 1406 | wl->chip.fw_ver_str); |
1173 | 1407 | ||
1174 | /* Check if any quirks are needed with older fw versions */ | ||
1175 | wl->quirks |= wl1271_get_fw_ver_quirks(wl); | ||
1176 | goto out; | 1408 | goto out; |
1177 | 1409 | ||
1178 | irq_disable: | 1410 | irq_disable: |
@@ -1352,13 +1584,10 @@ static struct notifier_block wl1271_dev_notifier = { | |||
1352 | }; | 1584 | }; |
1353 | 1585 | ||
1354 | #ifdef CONFIG_PM | 1586 | #ifdef CONFIG_PM |
1355 | static int wl1271_configure_suspend(struct wl1271 *wl) | 1587 | static int wl1271_configure_suspend_sta(struct wl1271 *wl) |
1356 | { | 1588 | { |
1357 | int ret; | 1589 | int ret; |
1358 | 1590 | ||
1359 | if (wl->bss_type != BSS_TYPE_STA_BSS) | ||
1360 | return 0; | ||
1361 | |||
1362 | mutex_lock(&wl->mutex); | 1591 | mutex_lock(&wl->mutex); |
1363 | 1592 | ||
1364 | ret = wl1271_ps_elp_wakeup(wl); | 1593 | ret = wl1271_ps_elp_wakeup(wl); |
@@ -1403,11 +1632,41 @@ out: | |||
1403 | 1632 | ||
1404 | } | 1633 | } |
1405 | 1634 | ||
1635 | static int wl1271_configure_suspend_ap(struct wl1271 *wl) | ||
1636 | { | ||
1637 | int ret; | ||
1638 | |||
1639 | mutex_lock(&wl->mutex); | ||
1640 | |||
1641 | ret = wl1271_ps_elp_wakeup(wl); | ||
1642 | if (ret < 0) | ||
1643 | goto out_unlock; | ||
1644 | |||
1645 | ret = wl1271_acx_set_ap_beacon_filter(wl, true); | ||
1646 | |||
1647 | wl1271_ps_elp_sleep(wl); | ||
1648 | out_unlock: | ||
1649 | mutex_unlock(&wl->mutex); | ||
1650 | return ret; | ||
1651 | |||
1652 | } | ||
1653 | |||
1654 | static int wl1271_configure_suspend(struct wl1271 *wl) | ||
1655 | { | ||
1656 | if (wl->bss_type == BSS_TYPE_STA_BSS) | ||
1657 | return wl1271_configure_suspend_sta(wl); | ||
1658 | if (wl->bss_type == BSS_TYPE_AP_BSS) | ||
1659 | return wl1271_configure_suspend_ap(wl); | ||
1660 | return 0; | ||
1661 | } | ||
1662 | |||
1406 | static void wl1271_configure_resume(struct wl1271 *wl) | 1663 | static void wl1271_configure_resume(struct wl1271 *wl) |
1407 | { | 1664 | { |
1408 | int ret; | 1665 | int ret; |
1666 | bool is_sta = wl->bss_type == BSS_TYPE_STA_BSS; | ||
1667 | bool is_ap = wl->bss_type == BSS_TYPE_AP_BSS; | ||
1409 | 1668 | ||
1410 | if (wl->bss_type != BSS_TYPE_STA_BSS) | 1669 | if (!is_sta && !is_ap) |
1411 | return; | 1670 | return; |
1412 | 1671 | ||
1413 | mutex_lock(&wl->mutex); | 1672 | mutex_lock(&wl->mutex); |
@@ -1415,10 +1674,14 @@ static void wl1271_configure_resume(struct wl1271 *wl) | |||
1415 | if (ret < 0) | 1674 | if (ret < 0) |
1416 | goto out; | 1675 | goto out; |
1417 | 1676 | ||
1418 | /* exit psm if it wasn't configured */ | 1677 | if (is_sta) { |
1419 | if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) | 1678 | /* exit psm if it wasn't configured */ |
1420 | wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, | 1679 | if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) |
1421 | wl->basic_rate, true); | 1680 | wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, |
1681 | wl->basic_rate, true); | ||
1682 | } else if (is_ap) { | ||
1683 | wl1271_acx_set_ap_beacon_filter(wl, false); | ||
1684 | } | ||
1422 | 1685 | ||
1423 | wl1271_ps_elp_sleep(wl); | 1686 | wl1271_ps_elp_sleep(wl); |
1424 | out: | 1687 | out: |
@@ -1429,69 +1692,69 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, | |||
1429 | struct cfg80211_wowlan *wow) | 1692 | struct cfg80211_wowlan *wow) |
1430 | { | 1693 | { |
1431 | struct wl1271 *wl = hw->priv; | 1694 | struct wl1271 *wl = hw->priv; |
1695 | int ret; | ||
1696 | |||
1432 | wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); | 1697 | wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); |
1433 | wl->wow_enabled = !!wow; | 1698 | WARN_ON(!wow || !wow->any); |
1434 | if (wl->wow_enabled) { | ||
1435 | int ret; | ||
1436 | ret = wl1271_configure_suspend(wl); | ||
1437 | if (ret < 0) { | ||
1438 | wl1271_warning("couldn't prepare device to suspend"); | ||
1439 | return ret; | ||
1440 | } | ||
1441 | /* flush any remaining work */ | ||
1442 | wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); | ||
1443 | flush_delayed_work(&wl->scan_complete_work); | ||
1444 | 1699 | ||
1445 | /* | 1700 | wl->wow_enabled = true; |
1446 | * disable and re-enable interrupts in order to flush | 1701 | ret = wl1271_configure_suspend(wl); |
1447 | * the threaded_irq | 1702 | if (ret < 0) { |
1448 | */ | 1703 | wl1271_warning("couldn't prepare device to suspend"); |
1449 | wl1271_disable_interrupts(wl); | 1704 | return ret; |
1705 | } | ||
1706 | /* flush any remaining work */ | ||
1707 | wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); | ||
1708 | flush_delayed_work(&wl->scan_complete_work); | ||
1450 | 1709 | ||
1451 | /* | 1710 | /* |
1452 | * set suspended flag to avoid triggering a new threaded_irq | 1711 | * disable and re-enable interrupts in order to flush |
1453 | * work. no need for spinlock as interrupts are disabled. | 1712 | * the threaded_irq |
1454 | */ | 1713 | */ |
1455 | set_bit(WL1271_FLAG_SUSPENDED, &wl->flags); | 1714 | wl1271_disable_interrupts(wl); |
1715 | |||
1716 | /* | ||
1717 | * set suspended flag to avoid triggering a new threaded_irq | ||
1718 | * work. no need for spinlock as interrupts are disabled. | ||
1719 | */ | ||
1720 | set_bit(WL1271_FLAG_SUSPENDED, &wl->flags); | ||
1721 | |||
1722 | wl1271_enable_interrupts(wl); | ||
1723 | flush_work(&wl->tx_work); | ||
1724 | flush_delayed_work(&wl->pspoll_work); | ||
1725 | flush_delayed_work(&wl->elp_work); | ||
1456 | 1726 | ||
1457 | wl1271_enable_interrupts(wl); | ||
1458 | flush_work(&wl->tx_work); | ||
1459 | flush_delayed_work(&wl->pspoll_work); | ||
1460 | flush_delayed_work(&wl->elp_work); | ||
1461 | } | ||
1462 | return 0; | 1727 | return 0; |
1463 | } | 1728 | } |
1464 | 1729 | ||
1465 | static int wl1271_op_resume(struct ieee80211_hw *hw) | 1730 | static int wl1271_op_resume(struct ieee80211_hw *hw) |
1466 | { | 1731 | { |
1467 | struct wl1271 *wl = hw->priv; | 1732 | struct wl1271 *wl = hw->priv; |
1733 | unsigned long flags; | ||
1734 | bool run_irq_work = false; | ||
1735 | |||
1468 | wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", | 1736 | wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", |
1469 | wl->wow_enabled); | 1737 | wl->wow_enabled); |
1738 | WARN_ON(!wl->wow_enabled); | ||
1470 | 1739 | ||
1471 | /* | 1740 | /* |
1472 | * re-enable irq_work enqueuing, and call irq_work directly if | 1741 | * re-enable irq_work enqueuing, and call irq_work directly if |
1473 | * there is a pending work. | 1742 | * there is a pending work. |
1474 | */ | 1743 | */ |
1475 | if (wl->wow_enabled) { | 1744 | spin_lock_irqsave(&wl->wl_lock, flags); |
1476 | struct wl1271 *wl = hw->priv; | 1745 | clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags); |
1477 | unsigned long flags; | 1746 | if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags)) |
1478 | bool run_irq_work = false; | 1747 | run_irq_work = true; |
1479 | 1748 | spin_unlock_irqrestore(&wl->wl_lock, flags); | |
1480 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
1481 | clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags); | ||
1482 | if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags)) | ||
1483 | run_irq_work = true; | ||
1484 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
1485 | |||
1486 | if (run_irq_work) { | ||
1487 | wl1271_debug(DEBUG_MAC80211, | ||
1488 | "run postponed irq_work directly"); | ||
1489 | wl1271_irq(0, wl); | ||
1490 | wl1271_enable_interrupts(wl); | ||
1491 | } | ||
1492 | 1749 | ||
1493 | wl1271_configure_resume(wl); | 1750 | if (run_irq_work) { |
1751 | wl1271_debug(DEBUG_MAC80211, | ||
1752 | "run postponed irq_work directly"); | ||
1753 | wl1271_irq(0, wl); | ||
1754 | wl1271_enable_interrupts(wl); | ||
1494 | } | 1755 | } |
1756 | wl1271_configure_resume(wl); | ||
1757 | wl->wow_enabled = false; | ||
1495 | 1758 | ||
1496 | return 0; | 1759 | return 0; |
1497 | } | 1760 | } |
@@ -1629,9 +1892,6 @@ power_off: | |||
1629 | strncpy(wiphy->fw_version, wl->chip.fw_ver_str, | 1892 | strncpy(wiphy->fw_version, wl->chip.fw_ver_str, |
1630 | sizeof(wiphy->fw_version)); | 1893 | sizeof(wiphy->fw_version)); |
1631 | 1894 | ||
1632 | /* Check if any quirks are needed with older fw versions */ | ||
1633 | wl->quirks |= wl1271_get_fw_ver_quirks(wl); | ||
1634 | |||
1635 | /* | 1895 | /* |
1636 | * Now we know if 11a is supported (info from the NVS), so disable | 1896 | * Now we know if 11a is supported (info from the NVS), so disable |
1637 | * 11a channels if not supported | 1897 | * 11a channels if not supported |
@@ -1694,6 +1954,9 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, | |||
1694 | cancel_delayed_work_sync(&wl->scan_complete_work); | 1954 | cancel_delayed_work_sync(&wl->scan_complete_work); |
1695 | cancel_work_sync(&wl->netstack_work); | 1955 | cancel_work_sync(&wl->netstack_work); |
1696 | cancel_work_sync(&wl->tx_work); | 1956 | cancel_work_sync(&wl->tx_work); |
1957 | del_timer_sync(&wl->rx_streaming_timer); | ||
1958 | cancel_work_sync(&wl->rx_streaming_enable_work); | ||
1959 | cancel_work_sync(&wl->rx_streaming_disable_work); | ||
1697 | cancel_delayed_work_sync(&wl->pspoll_work); | 1960 | cancel_delayed_work_sync(&wl->pspoll_work); |
1698 | cancel_delayed_work_sync(&wl->elp_work); | 1961 | cancel_delayed_work_sync(&wl->elp_work); |
1699 | 1962 | ||
@@ -2780,24 +3043,6 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, | |||
2780 | } | 3043 | } |
2781 | } | 3044 | } |
2782 | 3045 | ||
2783 | if (changed & BSS_CHANGED_IBSS) { | ||
2784 | wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d", | ||
2785 | bss_conf->ibss_joined); | ||
2786 | |||
2787 | if (bss_conf->ibss_joined) { | ||
2788 | u32 rates = bss_conf->basic_rates; | ||
2789 | wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, | ||
2790 | rates); | ||
2791 | wl->basic_rate = wl1271_tx_min_rate_get(wl); | ||
2792 | |||
2793 | /* by default, use 11b rates */ | ||
2794 | wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES; | ||
2795 | ret = wl1271_acx_sta_rate_policies(wl); | ||
2796 | if (ret < 0) | ||
2797 | goto out; | ||
2798 | } | ||
2799 | } | ||
2800 | |||
2801 | ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); | 3046 | ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); |
2802 | if (ret < 0) | 3047 | if (ret < 0) |
2803 | goto out; | 3048 | goto out; |
@@ -3023,6 +3268,24 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, | |||
3023 | } | 3268 | } |
3024 | } | 3269 | } |
3025 | 3270 | ||
3271 | if (changed & BSS_CHANGED_IBSS) { | ||
3272 | wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d", | ||
3273 | bss_conf->ibss_joined); | ||
3274 | |||
3275 | if (bss_conf->ibss_joined) { | ||
3276 | u32 rates = bss_conf->basic_rates; | ||
3277 | wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, | ||
3278 | rates); | ||
3279 | wl->basic_rate = wl1271_tx_min_rate_get(wl); | ||
3280 | |||
3281 | /* by default, use 11b rates */ | ||
3282 | wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES; | ||
3283 | ret = wl1271_acx_sta_rate_policies(wl); | ||
3284 | if (ret < 0) | ||
3285 | goto out; | ||
3286 | } | ||
3287 | } | ||
3288 | |||
3026 | ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); | 3289 | ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); |
3027 | if (ret < 0) | 3290 | if (ret < 0) |
3028 | goto out; | 3291 | goto out; |
@@ -3061,6 +3324,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, | |||
3061 | wl1271_warning("cmd join failed %d", ret); | 3324 | wl1271_warning("cmd join failed %d", ret); |
3062 | goto out; | 3325 | goto out; |
3063 | } | 3326 | } |
3327 | wl1271_check_operstate(wl, ieee80211_get_operstate(vif)); | ||
3064 | } | 3328 | } |
3065 | 3329 | ||
3066 | out: | 3330 | out: |
@@ -3784,6 +4048,69 @@ static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev, | |||
3784 | static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR, | 4048 | static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR, |
3785 | wl1271_sysfs_show_hw_pg_ver, NULL); | 4049 | wl1271_sysfs_show_hw_pg_ver, NULL); |
3786 | 4050 | ||
4051 | static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, | ||
4052 | struct bin_attribute *bin_attr, | ||
4053 | char *buffer, loff_t pos, size_t count) | ||
4054 | { | ||
4055 | struct device *dev = container_of(kobj, struct device, kobj); | ||
4056 | struct wl1271 *wl = dev_get_drvdata(dev); | ||
4057 | ssize_t len; | ||
4058 | int ret; | ||
4059 | |||
4060 | ret = mutex_lock_interruptible(&wl->mutex); | ||
4061 | if (ret < 0) | ||
4062 | return -ERESTARTSYS; | ||
4063 | |||
4064 | /* Let only one thread read the log at a time, blocking others */ | ||
4065 | while (wl->fwlog_size == 0) { | ||
4066 | DEFINE_WAIT(wait); | ||
4067 | |||
4068 | prepare_to_wait_exclusive(&wl->fwlog_waitq, | ||
4069 | &wait, | ||
4070 | TASK_INTERRUPTIBLE); | ||
4071 | |||
4072 | if (wl->fwlog_size != 0) { | ||
4073 | finish_wait(&wl->fwlog_waitq, &wait); | ||
4074 | break; | ||
4075 | } | ||
4076 | |||
4077 | mutex_unlock(&wl->mutex); | ||
4078 | |||
4079 | schedule(); | ||
4080 | finish_wait(&wl->fwlog_waitq, &wait); | ||
4081 | |||
4082 | if (signal_pending(current)) | ||
4083 | return -ERESTARTSYS; | ||
4084 | |||
4085 | ret = mutex_lock_interruptible(&wl->mutex); | ||
4086 | if (ret < 0) | ||
4087 | return -ERESTARTSYS; | ||
4088 | } | ||
4089 | |||
4090 | /* Check if the fwlog is still valid */ | ||
4091 | if (wl->fwlog_size < 0) { | ||
4092 | mutex_unlock(&wl->mutex); | ||
4093 | return 0; | ||
4094 | } | ||
4095 | |||
4096 | /* Seeking is not supported - old logs are not kept. Disregard pos. */ | ||
4097 | len = min(count, (size_t)wl->fwlog_size); | ||
4098 | wl->fwlog_size -= len; | ||
4099 | memcpy(buffer, wl->fwlog, len); | ||
4100 | |||
4101 | /* Make room for new messages */ | ||
4102 | memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size); | ||
4103 | |||
4104 | mutex_unlock(&wl->mutex); | ||
4105 | |||
4106 | return len; | ||
4107 | } | ||
4108 | |||
4109 | static struct bin_attribute fwlog_attr = { | ||
4110 | .attr = {.name = "fwlog", .mode = S_IRUSR}, | ||
4111 | .read = wl1271_sysfs_read_fwlog, | ||
4112 | }; | ||
4113 | |||
3787 | int wl1271_register_hw(struct wl1271 *wl) | 4114 | int wl1271_register_hw(struct wl1271 *wl) |
3788 | { | 4115 | { |
3789 | int ret; | 4116 | int ret; |
@@ -3964,6 +4291,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
3964 | INIT_WORK(&wl->tx_work, wl1271_tx_work); | 4291 | INIT_WORK(&wl->tx_work, wl1271_tx_work); |
3965 | INIT_WORK(&wl->recovery_work, wl1271_recovery_work); | 4292 | INIT_WORK(&wl->recovery_work, wl1271_recovery_work); |
3966 | INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); | 4293 | INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); |
4294 | INIT_WORK(&wl->rx_streaming_enable_work, | ||
4295 | wl1271_rx_streaming_enable_work); | ||
4296 | INIT_WORK(&wl->rx_streaming_disable_work, | ||
4297 | wl1271_rx_streaming_disable_work); | ||
4298 | |||
4299 | wl->freezable_wq = create_freezable_workqueue("wl12xx_wq"); | ||
4300 | if (!wl->freezable_wq) { | ||
4301 | ret = -ENOMEM; | ||
4302 | goto err_hw; | ||
4303 | } | ||
4304 | |||
3967 | wl->channel = WL1271_DEFAULT_CHANNEL; | 4305 | wl->channel = WL1271_DEFAULT_CHANNEL; |
3968 | wl->beacon_int = WL1271_DEFAULT_BEACON_INT; | 4306 | wl->beacon_int = WL1271_DEFAULT_BEACON_INT; |
3969 | wl->default_key = 0; | 4307 | wl->default_key = 0; |
@@ -3989,6 +4327,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
3989 | wl->quirks = 0; | 4327 | wl->quirks = 0; |
3990 | wl->platform_quirks = 0; | 4328 | wl->platform_quirks = 0; |
3991 | wl->sched_scanning = false; | 4329 | wl->sched_scanning = false; |
4330 | setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer, | ||
4331 | (unsigned long) wl); | ||
4332 | wl->fwlog_size = 0; | ||
4333 | init_waitqueue_head(&wl->fwlog_waitq); | ||
3992 | 4334 | ||
3993 | memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); | 4335 | memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); |
3994 | for (i = 0; i < ACX_TX_DESCRIPTORS; i++) | 4336 | for (i = 0; i < ACX_TX_DESCRIPTORS; i++) |
@@ -4006,7 +4348,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
4006 | wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); | 4348 | wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); |
4007 | if (!wl->aggr_buf) { | 4349 | if (!wl->aggr_buf) { |
4008 | ret = -ENOMEM; | 4350 | ret = -ENOMEM; |
4009 | goto err_hw; | 4351 | goto err_wq; |
4010 | } | 4352 | } |
4011 | 4353 | ||
4012 | wl->dummy_packet = wl12xx_alloc_dummy_packet(wl); | 4354 | wl->dummy_packet = wl12xx_alloc_dummy_packet(wl); |
@@ -4015,11 +4357,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
4015 | goto err_aggr; | 4357 | goto err_aggr; |
4016 | } | 4358 | } |
4017 | 4359 | ||
4360 | /* Allocate one page for the FW log */ | ||
4361 | wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL); | ||
4362 | if (!wl->fwlog) { | ||
4363 | ret = -ENOMEM; | ||
4364 | goto err_dummy_packet; | ||
4365 | } | ||
4366 | |||
4018 | /* Register platform device */ | 4367 | /* Register platform device */ |
4019 | ret = platform_device_register(wl->plat_dev); | 4368 | ret = platform_device_register(wl->plat_dev); |
4020 | if (ret) { | 4369 | if (ret) { |
4021 | wl1271_error("couldn't register platform device"); | 4370 | wl1271_error("couldn't register platform device"); |
4022 | goto err_dummy_packet; | 4371 | goto err_fwlog; |
4023 | } | 4372 | } |
4024 | dev_set_drvdata(&wl->plat_dev->dev, wl); | 4373 | dev_set_drvdata(&wl->plat_dev->dev, wl); |
4025 | 4374 | ||
@@ -4037,20 +4386,36 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
4037 | goto err_bt_coex_state; | 4386 | goto err_bt_coex_state; |
4038 | } | 4387 | } |
4039 | 4388 | ||
4389 | /* Create sysfs file for the FW log */ | ||
4390 | ret = device_create_bin_file(&wl->plat_dev->dev, &fwlog_attr); | ||
4391 | if (ret < 0) { | ||
4392 | wl1271_error("failed to create sysfs file fwlog"); | ||
4393 | goto err_hw_pg_ver; | ||
4394 | } | ||
4395 | |||
4040 | return hw; | 4396 | return hw; |
4041 | 4397 | ||
4398 | err_hw_pg_ver: | ||
4399 | device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver); | ||
4400 | |||
4042 | err_bt_coex_state: | 4401 | err_bt_coex_state: |
4043 | device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state); | 4402 | device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state); |
4044 | 4403 | ||
4045 | err_platform: | 4404 | err_platform: |
4046 | platform_device_unregister(wl->plat_dev); | 4405 | platform_device_unregister(wl->plat_dev); |
4047 | 4406 | ||
4407 | err_fwlog: | ||
4408 | free_page((unsigned long)wl->fwlog); | ||
4409 | |||
4048 | err_dummy_packet: | 4410 | err_dummy_packet: |
4049 | dev_kfree_skb(wl->dummy_packet); | 4411 | dev_kfree_skb(wl->dummy_packet); |
4050 | 4412 | ||
4051 | err_aggr: | 4413 | err_aggr: |
4052 | free_pages((unsigned long)wl->aggr_buf, order); | 4414 | free_pages((unsigned long)wl->aggr_buf, order); |
4053 | 4415 | ||
4416 | err_wq: | ||
4417 | destroy_workqueue(wl->freezable_wq); | ||
4418 | |||
4054 | err_hw: | 4419 | err_hw: |
4055 | wl1271_debugfs_exit(wl); | 4420 | wl1271_debugfs_exit(wl); |
4056 | kfree(plat_dev); | 4421 | kfree(plat_dev); |
@@ -4066,7 +4431,15 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); | |||
4066 | 4431 | ||
4067 | int wl1271_free_hw(struct wl1271 *wl) | 4432 | int wl1271_free_hw(struct wl1271 *wl) |
4068 | { | 4433 | { |
4434 | /* Unblock any fwlog readers */ | ||
4435 | mutex_lock(&wl->mutex); | ||
4436 | wl->fwlog_size = -1; | ||
4437 | wake_up_interruptible_all(&wl->fwlog_waitq); | ||
4438 | mutex_unlock(&wl->mutex); | ||
4439 | |||
4440 | device_remove_bin_file(&wl->plat_dev->dev, &fwlog_attr); | ||
4069 | platform_device_unregister(wl->plat_dev); | 4441 | platform_device_unregister(wl->plat_dev); |
4442 | free_page((unsigned long)wl->fwlog); | ||
4070 | dev_kfree_skb(wl->dummy_packet); | 4443 | dev_kfree_skb(wl->dummy_packet); |
4071 | free_pages((unsigned long)wl->aggr_buf, | 4444 | free_pages((unsigned long)wl->aggr_buf, |
4072 | get_order(WL1271_AGGR_BUFFER_SIZE)); | 4445 | get_order(WL1271_AGGR_BUFFER_SIZE)); |
@@ -4081,6 +4454,7 @@ int wl1271_free_hw(struct wl1271 *wl) | |||
4081 | 4454 | ||
4082 | kfree(wl->fw_status); | 4455 | kfree(wl->fw_status); |
4083 | kfree(wl->tx_res_if); | 4456 | kfree(wl->tx_res_if); |
4457 | destroy_workqueue(wl->freezable_wq); | ||
4084 | 4458 | ||
4085 | ieee80211_free_hw(wl->hw); | 4459 | ieee80211_free_hw(wl->hw); |
4086 | 4460 | ||
@@ -4093,6 +4467,10 @@ EXPORT_SYMBOL_GPL(wl12xx_debug_level); | |||
4093 | module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); | 4467 | module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); |
4094 | MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); | 4468 | MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); |
4095 | 4469 | ||
4470 | module_param_named(fwlog, fwlog_param, charp, 0); | ||
4471 | MODULE_PARM_DESC(keymap, | ||
4472 | "FW logger options: continuous, ondemand, dbgpins or disable"); | ||
4473 | |||
4096 | MODULE_LICENSE("GPL"); | 4474 | MODULE_LICENSE("GPL"); |
4097 | MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); | 4475 | MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); |
4098 | MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); | 4476 | MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); |
diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index b59b67711a17..3e68a664c9de 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c | |||
@@ -118,7 +118,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) | |||
118 | &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); | 118 | &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); |
119 | if (ret == 0) { | 119 | if (ret == 0) { |
120 | wl1271_error("ELP wakeup timeout!"); | 120 | wl1271_error("ELP wakeup timeout!"); |
121 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | 121 | wl12xx_queue_recovery_work(wl); |
122 | ret = -ETIMEDOUT; | 122 | ret = -ETIMEDOUT; |
123 | goto err; | 123 | goto err; |
124 | } else if (ret < 0) { | 124 | } else if (ret < 0) { |
@@ -169,9 +169,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, | |||
169 | wl1271_debug(DEBUG_PSM, "leaving psm"); | 169 | wl1271_debug(DEBUG_PSM, "leaving psm"); |
170 | 170 | ||
171 | /* disable beacon early termination */ | 171 | /* disable beacon early termination */ |
172 | ret = wl1271_acx_bet_enable(wl, false); | 172 | if (wl->band == IEEE80211_BAND_2GHZ) { |
173 | if (ret < 0) | 173 | ret = wl1271_acx_bet_enable(wl, false); |
174 | return ret; | 174 | if (ret < 0) |
175 | return ret; | ||
176 | } | ||
175 | 177 | ||
176 | /* disable beacon filtering */ | 178 | /* disable beacon filtering */ |
177 | ret = wl1271_acx_beacon_filter_opt(wl, false); | 179 | ret = wl1271_acx_beacon_filter_opt(wl, false); |
@@ -202,7 +204,7 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) | |||
202 | info = IEEE80211_SKB_CB(skb); | 204 | info = IEEE80211_SKB_CB(skb); |
203 | info->flags |= IEEE80211_TX_STAT_TX_FILTERED; | 205 | info->flags |= IEEE80211_TX_STAT_TX_FILTERED; |
204 | info->status.rates[0].idx = -1; | 206 | info->status.rates[0].idx = -1; |
205 | ieee80211_tx_status(wl->hw, skb); | 207 | ieee80211_tx_status_ni(wl->hw, skb); |
206 | filtered++; | 208 | filtered++; |
207 | } | 209 | } |
208 | } | 210 | } |
diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 70091035e019..0450fb49dbb1 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c | |||
@@ -22,6 +22,7 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/gfp.h> | 24 | #include <linux/gfp.h> |
25 | #include <linux/sched.h> | ||
25 | 26 | ||
26 | #include "wl12xx.h" | 27 | #include "wl12xx.h" |
27 | #include "acx.h" | 28 | #include "acx.h" |
@@ -95,6 +96,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) | |||
95 | struct ieee80211_hdr *hdr; | 96 | struct ieee80211_hdr *hdr; |
96 | u8 *buf; | 97 | u8 *buf; |
97 | u8 beacon = 0; | 98 | u8 beacon = 0; |
99 | u8 is_data = 0; | ||
98 | 100 | ||
99 | /* | 101 | /* |
100 | * In PLT mode we seem to get frames and mac80211 warns about them, | 102 | * In PLT mode we seem to get frames and mac80211 warns about them, |
@@ -106,6 +108,13 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) | |||
106 | /* the data read starts with the descriptor */ | 108 | /* the data read starts with the descriptor */ |
107 | desc = (struct wl1271_rx_descriptor *) data; | 109 | desc = (struct wl1271_rx_descriptor *) data; |
108 | 110 | ||
111 | if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) { | ||
112 | size_t len = length - sizeof(*desc); | ||
113 | wl12xx_copy_fwlog(wl, data + sizeof(*desc), len); | ||
114 | wake_up_interruptible(&wl->fwlog_waitq); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
109 | switch (desc->status & WL1271_RX_DESC_STATUS_MASK) { | 118 | switch (desc->status & WL1271_RX_DESC_STATUS_MASK) { |
110 | /* discard corrupted packets */ | 119 | /* discard corrupted packets */ |
111 | case WL1271_RX_DESC_DRIVER_RX_Q_FAIL: | 120 | case WL1271_RX_DESC_DRIVER_RX_Q_FAIL: |
@@ -137,6 +146,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) | |||
137 | hdr = (struct ieee80211_hdr *)skb->data; | 146 | hdr = (struct ieee80211_hdr *)skb->data; |
138 | if (ieee80211_is_beacon(hdr->frame_control)) | 147 | if (ieee80211_is_beacon(hdr->frame_control)) |
139 | beacon = 1; | 148 | beacon = 1; |
149 | if (ieee80211_is_data_present(hdr->frame_control)) | ||
150 | is_data = 1; | ||
140 | 151 | ||
141 | wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); | 152 | wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); |
142 | 153 | ||
@@ -147,9 +158,9 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) | |||
147 | skb_trim(skb, skb->len - desc->pad_len); | 158 | skb_trim(skb, skb->len - desc->pad_len); |
148 | 159 | ||
149 | skb_queue_tail(&wl->deferred_rx_queue, skb); | 160 | skb_queue_tail(&wl->deferred_rx_queue, skb); |
150 | ieee80211_queue_work(wl->hw, &wl->netstack_work); | 161 | queue_work(wl->freezable_wq, &wl->netstack_work); |
151 | 162 | ||
152 | return 0; | 163 | return is_data; |
153 | } | 164 | } |
154 | 165 | ||
155 | void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) | 166 | void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) |
@@ -162,6 +173,8 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) | |||
162 | u32 mem_block; | 173 | u32 mem_block; |
163 | u32 pkt_length; | 174 | u32 pkt_length; |
164 | u32 pkt_offset; | 175 | u32 pkt_offset; |
176 | bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); | ||
177 | bool had_data = false; | ||
165 | 178 | ||
166 | while (drv_rx_counter != fw_rx_counter) { | 179 | while (drv_rx_counter != fw_rx_counter) { |
167 | buf_size = 0; | 180 | buf_size = 0; |
@@ -214,9 +227,11 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) | |||
214 | * conditions, in that case the received frame will just | 227 | * conditions, in that case the received frame will just |
215 | * be dropped. | 228 | * be dropped. |
216 | */ | 229 | */ |
217 | wl1271_rx_handle_data(wl, | 230 | if (wl1271_rx_handle_data(wl, |
218 | wl->aggr_buf + pkt_offset, | 231 | wl->aggr_buf + pkt_offset, |
219 | pkt_length); | 232 | pkt_length) == 1) |
233 | had_data = true; | ||
234 | |||
220 | wl->rx_counter++; | 235 | wl->rx_counter++; |
221 | drv_rx_counter++; | 236 | drv_rx_counter++; |
222 | drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; | 237 | drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; |
@@ -230,6 +245,20 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) | |||
230 | */ | 245 | */ |
231 | if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION) | 246 | if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION) |
232 | wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); | 247 | wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); |
248 | |||
249 | if (!is_ap && wl->conf.rx_streaming.interval && had_data && | ||
250 | (wl->conf.rx_streaming.always || | ||
251 | test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) { | ||
252 | u32 timeout = wl->conf.rx_streaming.duration; | ||
253 | |||
254 | /* restart rx streaming */ | ||
255 | if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags)) | ||
256 | ieee80211_queue_work(wl->hw, | ||
257 | &wl->rx_streaming_enable_work); | ||
258 | |||
259 | mod_timer(&wl->rx_streaming_timer, | ||
260 | jiffies + msecs_to_jiffies(timeout)); | ||
261 | } | ||
233 | } | 262 | } |
234 | 263 | ||
235 | void wl1271_set_default_filters(struct wl1271 *wl) | 264 | void wl1271_set_default_filters(struct wl1271 *wl) |
diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h index 75fabf836491..c88e3fa1d603 100644 --- a/drivers/net/wireless/wl12xx/rx.h +++ b/drivers/net/wireless/wl12xx/rx.h | |||
@@ -97,6 +97,18 @@ | |||
97 | #define RX_BUF_SIZE_MASK 0xFFF00 | 97 | #define RX_BUF_SIZE_MASK 0xFFF00 |
98 | #define RX_BUF_SIZE_SHIFT_DIV 6 | 98 | #define RX_BUF_SIZE_SHIFT_DIV 6 |
99 | 99 | ||
100 | enum { | ||
101 | WL12XX_RX_CLASS_UNKNOWN, | ||
102 | WL12XX_RX_CLASS_MANAGEMENT, | ||
103 | WL12XX_RX_CLASS_DATA, | ||
104 | WL12XX_RX_CLASS_QOS_DATA, | ||
105 | WL12XX_RX_CLASS_BCN_PRBRSP, | ||
106 | WL12XX_RX_CLASS_EAPOL, | ||
107 | WL12XX_RX_CLASS_BA_EVENT, | ||
108 | WL12XX_RX_CLASS_AMSDU, | ||
109 | WL12XX_RX_CLASS_LOGGER, | ||
110 | }; | ||
111 | |||
100 | struct wl1271_rx_descriptor { | 112 | struct wl1271_rx_descriptor { |
101 | __le16 length; | 113 | __le16 length; |
102 | u8 status; | 114 | u8 status; |
diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 56f76abc754d..5e5c66dd06d5 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c | |||
@@ -62,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work) | |||
62 | 62 | ||
63 | if (wl->scan.failed) { | 63 | if (wl->scan.failed) { |
64 | wl1271_info("Scan completed due to error."); | 64 | wl1271_info("Scan completed due to error."); |
65 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | 65 | wl12xx_queue_recovery_work(wl); |
66 | } | 66 | } |
67 | 67 | ||
68 | out: | 68 | out: |
@@ -326,7 +326,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, | |||
326 | struct cfg80211_sched_scan_request *req, | 326 | struct cfg80211_sched_scan_request *req, |
327 | struct conn_scan_ch_params *channels, | 327 | struct conn_scan_ch_params *channels, |
328 | u32 band, bool radar, bool passive, | 328 | u32 band, bool radar, bool passive, |
329 | int start) | 329 | int start, int max_channels) |
330 | { | 330 | { |
331 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | 331 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; |
332 | int i, j; | 332 | int i, j; |
@@ -334,7 +334,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, | |||
334 | bool force_passive = !req->n_ssids; | 334 | bool force_passive = !req->n_ssids; |
335 | 335 | ||
336 | for (i = 0, j = start; | 336 | for (i = 0, j = start; |
337 | i < req->n_channels && j < MAX_CHANNELS_ALL_BANDS; | 337 | i < req->n_channels && j < max_channels; |
338 | i++) { | 338 | i++) { |
339 | flags = req->channels[i]->flags; | 339 | flags = req->channels[i]->flags; |
340 | 340 | ||
@@ -380,46 +380,42 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, | |||
380 | return j - start; | 380 | return j - start; |
381 | } | 381 | } |
382 | 382 | ||
383 | static int | 383 | static bool |
384 | wl1271_scan_sched_scan_channels(struct wl1271 *wl, | 384 | wl1271_scan_sched_scan_channels(struct wl1271 *wl, |
385 | struct cfg80211_sched_scan_request *req, | 385 | struct cfg80211_sched_scan_request *req, |
386 | struct wl1271_cmd_sched_scan_config *cfg) | 386 | struct wl1271_cmd_sched_scan_config *cfg) |
387 | { | 387 | { |
388 | int idx = 0; | ||
389 | |||
390 | cfg->passive[0] = | 388 | cfg->passive[0] = |
391 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | 389 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, |
392 | IEEE80211_BAND_2GHZ, | 390 | IEEE80211_BAND_2GHZ, |
393 | false, true, idx); | 391 | false, true, 0, |
394 | idx += cfg->passive[0]; | 392 | MAX_CHANNELS_2GHZ); |
395 | |||
396 | cfg->active[0] = | 393 | cfg->active[0] = |
397 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | 394 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, |
398 | IEEE80211_BAND_2GHZ, | 395 | IEEE80211_BAND_2GHZ, |
399 | false, false, idx); | 396 | false, false, |
400 | /* | 397 | cfg->passive[0], |
401 | * 5GHz channels always start at position 14, not immediately | 398 | MAX_CHANNELS_2GHZ); |
402 | * after the last 2.4GHz channel | ||
403 | */ | ||
404 | idx = 14; | ||
405 | |||
406 | cfg->passive[1] = | 399 | cfg->passive[1] = |
407 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | 400 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, |
408 | IEEE80211_BAND_5GHZ, | 401 | IEEE80211_BAND_5GHZ, |
409 | false, true, idx); | 402 | false, true, 0, |
410 | idx += cfg->passive[1]; | 403 | MAX_CHANNELS_5GHZ); |
411 | |||
412 | cfg->dfs = | 404 | cfg->dfs = |
413 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | 405 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, |
414 | IEEE80211_BAND_5GHZ, | 406 | IEEE80211_BAND_5GHZ, |
415 | true, true, idx); | 407 | true, true, |
416 | idx += cfg->dfs; | 408 | cfg->passive[1], |
417 | 409 | MAX_CHANNELS_5GHZ); | |
418 | cfg->active[1] = | 410 | cfg->active[1] = |
419 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | 411 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, |
420 | IEEE80211_BAND_5GHZ, | 412 | IEEE80211_BAND_5GHZ, |
421 | false, false, idx); | 413 | false, false, |
422 | idx += cfg->active[1]; | 414 | cfg->passive[1] + cfg->dfs, |
415 | MAX_CHANNELS_5GHZ); | ||
416 | /* 802.11j channels are not supported yet */ | ||
417 | cfg->passive[2] = 0; | ||
418 | cfg->active[2] = 0; | ||
423 | 419 | ||
424 | wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", | 420 | wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", |
425 | cfg->active[0], cfg->passive[0]); | 421 | cfg->active[0], cfg->passive[0]); |
@@ -427,7 +423,9 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl, | |||
427 | cfg->active[1], cfg->passive[1]); | 423 | cfg->active[1], cfg->passive[1]); |
428 | wl1271_debug(DEBUG_SCAN, " DFS: %d", cfg->dfs); | 424 | wl1271_debug(DEBUG_SCAN, " DFS: %d", cfg->dfs); |
429 | 425 | ||
430 | return idx; | 426 | return cfg->passive[0] || cfg->active[0] || |
427 | cfg->passive[1] || cfg->active[1] || cfg->dfs || | ||
428 | cfg->passive[2] || cfg->active[2]; | ||
431 | } | 429 | } |
432 | 430 | ||
433 | int wl1271_scan_sched_scan_config(struct wl1271 *wl, | 431 | int wl1271_scan_sched_scan_config(struct wl1271 *wl, |
@@ -436,7 +434,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, | |||
436 | { | 434 | { |
437 | struct wl1271_cmd_sched_scan_config *cfg = NULL; | 435 | struct wl1271_cmd_sched_scan_config *cfg = NULL; |
438 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | 436 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; |
439 | int i, total_channels, ret; | 437 | int i, ret; |
440 | bool force_passive = !req->n_ssids; | 438 | bool force_passive = !req->n_ssids; |
441 | 439 | ||
442 | wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); | 440 | wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); |
@@ -471,8 +469,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, | |||
471 | cfg->ssid_len = 0; | 469 | cfg->ssid_len = 0; |
472 | } | 470 | } |
473 | 471 | ||
474 | total_channels = wl1271_scan_sched_scan_channels(wl, req, cfg); | 472 | if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) { |
475 | if (total_channels == 0) { | ||
476 | wl1271_error("scan channel list is empty"); | 473 | wl1271_error("scan channel list is empty"); |
477 | ret = -EINVAL; | 474 | ret = -EINVAL; |
478 | goto out; | 475 | goto out; |
diff --git a/drivers/net/wireless/wl12xx/scan.h b/drivers/net/wireless/wl12xx/scan.h index a0b6c5d67b07..ca81de20ebef 100644 --- a/drivers/net/wireless/wl12xx/scan.h +++ b/drivers/net/wireless/wl12xx/scan.h | |||
@@ -112,19 +112,14 @@ struct wl1271_cmd_trigger_scan_to { | |||
112 | __le32 timeout; | 112 | __le32 timeout; |
113 | } __packed; | 113 | } __packed; |
114 | 114 | ||
115 | #define MAX_CHANNELS_ALL_BANDS 41 | 115 | #define MAX_CHANNELS_2GHZ 14 |
116 | #define MAX_CHANNELS_5GHZ 23 | ||
117 | #define MAX_CHANNELS_4GHZ 4 | ||
118 | |||
116 | #define SCAN_MAX_CYCLE_INTERVALS 16 | 119 | #define SCAN_MAX_CYCLE_INTERVALS 16 |
117 | #define SCAN_MAX_BANDS 3 | 120 | #define SCAN_MAX_BANDS 3 |
118 | 121 | ||
119 | enum { | 122 | enum { |
120 | SCAN_CHANNEL_TYPE_2GHZ_PASSIVE, | ||
121 | SCAN_CHANNEL_TYPE_2GHZ_ACTIVE, | ||
122 | SCAN_CHANNEL_TYPE_5GHZ_PASSIVE, | ||
123 | SCAN_CHANNEL_TYPE_5GHZ_ACTIVE, | ||
124 | SCAN_CHANNEL_TYPE_5GHZ_DFS, | ||
125 | }; | ||
126 | |||
127 | enum { | ||
128 | SCAN_SSID_FILTER_ANY = 0, | 123 | SCAN_SSID_FILTER_ANY = 0, |
129 | SCAN_SSID_FILTER_SPECIFIC = 1, | 124 | SCAN_SSID_FILTER_SPECIFIC = 1, |
130 | SCAN_SSID_FILTER_LIST = 2, | 125 | SCAN_SSID_FILTER_LIST = 2, |
@@ -182,7 +177,9 @@ struct wl1271_cmd_sched_scan_config { | |||
182 | 177 | ||
183 | u8 padding[3]; | 178 | u8 padding[3]; |
184 | 179 | ||
185 | struct conn_scan_ch_params channels[MAX_CHANNELS_ALL_BANDS]; | 180 | struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; |
181 | struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; | ||
182 | struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; | ||
186 | } __packed; | 183 | } __packed; |
187 | 184 | ||
188 | 185 | ||
diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 536e5065454b..4dc4573b6861 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c | |||
@@ -23,7 +23,6 @@ | |||
23 | 23 | ||
24 | #include <linux/irq.h> | 24 | #include <linux/irq.h> |
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/crc7.h> | ||
27 | #include <linux/vmalloc.h> | 26 | #include <linux/vmalloc.h> |
28 | #include <linux/mmc/sdio_func.h> | 27 | #include <linux/mmc/sdio_func.h> |
29 | #include <linux/mmc/sdio_ids.h> | 28 | #include <linux/mmc/sdio_ids.h> |
@@ -45,7 +44,7 @@ | |||
45 | #define SDIO_DEVICE_ID_TI_WL1271 0x4076 | 44 | #define SDIO_DEVICE_ID_TI_WL1271 0x4076 |
46 | #endif | 45 | #endif |
47 | 46 | ||
48 | static const struct sdio_device_id wl1271_devices[] = { | 47 | static const struct sdio_device_id wl1271_devices[] __devinitconst = { |
49 | { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) }, | 48 | { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) }, |
50 | {} | 49 | {} |
51 | }; | 50 | }; |
@@ -107,14 +106,6 @@ static void wl1271_sdio_enable_interrupts(struct wl1271 *wl) | |||
107 | enable_irq(wl->irq); | 106 | enable_irq(wl->irq); |
108 | } | 107 | } |
109 | 108 | ||
110 | static void wl1271_sdio_reset(struct wl1271 *wl) | ||
111 | { | ||
112 | } | ||
113 | |||
114 | static void wl1271_sdio_init(struct wl1271 *wl) | ||
115 | { | ||
116 | } | ||
117 | |||
118 | static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, | 109 | static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, |
119 | size_t len, bool fixed) | 110 | size_t len, bool fixed) |
120 | { | 111 | { |
@@ -170,10 +161,12 @@ static int wl1271_sdio_power_on(struct wl1271 *wl) | |||
170 | struct sdio_func *func = wl_to_func(wl); | 161 | struct sdio_func *func = wl_to_func(wl); |
171 | int ret; | 162 | int ret; |
172 | 163 | ||
173 | /* Make sure the card will not be powered off by runtime PM */ | 164 | /* If enabled, tell runtime PM not to power off the card */ |
174 | ret = pm_runtime_get_sync(&func->dev); | 165 | if (pm_runtime_enabled(&func->dev)) { |
175 | if (ret < 0) | 166 | ret = pm_runtime_get_sync(&func->dev); |
176 | goto out; | 167 | if (ret) |
168 | goto out; | ||
169 | } | ||
177 | 170 | ||
178 | /* Runtime PM might be disabled, so power up the card manually */ | 171 | /* Runtime PM might be disabled, so power up the card manually */ |
179 | ret = mmc_power_restore_host(func->card->host); | 172 | ret = mmc_power_restore_host(func->card->host); |
@@ -200,8 +193,11 @@ static int wl1271_sdio_power_off(struct wl1271 *wl) | |||
200 | if (ret < 0) | 193 | if (ret < 0) |
201 | return ret; | 194 | return ret; |
202 | 195 | ||
203 | /* Let runtime PM know the card is powered off */ | 196 | /* If enabled, let runtime PM know the card is powered off */ |
204 | return pm_runtime_put_sync(&func->dev); | 197 | if (pm_runtime_enabled(&func->dev)) |
198 | ret = pm_runtime_put_sync(&func->dev); | ||
199 | |||
200 | return ret; | ||
205 | } | 201 | } |
206 | 202 | ||
207 | static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable) | 203 | static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable) |
@@ -215,8 +211,6 @@ static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable) | |||
215 | static struct wl1271_if_operations sdio_ops = { | 211 | static struct wl1271_if_operations sdio_ops = { |
216 | .read = wl1271_sdio_raw_read, | 212 | .read = wl1271_sdio_raw_read, |
217 | .write = wl1271_sdio_raw_write, | 213 | .write = wl1271_sdio_raw_write, |
218 | .reset = wl1271_sdio_reset, | ||
219 | .init = wl1271_sdio_init, | ||
220 | .power = wl1271_sdio_set_power, | 214 | .power = wl1271_sdio_set_power, |
221 | .dev = wl1271_sdio_wl_to_dev, | 215 | .dev = wl1271_sdio_wl_to_dev, |
222 | .enable_irq = wl1271_sdio_enable_interrupts, | 216 | .enable_irq = wl1271_sdio_enable_interrupts, |
@@ -278,17 +272,19 @@ static int __devinit wl1271_probe(struct sdio_func *func, | |||
278 | goto out_free; | 272 | goto out_free; |
279 | } | 273 | } |
280 | 274 | ||
281 | enable_irq_wake(wl->irq); | 275 | ret = enable_irq_wake(wl->irq); |
282 | device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1); | 276 | if (!ret) { |
283 | 277 | wl->irq_wake_enabled = true; | |
284 | disable_irq(wl->irq); | 278 | device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1); |
285 | 279 | ||
286 | /* if sdio can keep power while host is suspended, enable wow */ | 280 | /* if sdio can keep power while host is suspended, enable wow */ |
287 | mmcflags = sdio_get_host_pm_caps(func); | 281 | mmcflags = sdio_get_host_pm_caps(func); |
288 | wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags); | 282 | wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags); |
289 | 283 | ||
290 | if (mmcflags & MMC_PM_KEEP_POWER) | 284 | if (mmcflags & MMC_PM_KEEP_POWER) |
291 | hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY; | 285 | hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY; |
286 | } | ||
287 | disable_irq(wl->irq); | ||
292 | 288 | ||
293 | ret = wl1271_init_ieee80211(wl); | 289 | ret = wl1271_init_ieee80211(wl); |
294 | if (ret) | 290 | if (ret) |
@@ -303,8 +299,6 @@ static int __devinit wl1271_probe(struct sdio_func *func, | |||
303 | /* Tell PM core that we don't need the card to be powered now */ | 299 | /* Tell PM core that we don't need the card to be powered now */ |
304 | pm_runtime_put_noidle(&func->dev); | 300 | pm_runtime_put_noidle(&func->dev); |
305 | 301 | ||
306 | wl1271_notice("initialized"); | ||
307 | |||
308 | return 0; | 302 | return 0; |
309 | 303 | ||
310 | out_irq: | 304 | out_irq: |
@@ -324,8 +318,10 @@ static void __devexit wl1271_remove(struct sdio_func *func) | |||
324 | pm_runtime_get_noresume(&func->dev); | 318 | pm_runtime_get_noresume(&func->dev); |
325 | 319 | ||
326 | wl1271_unregister_hw(wl); | 320 | wl1271_unregister_hw(wl); |
327 | device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0); | 321 | if (wl->irq_wake_enabled) { |
328 | disable_irq_wake(wl->irq); | 322 | device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0); |
323 | disable_irq_wake(wl->irq); | ||
324 | } | ||
329 | free_irq(wl->irq, wl); | 325 | free_irq(wl->irq, wl); |
330 | wl1271_free_hw(wl); | 326 | wl1271_free_hw(wl); |
331 | } | 327 | } |
@@ -402,23 +398,12 @@ static struct sdio_driver wl1271_sdio_driver = { | |||
402 | 398 | ||
403 | static int __init wl1271_init(void) | 399 | static int __init wl1271_init(void) |
404 | { | 400 | { |
405 | int ret; | 401 | return sdio_register_driver(&wl1271_sdio_driver); |
406 | |||
407 | ret = sdio_register_driver(&wl1271_sdio_driver); | ||
408 | if (ret < 0) { | ||
409 | wl1271_error("failed to register sdio driver: %d", ret); | ||
410 | goto out; | ||
411 | } | ||
412 | |||
413 | out: | ||
414 | return ret; | ||
415 | } | 402 | } |
416 | 403 | ||
417 | static void __exit wl1271_exit(void) | 404 | static void __exit wl1271_exit(void) |
418 | { | 405 | { |
419 | sdio_unregister_driver(&wl1271_sdio_driver); | 406 | sdio_unregister_driver(&wl1271_sdio_driver); |
420 | |||
421 | wl1271_notice("unloaded"); | ||
422 | } | 407 | } |
423 | 408 | ||
424 | module_init(wl1271_init); | 409 | module_init(wl1271_init); |
diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 51662bb68019..b73cee117ada 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c | |||
@@ -435,8 +435,6 @@ static int __devinit wl1271_probe(struct spi_device *spi) | |||
435 | if (ret) | 435 | if (ret) |
436 | goto out_irq; | 436 | goto out_irq; |
437 | 437 | ||
438 | wl1271_notice("initialized"); | ||
439 | |||
440 | return 0; | 438 | return 0; |
441 | 439 | ||
442 | out_irq: | 440 | out_irq: |
@@ -473,23 +471,12 @@ static struct spi_driver wl1271_spi_driver = { | |||
473 | 471 | ||
474 | static int __init wl1271_init(void) | 472 | static int __init wl1271_init(void) |
475 | { | 473 | { |
476 | int ret; | 474 | return spi_register_driver(&wl1271_spi_driver); |
477 | |||
478 | ret = spi_register_driver(&wl1271_spi_driver); | ||
479 | if (ret < 0) { | ||
480 | wl1271_error("failed to register spi driver: %d", ret); | ||
481 | goto out; | ||
482 | } | ||
483 | |||
484 | out: | ||
485 | return ret; | ||
486 | } | 475 | } |
487 | 476 | ||
488 | static void __exit wl1271_exit(void) | 477 | static void __exit wl1271_exit(void) |
489 | { | 478 | { |
490 | spi_unregister_driver(&wl1271_spi_driver); | 479 | spi_unregister_driver(&wl1271_spi_driver); |
491 | |||
492 | wl1271_notice("unloaded"); | ||
493 | } | 480 | } |
494 | 481 | ||
495 | module_init(wl1271_init); | 482 | module_init(wl1271_init); |
diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c index da351d7cd1f2..5d5e1ef87206 100644 --- a/drivers/net/wireless/wl12xx/testmode.c +++ b/drivers/net/wireless/wl12xx/testmode.c | |||
@@ -260,7 +260,7 @@ static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[]) | |||
260 | { | 260 | { |
261 | wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover"); | 261 | wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover"); |
262 | 262 | ||
263 | ieee80211_queue_work(wl->hw, &wl->recovery_work); | 263 | wl12xx_queue_recovery_work(wl); |
264 | 264 | ||
265 | return 0; | 265 | return 0; |
266 | } | 266 | } |
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index ca3ab1c1acef..200590c0d9e3 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c | |||
@@ -562,17 +562,29 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) | |||
562 | spin_unlock_irqrestore(&wl->wl_lock, flags); | 562 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
563 | } | 563 | } |
564 | 564 | ||
565 | static bool wl1271_tx_is_data_present(struct sk_buff *skb) | ||
566 | { | ||
567 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); | ||
568 | |||
569 | return ieee80211_is_data_present(hdr->frame_control); | ||
570 | } | ||
571 | |||
565 | void wl1271_tx_work_locked(struct wl1271 *wl) | 572 | void wl1271_tx_work_locked(struct wl1271 *wl) |
566 | { | 573 | { |
567 | struct sk_buff *skb; | 574 | struct sk_buff *skb; |
568 | u32 buf_offset = 0; | 575 | u32 buf_offset = 0; |
569 | bool sent_packets = false; | 576 | bool sent_packets = false; |
577 | bool had_data = false; | ||
578 | bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); | ||
570 | int ret; | 579 | int ret; |
571 | 580 | ||
572 | if (unlikely(wl->state == WL1271_STATE_OFF)) | 581 | if (unlikely(wl->state == WL1271_STATE_OFF)) |
573 | return; | 582 | return; |
574 | 583 | ||
575 | while ((skb = wl1271_skb_dequeue(wl))) { | 584 | while ((skb = wl1271_skb_dequeue(wl))) { |
585 | if (wl1271_tx_is_data_present(skb)) | ||
586 | had_data = true; | ||
587 | |||
576 | ret = wl1271_prepare_tx_frame(wl, skb, buf_offset); | 588 | ret = wl1271_prepare_tx_frame(wl, skb, buf_offset); |
577 | if (ret == -EAGAIN) { | 589 | if (ret == -EAGAIN) { |
578 | /* | 590 | /* |
@@ -619,6 +631,19 @@ out_ack: | |||
619 | 631 | ||
620 | wl1271_handle_tx_low_watermark(wl); | 632 | wl1271_handle_tx_low_watermark(wl); |
621 | } | 633 | } |
634 | if (!is_ap && wl->conf.rx_streaming.interval && had_data && | ||
635 | (wl->conf.rx_streaming.always || | ||
636 | test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) { | ||
637 | u32 timeout = wl->conf.rx_streaming.duration; | ||
638 | |||
639 | /* enable rx streaming */ | ||
640 | if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags)) | ||
641 | ieee80211_queue_work(wl->hw, | ||
642 | &wl->rx_streaming_enable_work); | ||
643 | |||
644 | mod_timer(&wl->rx_streaming_timer, | ||
645 | jiffies + msecs_to_jiffies(timeout)); | ||
646 | } | ||
622 | } | 647 | } |
623 | 648 | ||
624 | void wl1271_tx_work(struct work_struct *work) | 649 | void wl1271_tx_work(struct work_struct *work) |
@@ -702,7 +727,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, | |||
702 | 727 | ||
703 | /* return the packet to the stack */ | 728 | /* return the packet to the stack */ |
704 | skb_queue_tail(&wl->deferred_tx_queue, skb); | 729 | skb_queue_tail(&wl->deferred_tx_queue, skb); |
705 | ieee80211_queue_work(wl->hw, &wl->netstack_work); | 730 | queue_work(wl->freezable_wq, &wl->netstack_work); |
706 | wl1271_free_tx_id(wl, result->id); | 731 | wl1271_free_tx_id(wl, result->id); |
707 | } | 732 | } |
708 | 733 | ||
@@ -757,7 +782,7 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) | |||
757 | info = IEEE80211_SKB_CB(skb); | 782 | info = IEEE80211_SKB_CB(skb); |
758 | info->status.rates[0].idx = -1; | 783 | info->status.rates[0].idx = -1; |
759 | info->status.rates[0].count = 0; | 784 | info->status.rates[0].count = 0; |
760 | ieee80211_tx_status(wl->hw, skb); | 785 | ieee80211_tx_status_ni(wl->hw, skb); |
761 | total++; | 786 | total++; |
762 | } | 787 | } |
763 | } | 788 | } |
@@ -795,7 +820,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues) | |||
795 | info = IEEE80211_SKB_CB(skb); | 820 | info = IEEE80211_SKB_CB(skb); |
796 | info->status.rates[0].idx = -1; | 821 | info->status.rates[0].idx = -1; |
797 | info->status.rates[0].count = 0; | 822 | info->status.rates[0].count = 0; |
798 | ieee80211_tx_status(wl->hw, skb); | 823 | ieee80211_tx_status_ni(wl->hw, skb); |
799 | } | 824 | } |
800 | } | 825 | } |
801 | } | 826 | } |
@@ -838,7 +863,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues) | |||
838 | info->status.rates[0].idx = -1; | 863 | info->status.rates[0].idx = -1; |
839 | info->status.rates[0].count = 0; | 864 | info->status.rates[0].count = 0; |
840 | 865 | ||
841 | ieee80211_tx_status(wl->hw, skb); | 866 | ieee80211_tx_status_ni(wl->hw, skb); |
842 | } | 867 | } |
843 | } | 868 | } |
844 | } | 869 | } |
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 3bc794a1ee75..d7db6e77047a 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h | |||
@@ -226,6 +226,8 @@ enum { | |||
226 | #define FW_VER_MINOR_1_SPARE_STA_MIN 58 | 226 | #define FW_VER_MINOR_1_SPARE_STA_MIN 58 |
227 | #define FW_VER_MINOR_1_SPARE_AP_MIN 47 | 227 | #define FW_VER_MINOR_1_SPARE_AP_MIN 47 |
228 | 228 | ||
229 | #define FW_VER_MINOR_FWLOG_STA_MIN 70 | ||
230 | |||
229 | struct wl1271_chip { | 231 | struct wl1271_chip { |
230 | u32 id; | 232 | u32 id; |
231 | char fw_ver_str[ETHTOOL_BUSINFO_LEN]; | 233 | char fw_ver_str[ETHTOOL_BUSINFO_LEN]; |
@@ -284,8 +286,7 @@ struct wl1271_fw_sta_status { | |||
284 | u8 tx_total; | 286 | u8 tx_total; |
285 | u8 reserved1; | 287 | u8 reserved1; |
286 | __le16 reserved2; | 288 | __le16 reserved2; |
287 | /* Total structure size is 68 bytes */ | 289 | __le32 log_start_addr; |
288 | u32 padding; | ||
289 | } __packed; | 290 | } __packed; |
290 | 291 | ||
291 | struct wl1271_fw_full_status { | 292 | struct wl1271_fw_full_status { |
@@ -359,6 +360,9 @@ enum wl12xx_flags { | |||
359 | WL1271_FLAG_DUMMY_PACKET_PENDING, | 360 | WL1271_FLAG_DUMMY_PACKET_PENDING, |
360 | WL1271_FLAG_SUSPENDED, | 361 | WL1271_FLAG_SUSPENDED, |
361 | WL1271_FLAG_PENDING_WORK, | 362 | WL1271_FLAG_PENDING_WORK, |
363 | WL1271_FLAG_SOFT_GEMINI, | ||
364 | WL1271_FLAG_RX_STREAMING_STARTED, | ||
365 | WL1271_FLAG_RECOVERY_IN_PROGRESS, | ||
362 | }; | 366 | }; |
363 | 367 | ||
364 | struct wl1271_link { | 368 | struct wl1271_link { |
@@ -443,6 +447,7 @@ struct wl1271 { | |||
443 | struct sk_buff_head deferred_tx_queue; | 447 | struct sk_buff_head deferred_tx_queue; |
444 | 448 | ||
445 | struct work_struct tx_work; | 449 | struct work_struct tx_work; |
450 | struct workqueue_struct *freezable_wq; | ||
446 | 451 | ||
447 | /* Pending TX frames */ | 452 | /* Pending TX frames */ |
448 | unsigned long tx_frames_map[BITS_TO_LONGS(ACX_TX_DESCRIPTORS)]; | 453 | unsigned long tx_frames_map[BITS_TO_LONGS(ACX_TX_DESCRIPTORS)]; |
@@ -468,6 +473,15 @@ struct wl1271 { | |||
468 | /* Network stack work */ | 473 | /* Network stack work */ |
469 | struct work_struct netstack_work; | 474 | struct work_struct netstack_work; |
470 | 475 | ||
476 | /* FW log buffer */ | ||
477 | u8 *fwlog; | ||
478 | |||
479 | /* Number of valid bytes in the FW log buffer */ | ||
480 | ssize_t fwlog_size; | ||
481 | |||
482 | /* Sysfs FW log entry readers wait queue */ | ||
483 | wait_queue_head_t fwlog_waitq; | ||
484 | |||
471 | /* Hardware recovery work */ | 485 | /* Hardware recovery work */ |
472 | struct work_struct recovery_work; | 486 | struct work_struct recovery_work; |
473 | 487 | ||
@@ -508,6 +522,11 @@ struct wl1271 { | |||
508 | /* Default key (for WEP) */ | 522 | /* Default key (for WEP) */ |
509 | u32 default_key; | 523 | u32 default_key; |
510 | 524 | ||
525 | /* Rx Streaming */ | ||
526 | struct work_struct rx_streaming_enable_work; | ||
527 | struct work_struct rx_streaming_disable_work; | ||
528 | struct timer_list rx_streaming_timer; | ||
529 | |||
511 | unsigned int filters; | 530 | unsigned int filters; |
512 | unsigned int rx_config; | 531 | unsigned int rx_config; |
513 | unsigned int rx_filter; | 532 | unsigned int rx_filter; |
@@ -573,6 +592,7 @@ struct wl1271 { | |||
573 | * (currently, only "ANY" trigger is supported) | 592 | * (currently, only "ANY" trigger is supported) |
574 | */ | 593 | */ |
575 | bool wow_enabled; | 594 | bool wow_enabled; |
595 | bool irq_wake_enabled; | ||
576 | 596 | ||
577 | /* | 597 | /* |
578 | * AP-mode - links indexed by HLID. The global and broadcast links | 598 | * AP-mode - links indexed by HLID. The global and broadcast links |
@@ -602,6 +622,9 @@ struct wl1271_station { | |||
602 | 622 | ||
603 | int wl1271_plt_start(struct wl1271 *wl); | 623 | int wl1271_plt_start(struct wl1271 *wl); |
604 | int wl1271_plt_stop(struct wl1271 *wl); | 624 | int wl1271_plt_stop(struct wl1271 *wl); |
625 | int wl1271_recalc_rx_streaming(struct wl1271 *wl); | ||
626 | void wl12xx_queue_recovery_work(struct wl1271 *wl); | ||
627 | size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen); | ||
605 | 628 | ||
606 | #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ | 629 | #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ |
607 | 630 | ||
@@ -637,4 +660,15 @@ int wl1271_plt_stop(struct wl1271 *wl); | |||
637 | /* WL128X requires aggregated packets to be aligned to the SDIO block size */ | 660 | /* WL128X requires aggregated packets to be aligned to the SDIO block size */ |
638 | #define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT BIT(2) | 661 | #define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT BIT(2) |
639 | 662 | ||
663 | /* | ||
664 | * WL127X AP mode requires Low Power DRPw (LPD) enable to reduce power | ||
665 | * consumption | ||
666 | */ | ||
667 | #define WL12XX_QUIRK_LPD_MODE BIT(3) | ||
668 | |||
669 | /* Older firmwares did not implement the FW logger over bus feature */ | ||
670 | #define WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED BIT(4) | ||
671 | |||
672 | #define WL12XX_HW_BLOCK_SIZE 256 | ||
673 | |||
640 | #endif | 674 | #endif |