From d6bf9ada92c113e56151b6a993b9b9d5d03f1365 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 31 Jan 2012 11:57:20 +0200 Subject: wl12xx: remove PS management code Removal of PS management code from the driver as PS is handled by the FW (dynamic PS) Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/net/wireless/wl12xx/conf.h') diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 1bcfb017058d..47cf80f0b036 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -867,13 +867,6 @@ struct conf_conn_settings { */ u8 ps_poll_threshold; - /* - * PS Poll failure recovery ACTIVE period length - * - * Range: u32 (ms) - */ - u32 ps_poll_recovery_period; - /* * Configuration of signal average weights. */ -- cgit v1.2.3 From f1d63a59635feef3481ed1972a883a5d6be7f9bb Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 31 Jan 2012 11:57:21 +0200 Subject: wl12xx: add support for HW dynamic PS FW now supports dynamic PS so we don't need to use mac80211 support. FW will go to PSM after a specified timeout with no Rx/Tx traffic. - Changed FW API to include new PS mode (AUTO_MODE) and including timeout parameter - The default PS mode would be dynamic PS - Default timeout is 100ms (same as it used to be in mac80211) - Avoid using mac80211 APIs to disable/enable dynamic PS as we're not using mac80211 PS control anymore. - COEX is handled by the FW while in dynamic PS so removed handling of SOFT_GEMINI Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 3 ++- drivers/net/wireless/wl12xx/cmd.h | 5 +++-- drivers/net/wireless/wl12xx/conf.h | 6 ++++++ drivers/net/wireless/wl12xx/event.c | 8 -------- drivers/net/wireless/wl12xx/main.c | 15 +++++---------- drivers/net/wireless/wl12xx/ps.c | 15 ++++++++++----- 6 files changed, 26 insertions(+), 26 deletions(-) (limited to 'drivers/net/wireless/wl12xx/conf.h') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 88e94c5938a4..bf3c37bd1f12 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -996,7 +996,7 @@ out: } int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, - u8 ps_mode) + u8 ps_mode, u16 auto_ps_timeout) { struct wl1271_cmd_ps_params *ps_params = NULL; int ret = 0; @@ -1011,6 +1011,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, ps_params->role_id = wlvif->role_id; ps_params->ps_mode = ps_mode; + ps_params->auto_ps_timeout = auto_ps_timeout; ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params, sizeof(*ps_params), 0); diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index edd240db0dcc..83a2a2edb261 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -51,7 +51,7 @@ 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_data_path(struct wl1271 *wl, bool enable); int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, - u8 ps_mode); + u8 ps_mode, u16 auto_ps_timeout); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, @@ -400,6 +400,7 @@ struct wl1271_tim { } __packed; enum wl1271_cmd_ps_mode { + STATION_AUTO_PS_MODE, /* Dynamic Power Save */ STATION_ACTIVE_MODE, STATION_POWER_SAVE_MODE }; @@ -409,7 +410,7 @@ struct wl1271_cmd_ps_params { u8 role_id; u8 ps_mode; /* STATION_* */ - u8 padding[2]; + u16 auto_ps_timeout; } __packed; /* HW encryption keys */ diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 47cf80f0b036..10e5e3df4b95 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -914,6 +914,12 @@ struct conf_conn_settings { */ u8 psm_entry_nullfunc_retries; + /* + * Specifies the dynamic PS timeout in ms that will be used + * by the FW when in AUTO_PS mode + */ + u16 dynamic_ps_timeout; + /* * * Specifies the interval of the connection keep-alive null-func diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 05cd2ce927c6..c953717f38eb 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -78,21 +78,13 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, u8 enable) { - struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; if (enable) { - /* disable dynamic PS when requested by the firmware */ - wl12xx_for_each_wlvif_sta(wl, wlvif) { - vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_disable_dyn_ps(vif); - } set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); } else { clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); wl12xx_for_each_wlvif_sta(wl, wlvif) { - vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_enable_dyn_ps(vif); wl1271_recalc_rx_streaming(wl, wlvif); } } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 74d4abb25ab1..6b240868ac75 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1,3 +1,4 @@ + /* * This file is part of wl1271 * @@ -240,6 +241,7 @@ static struct conf_drv_settings default_conf = { .psm_entry_retries = 8, .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, + .dynamic_ps_timeout = 100, .keep_alive_interval = 55000, .max_listen_interval = 20, }, @@ -2142,10 +2144,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, wl1271_info("down"); - /* enable dyn ps just in case (if left on due to fw crash etc) */ - if (wlvif->bss_type == BSS_TYPE_STA_BSS) - ieee80211_enable_dyn_ps(vif); - if (wl->scan.state != WL1271_SCAN_STATE_IDLE && wl->scan_vif == vif) { wl->scan.state = WL1271_SCAN_STATE_IDLE; @@ -3694,9 +3692,6 @@ sta_not_found: dev_kfree_skb(wlvif->probereq); wlvif->probereq = NULL; - /* re-enable dynamic ps - just in case */ - ieee80211_enable_dyn_ps(vif); - /* revert back to minimum rates for the current band */ wl1271_set_band_rate(wl, wlvif); wlvif->basic_rate = @@ -3827,10 +3822,9 @@ sta_not_found: /* If we want to go in PSM but we're not there yet */ if (test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags) && !test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) { - enum wl1271_cmd_ps_mode mode; - mode = STATION_POWER_SAVE_MODE; - ret = wl1271_ps_set_mode(wl, wlvif, mode, + ret = wl1271_ps_set_mode(wl, wlvif, + STATION_AUTO_PS_MODE, wlvif->basic_rate, true); if (ret < 0) @@ -4976,6 +4970,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index a2bdacdd7e1d..60f03c4dfbe7 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -163,10 +163,12 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum wl1271_cmd_ps_mode mode, u32 rates, bool send) { int ret; + u16 timeout = wl->conf.conn.dynamic_ps_timeout; switch (mode) { - case STATION_POWER_SAVE_MODE: - wl1271_debug(DEBUG_PSM, "entering psm"); + case STATION_AUTO_PS_MODE: + wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)", + mode, timeout); ret = wl1271_acx_wake_up_conditions(wl, wlvif); if (ret < 0) { @@ -174,14 +176,13 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, return ret; } - ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_POWER_SAVE_MODE); + ret = wl1271_cmd_ps_mode(wl, wlvif, mode, timeout); if (ret < 0) return ret; set_bit(WLVIF_FLAG_PSM, &wlvif->flags); break; case STATION_ACTIVE_MODE: - default: wl1271_debug(DEBUG_PSM, "leaving psm"); /* disable beacon early termination */ @@ -191,12 +192,16 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, return ret; } - ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_ACTIVE_MODE); + ret = wl1271_cmd_ps_mode(wl, wlvif, mode, 0); if (ret < 0) return ret; clear_bit(WLVIF_FLAG_PSM, &wlvif->flags); break; + case STATION_POWER_SAVE_MODE: + default: + wl1271_warning("trying to set ps to unsupported mode %d", mode); + ret = -EINVAL; } return ret; -- cgit v1.2.3 From d2e2d769e3d328ba7cbf08e8ed85e3f817915843 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Tue, 31 Jan 2012 17:54:40 +0200 Subject: wl12xx: Set IEEE80211_TX_RC_MCS on MCS rates on TX complete. IEEE80211_TX_RC_MCS was not set correctly leading to incorrect link speed calculation. Signed-off-by: Pontus Fuchs Signed-off-by: Ido Reis Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 4 ++++ drivers/net/wireless/wl12xx/tx.c | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx/conf.h') diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 10e5e3df4b95..209f05312909 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -91,6 +91,10 @@ enum { CONF_HW_RXTX_RATE_UNSUPPORTED = 0xff }; +/* Rates between and including these are MCS rates */ +#define CONF_HW_RXTX_RATE_MCS_MIN CONF_HW_RXTX_RATE_MCS7 +#define CONF_HW_RXTX_RATE_MCS_MAX CONF_HW_RXTX_RATE_MCS0 + enum { CONF_SG_DISABLE = 0, CONF_SG_PROTECTIVE, diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 2bf43b86a3f9..f95112f6769f 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -764,6 +764,14 @@ out: mutex_unlock(&wl->mutex); } +static u8 wl1271_tx_get_rate_flags(u8 rate_class_index) +{ + if (rate_class_index >= CONF_HW_RXTX_RATE_MCS_MIN && + rate_class_index <= CONF_HW_RXTX_RATE_MCS_MAX) + return IEEE80211_TX_RC_MCS; + return 0; +} + static void wl1271_tx_complete_packet(struct wl1271 *wl, struct wl1271_tx_hw_res_descr *result) { @@ -773,6 +781,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, struct sk_buff *skb; int id = result->id; int rate = -1; + u8 rate_flags = 0; u8 retries = 0; /* check for id legality */ @@ -799,6 +808,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, info->flags |= IEEE80211_TX_STAT_ACK; rate = wl1271_rate_to_idx(result->rate_class_index, wlvif->band); + rate_flags = wl1271_tx_get_rate_flags(result->rate_class_index); retries = result->ack_failures; } else if (result->status == TX_RETRY_EXCEEDED) { wl->stats.excessive_retries++; @@ -807,7 +817,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, info->status.rates[0].idx = rate; info->status.rates[0].count = retries; - info->status.rates[0].flags = 0; + info->status.rates[0].flags = rate_flags; info->status.ack_signal = -1; wl->stats.retry_count += result->ack_failures; -- cgit v1.2.3 From defe02c720d54fc9bda3e5e625be70adac347a61 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Tue, 31 Jan 2012 17:54:41 +0200 Subject: wl12xx: Set IEEE80211_TX_RC_SHORT_GI if short GI was used on New FW reports usage of short GI as a rate class index. Check for this rate and set the IEEE80211_TX_RC_SHORT_GI if used. Signed-off-by: Pontus Fuchs Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 5 +++-- drivers/net/wireless/wl12xx/main.c | 2 ++ drivers/net/wireless/wl12xx/tx.c | 8 ++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/wl12xx/conf.h') diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 209f05312909..1147396bf7eb 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -66,7 +66,8 @@ enum { }; enum { - CONF_HW_RXTX_RATE_MCS7 = 0, + CONF_HW_RXTX_RATE_MCS7_SGI = 0, + CONF_HW_RXTX_RATE_MCS7, CONF_HW_RXTX_RATE_MCS6, CONF_HW_RXTX_RATE_MCS5, CONF_HW_RXTX_RATE_MCS4, @@ -92,7 +93,7 @@ enum { }; /* Rates between and including these are MCS rates */ -#define CONF_HW_RXTX_RATE_MCS_MIN CONF_HW_RXTX_RATE_MCS7 +#define CONF_HW_RXTX_RATE_MCS_MIN CONF_HW_RXTX_RATE_MCS7_SGI #define CONF_HW_RXTX_RATE_MCS_MAX CONF_HW_RXTX_RATE_MCS0 enum { diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 4ca7278201ec..fa2bf307aa0d 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -4406,6 +4406,7 @@ static struct ieee80211_channel wl1271_channels[] = { /* mapping to indexes for wl1271_rates */ static const u8 wl1271_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ + 7, /* CONF_HW_RXTX_RATE_MCS7_SGI */ 7, /* CONF_HW_RXTX_RATE_MCS7 */ 6, /* CONF_HW_RXTX_RATE_MCS6 */ 5, /* CONF_HW_RXTX_RATE_MCS5 */ @@ -4527,6 +4528,7 @@ static struct ieee80211_channel wl1271_channels_5ghz[] = { /* mapping to indexes for wl1271_rates_5ghz */ static const u8 wl1271_rate_to_idx_5ghz[] = { /* MCS rates are used only with 11n */ + 7, /* CONF_HW_RXTX_RATE_MCS7_SGI */ 7, /* CONF_HW_RXTX_RATE_MCS7 */ 6, /* CONF_HW_RXTX_RATE_MCS6 */ 5, /* CONF_HW_RXTX_RATE_MCS5 */ diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index f95112f6769f..ee313e7ce8fe 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -766,10 +766,14 @@ out: static u8 wl1271_tx_get_rate_flags(u8 rate_class_index) { + u8 flags = 0; + if (rate_class_index >= CONF_HW_RXTX_RATE_MCS_MIN && rate_class_index <= CONF_HW_RXTX_RATE_MCS_MAX) - return IEEE80211_TX_RC_MCS; - return 0; + flags |= IEEE80211_TX_RC_MCS; + if (rate_class_index == CONF_HW_RXTX_RATE_MCS7_SGI) + flags |= IEEE80211_TX_RC_SHORT_GI; + return flags; } static void wl1271_tx_complete_packet(struct wl1271 *wl, -- cgit v1.2.3 From 26612c435b34bd99fdf79f5c1d8826ebd0ea86f3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 17:54:43 +0200 Subject: wl12xx: add new coex params new params were added to the coex params. Add them with default value of 0. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 4 ++++ drivers/net/wireless/wl12xx/main.c | 3 +++ 2 files changed, 7 insertions(+) (limited to 'drivers/net/wireless/wl12xx/conf.h') diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 1147396bf7eb..18637eec1e21 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -317,6 +317,10 @@ enum { CONF_AP_BT_ACL_VAL_BT_SERVE_TIME, CONF_AP_BT_ACL_VAL_WL_SERVE_TIME, + /* CTS Diluting params */ + CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH, + CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER, + CONF_SG_TEMP_PARAM_1, CONF_SG_TEMP_PARAM_2, CONF_SG_TEMP_PARAM_3, diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index fa2bf307aa0d..aa5d512c8f82 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -116,6 +116,9 @@ static struct conf_drv_settings default_conf = { [CONF_AP_CONNECTION_PROTECTION_TIME] = 0, [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25, [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25, + /* CTS Diluting params */ + [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, + [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, }, .state = CONF_SG_PROTECTIVE, }, -- cgit v1.2.3 From dae728fe67c7c5ab10f91366bd9d1f8db203e123 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 2 Feb 2012 12:03:39 +0200 Subject: wl12xx: Set different wake up conditions in case of suspend Added ability to set different wake up conditions for suspend/resume. Set default values to wake up every 3 DTIMs while suspended and every 1 DTIM while resumed Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 10 +++++--- drivers/net/wireless/wl12xx/acx.h | 3 ++- drivers/net/wireless/wl12xx/conf.h | 13 ++++++++++ drivers/net/wireless/wl12xx/main.c | 51 +++++++++++++++++++++++++++++++++++--- drivers/net/wireless/wl12xx/ps.c | 4 ++- 5 files changed, 72 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/wl12xx/conf.h') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index af2c3123d1d7..bc96db0683a5 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -34,12 +34,14 @@ #include "reg.h" #include "ps.h" -int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif) +int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 wake_up_event, u8 listen_interval) { struct acx_wake_up_condition *wake_up; int ret; - wl1271_debug(DEBUG_ACX, "acx wake up conditions"); + wl1271_debug(DEBUG_ACX, "acx wake up conditions (wake_up_event %d listen_interval %d)", + wake_up_event, listen_interval); wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL); if (!wake_up) { @@ -48,8 +50,8 @@ int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif) } wake_up->role_id = wlvif->role_id; - wake_up->wake_up_event = wl->conf.conn.wake_up_event; - wake_up->listen_interval = wl->conf.conn.listen_interval; + wake_up->wake_up_event = wake_up_event; + wake_up->listen_interval = listen_interval; ret = wl1271_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS, wake_up, sizeof(*wake_up)); diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 0749df58c85d..a28fc044034c 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1226,7 +1226,8 @@ enum { int wl1271_acx_wake_up_conditions(struct wl1271 *wl, - struct wl12xx_vif *wlvif); + struct wl12xx_vif *wlvif, + u8 wake_up_event, u8 listen_interval); int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth); int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif, int power); diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 18637eec1e21..80eafadc389d 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -818,6 +818,19 @@ struct conf_conn_settings { */ u8 listen_interval; + /* + * Firmware wakeup conditions during suspend + * Range: CONF_WAKE_UP_EVENT_* + */ + u8 suspend_wake_up_event; + + /* + * Listen interval during suspend. + * Currently will be in DTIMs (1-10) + * + */ + u8 suspend_listen_interval; + /* * Enable or disable the beacon filtering. * diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index aa5d512c8f82..fa61dfd2084c 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -221,6 +221,8 @@ static struct conf_drv_settings default_conf = { .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .listen_interval = 1, + .suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM, + .suspend_listen_interval = 3, .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, .bcn_filt_ie_count = 2, .bcn_filt_ie = { @@ -1574,6 +1576,35 @@ static struct notifier_block wl1271_dev_notifier = { }; #ifdef CONFIG_PM +static int wl1271_configure_suspend_sta(struct wl1271 *wl, + struct wl12xx_vif *wlvif) +{ + int ret = 0; + + mutex_lock(&wl->mutex); + + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + goto out_unlock; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + + ret = wl1271_acx_wake_up_conditions(wl, wlvif, + wl->conf.conn.suspend_wake_up_event, + wl->conf.conn.suspend_listen_interval); + + if (ret < 0) + wl1271_error("suspend: set wake up conditions failed: %d", ret); + + + wl1271_ps_elp_sleep(wl); + +out_unlock: + mutex_unlock(&wl->mutex); + return ret; + +} static int wl1271_configure_suspend_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) @@ -1601,6 +1632,8 @@ out_unlock: static int wl1271_configure_suspend(struct wl1271 *wl, struct wl12xx_vif *wlvif) { + if (wlvif->bss_type == BSS_TYPE_STA_BSS) + return wl1271_configure_suspend_sta(wl, wlvif); if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl1271_configure_suspend_ap(wl, wlvif); return 0; @@ -1609,10 +1642,11 @@ static int wl1271_configure_suspend(struct wl1271 *wl, static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) { - int ret; + int ret = 0; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; + bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS; - if (!is_ap) + if ((!is_ap) && (!is_sta)) return; mutex_lock(&wl->mutex); @@ -1620,7 +1654,18 @@ static void wl1271_configure_resume(struct wl1271 *wl, if (ret < 0) goto out; - wl1271_acx_beacon_filter_opt(wl, wlvif, false); + if (is_sta) { + ret = wl1271_acx_wake_up_conditions(wl, wlvif, + wl->conf.conn.wake_up_event, + wl->conf.conn.listen_interval); + + if (ret < 0) + wl1271_error("resume: wake up conditions failed: %d", + ret); + + } else if (is_ap) { + ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); + } wl1271_ps_elp_sleep(wl); out: diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index e209e29ffb45..d1979223ad28 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -170,7 +170,9 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)", mode, timeout); - ret = wl1271_acx_wake_up_conditions(wl, wlvif); + ret = wl1271_acx_wake_up_conditions(wl, wlvif, + wl->conf.conn.wake_up_event, + wl->conf.conn.listen_interval); if (ret < 0) { wl1271_error("couldn't set wake up conditions"); return ret; -- cgit v1.2.3 From 5c0dc2fcfec606cf9f2d28ff31bbeb0a6225b27a Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 2 Feb 2012 19:06:45 +0200 Subject: wl12xx: add forced_ps mode For certain WiFi certification tests forcing PS is necessary. Since DPS is now enabled in the FW and this can't be achieved by using netlatency this required a new config option. Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 6 ++++++ drivers/net/wireless/wl12xx/debugfs.c | 2 +- drivers/net/wireless/wl12xx/main.c | 25 +++++++++++++++++++------ drivers/net/wireless/wl12xx/ps.c | 10 +++++----- drivers/net/wireless/wl12xx/wl12xx.h | 2 +- 5 files changed, 32 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/wl12xx/conf.h') diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 80eafadc389d..823535cdf150 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -942,6 +942,12 @@ struct conf_conn_settings { */ u16 dynamic_ps_timeout; + /* + * Specifies whether dynamic PS should be disabled and PSM forced. + * This is required for certain WiFi certification tests. + */ + u8 forced_ps; + /* * * Specifies the interval of the connection keep-alive null-func diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 15353fac0707..02da445ea98a 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -358,7 +358,7 @@ static ssize_t dynamic_ps_timeout_write(struct file *file, */ wl12xx_for_each_wlvif_sta(wl, wlvif) { - if (test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags)) + if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE); } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index fa61dfd2084c..b828149a1655 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -247,6 +247,7 @@ static struct conf_drv_settings default_conf = { .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, .dynamic_ps_timeout = 100, + .forced_ps = false, .keep_alive_interval = 55000, .max_listen_interval = 20, }, @@ -2510,17 +2511,29 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, if ((conf->flags & IEEE80211_CONF_PS) && test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && - !test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags)) { + !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { - wl1271_debug(DEBUG_PSM, "auto ps enabled"); + int ps_mode; + char *ps_mode_str; + + if (wl->conf.conn.forced_ps) { + ps_mode = STATION_POWER_SAVE_MODE; + ps_mode_str = "forced"; + } else { + ps_mode = STATION_AUTO_PS_MODE; + ps_mode_str = "auto"; + } + + wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str); + + ret = wl1271_ps_set_mode(wl, wlvif, ps_mode); - ret = wl1271_ps_set_mode(wl, wlvif, - STATION_AUTO_PS_MODE); if (ret < 0) - wl1271_warning("enter auto ps failed %d", ret); + wl1271_warning("enter %s ps failed %d", + ps_mode_str, ret); } else if (!(conf->flags & IEEE80211_CONF_PS) && - test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags)) { + test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { wl1271_debug(DEBUG_PSM, "auto ps disabled"); diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index d1979223ad28..23d67501c50a 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -56,7 +56,7 @@ void wl1271_elp_work(struct work_struct *work) if (wlvif->bss_type == BSS_TYPE_AP_BSS) goto out; - if (!test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags) && + if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) goto out; } @@ -84,7 +84,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) if (wlvif->bss_type == BSS_TYPE_AP_BSS) return; - if (!test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags) && + if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) return; } @@ -167,6 +167,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, switch (mode) { case STATION_AUTO_PS_MODE: + case STATION_POWER_SAVE_MODE: wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)", mode, timeout); @@ -182,7 +183,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) return ret; - set_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags); + set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); /* enable beacon early termination. Not relevant for 5GHz */ if (wlvif->band == IEEE80211_BAND_2GHZ) { @@ -205,9 +206,8 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) return ret; - clear_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags); + clear_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); break; - case STATION_POWER_SAVE_MODE: default: wl1271_warning("trying to set ps to unsupported mode %d", mode); ret = -EINVAL; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9baed6be6a31..61c58c13fa90 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -254,7 +254,7 @@ enum wl12xx_vif_flags { WLVIF_FLAG_STA_ASSOCIATED, WLVIF_FLAG_IBSS_JOINED, WLVIF_FLAG_AP_STARTED, - WLVIF_FLAG_IN_AUTO_PS, + WLVIF_FLAG_IN_PS, WLVIF_FLAG_STA_STATE_SENT, WLVIF_FLAG_RX_STREAMING_STARTED, WLVIF_FLAG_PSPOLL_FAILURE, -- cgit v1.2.3 From d647f2dd1a445f5a683372f1b47919fe92c19896 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 2 Feb 2012 13:54:28 +0200 Subject: wl12xx: use split scan for normal scan Split scan allows the FW to schedule other activities during a scan which may be a long operation. This is achieved by setting a trigger TID to ANY_TID and a scan trigger timeout other than 0. The default one is set to 50ms. Signed-off-by: Eyal Shapira Signed-off-by: Igal Chernobelsky Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 8 ++++++++ drivers/net/wireless/wl12xx/main.c | 1 + drivers/net/wireless/wl12xx/scan.c | 8 +++++--- drivers/net/wireless/wl12xx/scan.h | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/wl12xx/conf.h') diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 823535cdf150..cc50faaf03d1 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1082,6 +1082,14 @@ struct conf_scan_settings { */ u16 num_probe_reqs; + /* + * Scan trigger (split scan) timeout. The FW will split the scan + * operation into slices of the given time and allow the FW to schedule + * other tasks in between. + * + * Range: u32 Microsecs + */ + u32 split_scan_timeout; }; struct conf_sched_scan_settings { diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 253847ff344b..723df48ed447 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -272,6 +272,7 @@ static struct conf_drv_settings default_conf = { .min_dwell_time_passive = 100000, .max_dwell_time_passive = 100000, .num_probe_reqs = 2, + .split_scan_timeout = 50000, }, .sched_scan = { /* sched_scan requires dwell times in TU instead of TU/1000 */ diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index e3566ca90f0d..e43a6b2c1d91 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -172,6 +172,9 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, goto out; } + if (wl->conf.scan.split_scan_timeout) + scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN; + if (passive) scan_options |= WL1271_SCAN_OPT_PASSIVE; @@ -198,7 +201,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, cmd->params.tx_rate = cpu_to_le32(basic_rate); cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; - cmd->params.tid_trigger = 0; + cmd->params.tid_trigger = CONF_TX_AC_ANY_TID; cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; if (band == IEEE80211_BAND_2GHZ) @@ -223,8 +226,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, goto out; } - /* disable the timeout */ - trigger->timeout = 0; + trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout); ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, sizeof(*trigger), 0); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/scan.h b/drivers/net/wireless/wl12xx/scan.h index a7ed43dc08c9..96ff457a3a0b 100644 --- a/drivers/net/wireless/wl12xx/scan.h +++ b/drivers/net/wireless/wl12xx/scan.h @@ -48,7 +48,7 @@ void wl1271_scan_sched_scan_results(struct wl1271 *wl); #define WL1271_SCAN_CURRENT_TX_PWR 0 #define WL1271_SCAN_OPT_ACTIVE 0 #define WL1271_SCAN_OPT_PASSIVE 1 -#define WL1271_SCAN_OPT_TRIGGERED_SCAN 2 +#define WL1271_SCAN_OPT_SPLIT_SCAN 2 #define WL1271_SCAN_OPT_PRIORITY_HIGH 4 /* scan even if we fail to enter psm */ #define WL1271_SCAN_OPT_FORCE 8 -- cgit v1.2.3 From 55df5afb13718cda49128fa5985556df91d07765 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sat, 3 Mar 2012 22:18:00 +0200 Subject: wl12xx: implement SW Tx watchdog Track freed FW blocks during Tx. If no blocks were freed during a predefined timeout, initiate a HW recovery. This helps in situations when the FW watchdog fails. Don't trigger recovery during activities that can temporarily stop Tx. This includes: - scanning - buffering packets for sleeping stations (AP role) - ROC on any role Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 8 +++ drivers/net/wireless/wl12xx/conf.h | 3 + drivers/net/wireless/wl12xx/main.c | 110 +++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/scan.c | 6 ++ drivers/net/wireless/wl12xx/tx.c | 4 ++ drivers/net/wireless/wl12xx/tx.h | 1 + drivers/net/wireless/wl12xx/wl12xx.h | 3 + 7 files changed, 135 insertions(+) (limited to 'drivers/net/wireless/wl12xx/conf.h') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 1ef212f6440f..3414fc11e9ba 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -1818,6 +1818,14 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id) goto out; __clear_bit(role_id, wl->roc_map); + + /* + * Rearm the tx watchdog when removing the last ROC. This prevents + * recoveries due to just finished ROCs - when Tx hasn't yet had + * a chance to get out. + */ + if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) + wl12xx_rearm_tx_watchdog_locked(wl); out: return ret; } diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index cc50faaf03d1..3e581e19424c 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -690,6 +690,9 @@ struct conf_tx_settings { */ u8 tmpl_short_retry_limit; u8 tmpl_long_retry_limit; + + /* Time in ms for Tx watchdog timer to expire */ + u32 tx_watchdog_timeout; }; enum { diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 95a76a5f9eeb..39002363611e 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -217,6 +217,7 @@ static struct conf_drv_settings default_conf = { .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS, .tmpl_short_retry_limit = 10, .tmpl_long_retry_limit = 10, + .tx_watchdog_timeout = 5000, }, .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, @@ -553,6 +554,80 @@ static void wl1271_rx_streaming_timer(unsigned long data) ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work); } +/* wl->mutex must be taken */ +void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl) +{ + /* if the watchdog is not armed, don't do anything */ + if (wl->tx_allocated_blocks == 0) + return; + + cancel_delayed_work(&wl->tx_watchdog_work); + ieee80211_queue_delayed_work(wl->hw, &wl->tx_watchdog_work, + msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout)); +} + +static void wl12xx_tx_watchdog_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1271, tx_watchdog_work); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state == WL1271_STATE_OFF)) + goto out; + + /* Tx went out in the meantime - everything is ok */ + if (unlikely(wl->tx_allocated_blocks == 0)) + goto out; + + /* + * if a ROC is in progress, we might not have any Tx for a long + * time (e.g. pending Tx on the non-ROC channels) + */ + if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) { + wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to ROC", + wl->conf.tx.tx_watchdog_timeout); + wl12xx_rearm_tx_watchdog_locked(wl); + goto out; + } + + /* + * if a scan is in progress, we might not have any Tx for a long + * time + */ + if (wl->scan.state != WL1271_SCAN_STATE_IDLE) { + wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to scan", + wl->conf.tx.tx_watchdog_timeout); + wl12xx_rearm_tx_watchdog_locked(wl); + goto out; + } + + /* + * AP might cache a frame for a long time for a sleeping station, + * so rearm the timer if there's an AP interface with stations. If + * Tx is genuinely stuck we will most hopefully discover it when all + * stations are removed due to inactivity. + */ + if (wl->active_sta_count) { + wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms. AP has " + " %d stations", + wl->conf.tx.tx_watchdog_timeout, + wl->active_sta_count); + wl12xx_rearm_tx_watchdog_locked(wl); + goto out; + } + + wl1271_error("Tx stuck (in FW) for %d ms. Starting recovery", + wl->conf.tx.tx_watchdog_timeout); + wl12xx_queue_recovery_work(wl); + +out: + mutex_unlock(&wl->mutex); +} + static void wl1271_conf_init(struct wl1271 *wl) { @@ -745,6 +820,18 @@ static void wl12xx_fw_status(struct wl1271 *wl, wl->tx_allocated_blocks -= freed_blocks; + /* + * If the FW freed some blocks: + * If we still have allocated blocks - re-arm the timer, Tx is + * not stuck. Otherwise, cancel the timer (no Tx currently). + */ + if (freed_blocks) { + if (wl->tx_allocated_blocks) + wl12xx_rearm_tx_watchdog_locked(wl); + else + cancel_delayed_work(&wl->tx_watchdog_work); + } + avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks; /* @@ -1418,6 +1505,7 @@ int wl1271_plt_stop(struct wl1271 *wl) cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->recovery_work); cancel_delayed_work_sync(&wl->elp_work); + cancel_delayed_work_sync(&wl->tx_watchdog_work); mutex_lock(&wl->mutex); wl1271_power_off(wl); @@ -1789,6 +1877,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->tx_work); cancel_delayed_work_sync(&wl->elp_work); + cancel_delayed_work_sync(&wl->tx_watchdog_work); /* let's notify MAC80211 about the remaining pending TX frames */ wl12xx_tx_reset(wl, true); @@ -2218,6 +2307,12 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, if (wl->scan.state != WL1271_SCAN_STATE_IDLE && wl->scan_vif == vif) { + /* + * Rearm the tx watchdog just before idling scan. This + * prevents just-finished scans from triggering the watchdog + */ + wl12xx_rearm_tx_watchdog_locked(wl); + wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan_vif = NULL; @@ -3129,6 +3224,13 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; } + + /* + * Rearm the tx watchdog just before idling scan. This + * prevents just-finished scans from triggering the watchdog + */ + wl12xx_rearm_tx_watchdog_locked(wl); + wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan_vif = NULL; @@ -4138,6 +4240,13 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); wl12xx_free_link(wl, wlvif, &hlid); wl->active_sta_count--; + + /* + * rearm the tx watchdog when the last STA is freed - give the FW a + * chance to return STA-buffered packets before complaining. + */ + if (wl->active_sta_count == 0) + wl12xx_rearm_tx_watchdog_locked(wl); } static int wl12xx_sta_add(struct wl1271 *wl, @@ -5212,6 +5321,7 @@ static struct ieee80211_hw *wl1271_alloc_hw(void) INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->recovery_work, wl1271_recovery_work); INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); + INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work); wl->freezable_wq = create_freezable_workqueue("wl12xx_wq"); if (!wl->freezable_wq) { diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index e43a6b2c1d91..fcba055ef196 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -55,6 +55,12 @@ void wl1271_scan_complete_work(struct work_struct *work) vif = wl->scan_vif; wlvif = wl12xx_vif_to_data(vif); + /* + * Rearm the tx watchdog just before idling scan. This + * prevents just-finished scans from triggering the watchdog + */ + wl12xx_rearm_tx_watchdog_locked(wl); + wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 8f78fddcb723..43ae49143d68 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -226,6 +226,10 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl->tx_blocks_available -= total_blocks; wl->tx_allocated_blocks += total_blocks; + /* If the FW was empty before, arm the Tx watchdog */ + if (wl->tx_allocated_blocks == total_blocks) + wl12xx_rearm_tx_watchdog_locked(wl); + ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); wl->tx_allocated_pkts[ac]++; diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index e3977b55a710..5cf8c32d40d1 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -227,5 +227,6 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids); /* from main.c */ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); +void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 6e13a3073e9f..749a15a75d38 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -495,6 +495,9 @@ struct wl1271 { /* last wlvif we transmitted from */ struct wl12xx_vif *last_wlvif; + + /* work to fire when Tx is stuck */ + struct delayed_work tx_watchdog_work; }; struct wl1271_station { -- cgit v1.2.3