From dfae7cd8052081dc6688a95af76f3cf1dd109288 Mon Sep 17 00:00:00 2001 From: Yuri Ershov Date: Wed, 27 Oct 2010 17:21:09 +0400 Subject: [PATCH] wl1251: Handle failing PS entry It is possible, that the nullfunc message used to indicate to the associated-to AP that we are going to power save fails to reach the intended recipient. In that case, the STA and AP can become out of sync, resulting in connection loss. This patch adds code to attempt to recover from such failure by resending the nullfunc. Also fixed one compiler warning. Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Yuri Ershov --- drivers/net/wireless/wl12xx/wl1251.h | 2 ++ drivers/net/wireless/wl12xx/wl1251_boot.c | 3 +- drivers/net/wireless/wl12xx/wl1251_event.c | 39 ++++++++++++++++++++++ drivers/net/wireless/wl12xx/wl1251_event.h | 7 ++++ drivers/net/wireless/wl12xx/wl1251_main.c | 2 ++ 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h index 4a353e1..a851e7d 100644 --- a/drivers/net/wireless/wl12xx/wl1251.h +++ b/drivers/net/wireless/wl12xx/wl1251.h @@ -365,6 +365,8 @@ struct wl1251 { /* PSM mode requested */ bool psm_requested; + u8 ps_entry_retry; + u16 beacon_int; u8 dtim_period; diff --git a/drivers/net/wireless/wl12xx/wl1251_boot.c b/drivers/net/wireless/wl12xx/wl1251_boot.c index a956215..e2391f1 100644 --- a/drivers/net/wireless/wl12xx/wl1251_boot.c +++ b/drivers/net/wireless/wl12xx/wl1251_boot.c @@ -301,7 +301,8 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) ROAMING_TRIGGER_LOW_RSSI_EVENT_ID | ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID | REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID | - BT_PTA_PREDICTION_EVENT_ID | JOIN_EVENT_COMPLETE_ID; + BT_PTA_PREDICTION_EVENT_ID | JOIN_EVENT_COMPLETE_ID | + PS_REPORT_EVENT_ID; /* tmp to help debug rare issues */ wl->event_mask = EVENT_MBOX_ALL_EVENT_ID & diff --git a/drivers/net/wireless/wl12xx/wl1251_event.c b/drivers/net/wireless/wl12xx/wl1251_event.c index 689ebed..4929239 100644 --- a/drivers/net/wireless/wl12xx/wl1251_event.c +++ b/drivers/net/wireless/wl12xx/wl1251_event.c @@ -46,6 +46,38 @@ static int wl1251_event_scan_complete(struct wl1251 *wl, return 0; } +#define WL1251_PS_ENTRY_RETRIES 3 +static int wl1251_event_ps_report(struct wl1251 *wl, + struct event_mailbox *mbox) +{ + int ret = 0; + + wl1251_debug(DEBUG_EVENT, "ps status: %x", mbox->ps_status); + + switch (mbox->ps_status) { + case ENTER_POWER_SAVE_FAIL: + if (!wl->psm) { + wl->ps_entry_retry = 0; + break; + } + + if (wl->ps_entry_retry < WL1251_PS_ENTRY_RETRIES) { + ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + wl->ps_entry_retry++; + } else { + wl1251_error("Power save entry failed, giving up"); + wl->ps_entry_retry = 0; + } + break; + case ENTER_POWER_SAVE_SUCCESS: + default: + wl->ps_entry_retry = 0; + break; + } + + return 0; +} + static void wl1251_event_mbox_dump(struct event_mailbox *mbox) { wl1251_debug(DEBUG_EVENT, "MBOX DUMP:"); @@ -79,6 +111,13 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) } } + if (vector & PS_REPORT_EVENT_ID) { + wl1251_debug(DEBUG_EVENT, "PS_REPORT_EVENT_ID"); + ret = wl1251_event_ps_report(wl, mbox); + if (ret < 0) + return ret; + } + if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) { wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT"); /* need to unlock mutex to avoid deadlocking with rtnl */ diff --git a/drivers/net/wireless/wl12xx/wl1251_event.h b/drivers/net/wireless/wl12xx/wl1251_event.h index ec45647..e2ce0b1 100644 --- a/drivers/net/wireless/wl12xx/wl1251_event.h +++ b/drivers/net/wireless/wl12xx/wl1251_event.h @@ -75,6 +75,13 @@ enum { EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, }; +enum { + ENTER_POWER_SAVE_FAIL = 0, + ENTER_POWER_SAVE_SUCCESS, + EXIT_POWER_SAVE_FAIL, + EXIT_POWER_SAVE_SUCCESS +}; + struct event_debug_report { u8 debug_event_id; u8 num_params; diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 049f6c8..2f7c65c 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -503,6 +503,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) wl->next_tx_complete = 0; wl->elp = false; wl->psm = 0; + wl->ps_entry_retry = 0; wl->tx_queue_stopped = false; wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->channel = WL1251_DEFAULT_CHANNEL; @@ -1357,6 +1358,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) wl->elp = false; wl->psm = 0; wl->psm_requested = false; + wl->ps_entry_retry = 0; wl->tx_queue_stopped = false; wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->beacon_int = WL1251_DEFAULT_BEACON_INT; -- 2.39.5