Commit d8c42c0c authored by Juuso Oikarinen's avatar Juuso Oikarinen Committed by John W. Linville

wl1271: Fix PSM entry

Currently the PSM entry function assumes successful operation, and enables
ELP, BET and beacon filtering right away. This is bad, because the PSM
entry may fail due to environmental issues, which will cause the ELP, BET and
beacon filtering to be illegally enabled (because FW remains in active state.)

Fix this by enabling ELP, BET and beacon filtering only after successful entry,
and by ensuring the firmware is in active mode after the failure.
Signed-off-by: default avatarJuuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: default avatarLuciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent e2117cea
...@@ -555,7 +555,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable) ...@@ -555,7 +555,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable)
return ret; return ret;
} }
int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send)
{ {
struct wl1271_cmd_ps_params *ps_params = NULL; struct wl1271_cmd_ps_params *ps_params = NULL;
int ret = 0; int ret = 0;
...@@ -576,7 +576,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) ...@@ -576,7 +576,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode)
} }
ps_params->ps_mode = ps_mode; ps_params->ps_mode = ps_mode;
ps_params->send_null_data = 1; ps_params->send_null_data = send;
ps_params->retries = 5; ps_params->retries = 5;
ps_params->hang_over_period = 128; ps_params->hang_over_period = 128;
ps_params->null_data_rate = cpu_to_le32(1); /* 1 Mbps */ ps_params->null_data_rate = cpu_to_le32(1); /* 1 Mbps */
......
...@@ -38,7 +38,7 @@ int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); ...@@ -38,7 +38,7 @@ int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode); int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send);
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
size_t len); size_t len);
int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
......
...@@ -78,24 +78,55 @@ static int wl1271_event_ps_report(struct wl1271 *wl, ...@@ -78,24 +78,55 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
switch (mbox->ps_status) { switch (mbox->ps_status) {
case EVENT_ENTER_POWER_SAVE_FAIL: case EVENT_ENTER_POWER_SAVE_FAIL:
wl1271_debug(DEBUG_PSM, "PSM entry failed");
if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) {
/* remain in active mode */
wl->psm_entry_retry = 0; wl->psm_entry_retry = 0;
break; break;
} }
if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) { if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) {
wl->psm_entry_retry++; wl->psm_entry_retry++;
ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
true);
} else { } else {
wl1271_error("PSM entry failed, giving up.\n"); wl1271_error("PSM entry failed, giving up.\n");
/* make sure the firmware goes into active mode */
ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
false);
wl->psm_entry_retry = 0; wl->psm_entry_retry = 0;
} }
break; break;
case EVENT_ENTER_POWER_SAVE_SUCCESS: case EVENT_ENTER_POWER_SAVE_SUCCESS:
wl->psm_entry_retry = 0; wl->psm_entry_retry = 0;
/* enable beacon filtering */
ret = wl1271_acx_beacon_filter_opt(wl, true);
if (ret < 0)
break;
/* enable beacon early termination */
ret = wl1271_acx_bet_enable(wl, true);
if (ret < 0)
break;
/* go to extremely low power mode */
wl1271_ps_elp_sleep(wl);
if (ret < 0)
break;
break; break;
case EVENT_EXIT_POWER_SAVE_FAIL: case EVENT_EXIT_POWER_SAVE_FAIL:
wl1271_info("PSM exit failed"); wl1271_debug(DEBUG_PSM, "PSM exit failed");
if (test_bit(WL1271_FLAG_PSM, &wl->flags)) {
wl->psm_entry_retry = 0;
break;
}
/* make sure the firmware goes to active mode */
ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
false);
break; break;
case EVENT_EXIT_POWER_SAVE_SUCCESS: case EVENT_EXIT_POWER_SAVE_SUCCESS:
default: default:
......
...@@ -1248,7 +1248,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -1248,7 +1248,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
*/ */
if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) { if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
wl1271_info("psm enabled"); wl1271_info("psm enabled");
ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
true);
} }
} else if (!(conf->flags & IEEE80211_CONF_PS) && } else if (!(conf->flags & IEEE80211_CONF_PS) &&
test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) { test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
...@@ -1257,7 +1258,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -1257,7 +1258,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags); clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
if (test_bit(WL1271_FLAG_PSM, &wl->flags)) if (test_bit(WL1271_FLAG_PSM, &wl->flags))
ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
true);
} }
if (conf->power_level != wl->power_level) { if (conf->power_level != wl->power_level) {
...@@ -1637,7 +1639,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -1637,7 +1639,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) && if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
!test_bit(WL1271_FLAG_PSM, &wl->flags)) { !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
mode = STATION_POWER_SAVE_MODE; mode = STATION_POWER_SAVE_MODE;
ret = wl1271_ps_set_mode(wl, mode); ret = wl1271_ps_set_mode(wl, mode, true);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out_sleep;
} }
......
...@@ -118,7 +118,8 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) ...@@ -118,7 +118,8 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake)
return 0; return 0;
} }
int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
bool send)
{ {
int ret; int ret;
...@@ -126,21 +127,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) ...@@ -126,21 +127,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode)
case STATION_POWER_SAVE_MODE: case STATION_POWER_SAVE_MODE:
wl1271_debug(DEBUG_PSM, "entering psm"); wl1271_debug(DEBUG_PSM, "entering psm");
/* enable beacon filtering */ ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE, send);
ret = wl1271_acx_beacon_filter_opt(wl, true);
if (ret < 0)
return ret;
/* enable beacon early termination */
ret = wl1271_acx_bet_enable(wl, true);
if (ret < 0)
return ret;
ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE);
if (ret < 0)
return ret;
wl1271_ps_elp_sleep(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -163,7 +150,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) ...@@ -163,7 +150,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE); ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE, send);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -27,7 +27,8 @@ ...@@ -27,7 +27,8 @@
#include "wl1271.h" #include "wl1271.h"
#include "wl1271_acx.h" #include "wl1271_acx.h"
int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode); int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
bool send);
void wl1271_ps_elp_sleep(struct wl1271 *wl); void wl1271_ps_elp_sleep(struct wl1271 *wl);
int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake); int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake);
void wl1271_elp_work(struct work_struct *work); void wl1271_elp_work(struct work_struct *work);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment