From cdaac6281170ee2934ad443cb60fbdd6cf318b64 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 11:57:16 +0200 Subject: wl12xx: fw api change - add role_id to set_template The set_template commands now takes the role_id as parameter. Usually, we'll use the vif's main role_id. However, sometimes we'll want to use wlvif->dev_role_id instead of wlvif->role_id, so pass the wanted role_id as param. Update WL127X_FW_NAME/WL128X_FW_NAME. (This commit starts a series of fw update patches, and changes the start() callback to return an error in order to prevent the use of the driver during the transition. This change will be reverted in the last patch of series) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/wl12xx/cmd.c') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 25990bd38be6..28e0a56d794f 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -1022,8 +1022,9 @@ out: return ret; } -int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, - void *buf, size_t buf_len, int index, u32 rates) +int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, + u16 template_id, void *buf, size_t buf_len, + int index, u32 rates) { struct wl1271_cmd_template_set *cmd; int ret = 0; @@ -1039,6 +1040,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, goto out; } + /* during initialization wlvif is NULL */ + cmd->role_id = role_id; cmd->len = cpu_to_le16(buf_len); cmd->template_type = template_id; cmd->enabled_rates = cpu_to_le32(rates); @@ -1082,7 +1085,8 @@ int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif) ptr = skb->data; } - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_NULL_DATA, ptr, size, 0, wlvif->basic_rate); out: @@ -1105,7 +1109,7 @@ int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl, if (!skb) goto out; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_KLV, skb->data, skb->len, CMD_TEMPL_KLV_IDX_NULL_DATA, wlvif->basic_rate); @@ -1130,7 +1134,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (!skb) goto out; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_PS_POLL, skb->data, skb->len, 0, wlvif->basic_rate_set); out: @@ -1138,9 +1143,10 @@ out: return ret; } -int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, +int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 role_id, u8 band, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len, u8 band) + const u8 *ie, size_t ie_len) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct sk_buff *skb; @@ -1158,10 +1164,12 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); if (band == IEEE80211_BAND_2GHZ) - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + ret = wl1271_cmd_template_set(wl, role_id, + CMD_TEMPL_CFG_PROBE_REQ_2_4, skb->data, skb->len, 0, rate); else - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + ret = wl1271_cmd_template_set(wl, role_id, + CMD_TEMPL_CFG_PROBE_REQ_5, skb->data, skb->len, 0, rate); out: @@ -1186,10 +1194,12 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]); if (wlvif->band == IEEE80211_BAND_2GHZ) - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_CFG_PROBE_REQ_2_4, skb->data, skb->len, 0, rate); else - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_CFG_PROBE_REQ_5, skb->data, skb->len, 0, rate); if (ret < 0) @@ -1235,7 +1245,7 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif, memcpy(tmpl.sender_hw, vif->addr, ETH_ALEN); tmpl.sender_ip = ip_addr; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP, &tmpl, sizeof(tmpl), 0, wlvif->basic_rate); @@ -1260,7 +1270,8 @@ int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif) /* FIXME: not sure what priority to use here */ template.qos_ctrl = cpu_to_le16(0); - return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template, + return wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_QOS_NULL_DATA, &template, sizeof(template), 0, wlvif->basic_rate); } -- cgit v1.2.3 From c059beb2adbf632748ac63ea829c69437575761a Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 11:57:17 +0200 Subject: wl12xx: use dev_role_id for scans Use device role for scans when the sta is not associated. sched_scan is used only when the sta is not associated, and thus should use the dev role (instead of sta role). Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 3 ++- drivers/net/wireless/wl12xx/main.c | 2 +- drivers/net/wireless/wl12xx/scan.c | 20 ++++++++++++++------ 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/wl12xx/cmd.c') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 28e0a56d794f..10dba19f3eb3 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -1029,7 +1029,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, struct wl1271_cmd_template_set *cmd; int ret = 0; - wl1271_debug(DEBUG_CMD, "cmd template_set %d", template_id); + wl1271_debug(DEBUG_CMD, "cmd template_set %d (role %d)", + template_id, role_id); WARN_ON(buf_len > WL1271_CMD_TEMPL_MAX_SIZE); buf_len = min_t(size_t, buf_len, WL1271_CMD_TEMPL_MAX_SIZE); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index e4549dfb70d4..d16ea7a528a2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3070,7 +3070,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, /* cancel ROC before scanning */ if (wl12xx_dev_role_started(wlvif)) - wl12xx_stop_dev(wl, wlvif); + wl12xx_croc(wl, wlvif->dev_role_id); ret = wl1271_scan(hw->priv, vif, ssid, len, req); out_sleep: diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 416ae9145d61..6adc7333b7e6 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -77,7 +77,10 @@ void wl1271_scan_complete_work(struct work_struct *work) (is_ibss && !test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))) && !test_bit(wlvif->dev_role_id, wl->roc_map)) { /* restore remain on channel */ - wl12xx_start_dev(wl, wlvif); + if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) + wl12xx_start_dev(wl, wlvif); + else + wl12xx_roc(wl, wlvif, wlvif->dev_role_id); } wl1271_ps_elp_sleep(wl); @@ -185,11 +188,16 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, if (passive) scan_options |= WL1271_SCAN_OPT_PASSIVE; - if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID)) { + if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID || + wlvif->dev_role_id == WL12XX_INVALID_ROLE_ID)) { ret = -EINVAL; goto out; } - cmd->params.role_id = wlvif->role_id; + if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + cmd->params.role_id = wlvif->role_id; + else + cmd->params.role_id = wlvif->dev_role_id; + cmd->params.scan_options = cpu_to_le16(scan_options); cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, @@ -218,7 +226,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, memcpy(cmd->addr, vif->addr, ETH_ALEN); ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, + cmd->params.role_id, band, wl->scan.ssid, wl->scan.ssid_len, wl->scan.req->ie, wl->scan.req->ie_len); @@ -662,7 +670,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, if (!force_passive && cfg->active[0]) { u8 band = IEEE80211_BAND_2GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, + wlvif->dev_role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, ies->ie[band], @@ -676,7 +684,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, if (!force_passive && cfg->active[1]) { u8 band = IEEE80211_BAND_5GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, + wlvif->dev_role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, ies->ie[band], -- cgit v1.2.3 From 8332f0f6dc017aef9aa4a95a6a2bd6eba4c82d0a Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 11:57:19 +0200 Subject: wl12xx: fw api change - update cmd/acx/event enums Update enums/structs to the new fw api. Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.h | 137 ++++++++++++++++++------------------ drivers/net/wireless/wl12xx/boot.c | 2 +- drivers/net/wireless/wl12xx/cmd.c | 12 +++- drivers/net/wireless/wl12xx/cmd.h | 122 +++++++++++++++++--------------- drivers/net/wireless/wl12xx/event.h | 15 ++-- drivers/net/wireless/wl12xx/main.c | 2 +- 6 files changed, 156 insertions(+), 134 deletions(-) (limited to 'drivers/net/wireless/wl12xx/cmd.c') diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index a5bf761c83d2..0749df58c85d 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1153,74 +1153,75 @@ struct wl12xx_acx_config_hangover { } __packed; enum { - ACX_WAKE_UP_CONDITIONS = 0x0002, - ACX_MEM_CFG = 0x0003, - ACX_SLOT = 0x0004, - ACX_AC_CFG = 0x0007, - ACX_MEM_MAP = 0x0008, - ACX_AID = 0x000A, - ACX_MEDIUM_USAGE = 0x000F, - ACX_TX_QUEUE_CFG = 0x0011, /* FIXME: only used by wl1251 */ - ACX_STATISTICS = 0x0013, /* Debug API */ - ACX_PWR_CONSUMPTION_STATISTICS = 0x0014, - ACX_FEATURE_CFG = 0x0015, - ACX_TID_CFG = 0x001A, - ACX_PS_RX_STREAMING = 0x001B, - ACX_BEACON_FILTER_OPT = 0x001F, - ACX_NOISE_HIST = 0x0021, - ACX_HDK_VERSION = 0x0022, /* ??? */ - ACX_PD_THRESHOLD = 0x0023, - ACX_TX_CONFIG_OPT = 0x0024, - ACX_CCA_THRESHOLD = 0x0025, - ACX_EVENT_MBOX_MASK = 0x0026, - ACX_CONN_MONIT_PARAMS = 0x002D, - ACX_BCN_DTIM_OPTIONS = 0x0031, - ACX_SG_ENABLE = 0x0032, - ACX_SG_CFG = 0x0033, - ACX_FM_COEX_CFG = 0x0034, - ACX_BEACON_FILTER_TABLE = 0x0038, - ACX_ARP_IP_FILTER = 0x0039, - ACX_ROAMING_STATISTICS_TBL = 0x003B, - ACX_RATE_POLICY = 0x003D, - ACX_CTS_PROTECTION = 0x003E, - ACX_SLEEP_AUTH = 0x003F, - ACX_PREAMBLE_TYPE = 0x0040, - ACX_ERROR_CNT = 0x0041, - ACX_IBSS_FILTER = 0x0044, - ACX_SERVICE_PERIOD_TIMEOUT = 0x0045, - ACX_TSF_INFO = 0x0046, - ACX_CONFIG_PS_WMM = 0x0049, - ACX_ENABLE_RX_DATA_FILTER = 0x004A, - ACX_SET_RX_DATA_FILTER = 0x004B, - ACX_GET_DATA_FILTER_STATISTICS = 0x004C, - ACX_RX_CONFIG_OPT = 0x004E, - ACX_FRAG_CFG = 0x004F, - ACX_BET_ENABLE = 0x0050, - ACX_RSSI_SNR_TRIGGER = 0x0051, - ACX_RSSI_SNR_WEIGHTS = 0x0052, - ACX_KEEP_ALIVE_MODE = 0x0053, - ACX_SET_KEEP_ALIVE_CONFIG = 0x0054, - ACX_BA_SESSION_INIT_POLICY = 0x0055, - ACX_BA_SESSION_RX_SETUP = 0x0056, - ACX_PEER_HT_CAP = 0x0057, - ACX_HT_BSS_OPERATION = 0x0058, - ACX_COEX_ACTIVITY = 0x0059, - ACX_BURST_MODE = 0x005C, - ACX_SET_RATE_MGMT_PARAMS = 0x005D, - ACX_SET_RATE_ADAPT_PARAMS = 0x0060, - ACX_SET_DCO_ITRIM_PARAMS = 0x0061, - ACX_GEN_FW_CMD = 0x0070, - ACX_HOST_IF_CFG_BITMAP = 0x0071, - ACX_MAX_TX_FAILURE = 0x0072, - ACX_UPDATE_INCONNECTION_STA_LIST = 0x0073, - DOT11_RX_MSDU_LIFE_TIME = 0x1004, - DOT11_CUR_TX_PWR = 0x100D, - DOT11_RX_DOT11_MODE = 0x1012, - DOT11_RTS_THRESHOLD = 0x1013, - DOT11_GROUP_ADDRESS_TBL = 0x1014, - ACX_PM_CONFIG = 0x1016, - ACX_CONFIG_PS = 0x1017, - ACX_CONFIG_HANGOVER = 0x1018, + ACX_WAKE_UP_CONDITIONS = 0x0000, + ACX_MEM_CFG = 0x0001, + ACX_SLOT = 0x0002, + ACX_AC_CFG = 0x0003, + ACX_MEM_MAP = 0x0004, + ACX_AID = 0x0005, + ACX_MEDIUM_USAGE = 0x0006, + ACX_STATISTICS = 0x0007, + ACX_PWR_CONSUMPTION_STATISTICS = 0x0008, + ACX_TID_CFG = 0x0009, + ACX_PS_RX_STREAMING = 0x000A, + ACX_BEACON_FILTER_OPT = 0x000B, + ACX_NOISE_HIST = 0x000C, + ACX_HDK_VERSION = 0x000D, + ACX_PD_THRESHOLD = 0x000E, + ACX_TX_CONFIG_OPT = 0x000F, + ACX_CCA_THRESHOLD = 0x0010, + ACX_EVENT_MBOX_MASK = 0x0011, + ACX_CONN_MONIT_PARAMS = 0x0012, + ACX_DISABLE_BROADCASTS = 0x0013, + ACX_BCN_DTIM_OPTIONS = 0x0014, + ACX_SG_ENABLE = 0x0015, + ACX_SG_CFG = 0x0016, + ACX_FM_COEX_CFG = 0x0017, + ACX_BEACON_FILTER_TABLE = 0x0018, + ACX_ARP_IP_FILTER = 0x0019, + ACX_ROAMING_STATISTICS_TBL = 0x001A, + ACX_RATE_POLICY = 0x001B, + ACX_CTS_PROTECTION = 0x001C, + ACX_SLEEP_AUTH = 0x001D, + ACX_PREAMBLE_TYPE = 0x001E, + ACX_ERROR_CNT = 0x001F, + ACX_IBSS_FILTER = 0x0020, + ACX_SERVICE_PERIOD_TIMEOUT = 0x0021, + ACX_TSF_INFO = 0x0022, + ACX_CONFIG_PS_WMM = 0x0023, + ACX_ENABLE_RX_DATA_FILTER = 0x0024, + ACX_SET_RX_DATA_FILTER = 0x0025, + ACX_GET_DATA_FILTER_STATISTICS = 0x0026, + ACX_RX_CONFIG_OPT = 0x0027, + ACX_FRAG_CFG = 0x0028, + ACX_BET_ENABLE = 0x0029, + ACX_RSSI_SNR_TRIGGER = 0x002A, + ACX_RSSI_SNR_WEIGHTS = 0x002B, + ACX_KEEP_ALIVE_MODE = 0x002C, + ACX_SET_KEEP_ALIVE_CONFIG = 0x002D, + ACX_BA_SESSION_INIT_POLICY = 0x002E, + ACX_BA_SESSION_RX_SETUP = 0x002F, + ACX_PEER_HT_CAP = 0x0030, + ACX_HT_BSS_OPERATION = 0x0031, + ACX_COEX_ACTIVITY = 0x0032, + ACX_BURST_MODE = 0x0033, + ACX_SET_RATE_MGMT_PARAMS = 0x0034, + ACX_GET_RATE_MGMT_PARAMS = 0x0035, + ACX_SET_RATE_ADAPT_PARAMS = 0x0036, + ACX_SET_DCO_ITRIM_PARAMS = 0x0037, + ACX_GEN_FW_CMD = 0x0038, + ACX_HOST_IF_CFG_BITMAP = 0x0039, + ACX_MAX_TX_FAILURE = 0x003A, + ACX_UPDATE_INCONNECTION_STA_LIST = 0x003B, + DOT11_RX_MSDU_LIFE_TIME = 0x003C, + DOT11_CUR_TX_PWR = 0x003D, + DOT11_RTS_THRESHOLD = 0x003E, + DOT11_GROUP_ADDRESS_TBL = 0x003F, + ACX_PM_CONFIG = 0x0040, + ACX_CONFIG_PS = 0x0041, + ACX_CONFIG_HANGOVER = 0x0042, + ACX_FEATURE_CFG = 0x0043, + ACX_PROTECTION_CFG = 0x0044, }; diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 32a7f9b69d7c..eea2dda6e6e7 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -449,7 +449,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) wl->event_mask = BSS_LOSE_EVENT_ID | SCAN_COMPLETE_EVENT_ID | PS_REPORT_EVENT_ID | - DISCONNECT_EVENT_COMPLETE_ID | + ROLE_STOP_COMPLETE_EVENT_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | PSPOLL_DELIVERY_FAILURE_EVENT_ID | SOFT_GEMINI_SENSE_EVENT_ID | diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 10dba19f3eb3..88e94c5938a4 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -566,7 +566,7 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl, goto out_free; } - ret = wl1271_cmd_wait_for_event(wl, DISCONNECT_EVENT_COMPLETE_ID); + ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID); if (ret < 0) { wl1271_error("cmd role stop dev event completion error"); goto out_free; @@ -715,6 +715,8 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int); cmd->ap.dtim_interval = bss_conf->dtim_period; cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP; + /* FIXME: Change when adding DFS */ + cmd->ap.reset_tsf = 1; /* By default reset AP TSF */ cmd->channel = wlvif->channel; if (!bss_conf->hidden_ssid) { @@ -1756,6 +1758,7 @@ out: } int wl12xx_cmd_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch) { struct wl12xx_cmd_channel_switch *cmd; @@ -1769,10 +1772,13 @@ int wl12xx_cmd_channel_switch(struct wl1271 *wl, goto out; } + cmd->role_id = wlvif->role_id; cmd->channel = ch_switch->channel->hw_value; cmd->switch_time = ch_switch->count; - cmd->tx_suspend = ch_switch->block_tx; - cmd->flush = 0; /* this value is ignored by the FW */ + cmd->stop_tx = ch_switch->block_tx; + + /* FIXME: control from mac80211 in the future */ + cmd->post_switch_tx_disable = 0; /* Enable TX on the target channel */ ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 8e8035d33068..edd240db0dcc 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -91,6 +91,7 @@ int wl12xx_cmd_config_fwlog(struct wl1271 *wl); int wl12xx_cmd_start_fwlog(struct wl1271 *wl); int wl12xx_cmd_stop_fwlog(struct wl1271 *wl); int wl12xx_cmd_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl); int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, @@ -98,62 +99,65 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); enum wl1271_commands { - CMD_INTERROGATE = 1, /*use this to read information elements*/ - CMD_CONFIGURE = 2, /*use this to write information elements*/ - CMD_ENABLE_RX = 3, - CMD_ENABLE_TX = 4, - CMD_DISABLE_RX = 5, - CMD_DISABLE_TX = 6, - CMD_SCAN = 8, - CMD_STOP_SCAN = 9, - CMD_SET_KEYS = 12, - CMD_READ_MEMORY = 13, - CMD_WRITE_MEMORY = 14, - CMD_SET_TEMPLATE = 19, - CMD_TEST = 23, - CMD_NOISE_HIST = 28, - CMD_QUIET_ELEMENT_SET_STATE = 29, - CMD_SET_BCN_MODE = 33, - CMD_MEASUREMENT = 34, - CMD_STOP_MEASUREMENT = 35, - CMD_SET_PS_MODE = 37, - CMD_CHANNEL_SWITCH = 38, - CMD_STOP_CHANNEL_SWICTH = 39, - CMD_AP_DISCOVERY = 40, - CMD_STOP_AP_DISCOVERY = 41, - CMD_HEALTH_CHECK = 45, - CMD_DEBUG = 46, - CMD_TRIGGER_SCAN_TO = 47, - CMD_CONNECTION_SCAN_CFG = 48, - CMD_CONNECTION_SCAN_SSID_CFG = 49, - CMD_START_PERIODIC_SCAN = 50, - CMD_STOP_PERIODIC_SCAN = 51, - CMD_SET_PEER_STATE = 52, - CMD_REMAIN_ON_CHANNEL = 53, - CMD_CANCEL_REMAIN_ON_CHANNEL = 54, - - CMD_CONFIG_FWLOGGER = 55, - CMD_START_FWLOGGER = 56, - CMD_STOP_FWLOGGER = 57, - - /* AP commands */ - CMD_ADD_PEER = 62, - CMD_REMOVE_PEER = 63, + CMD_INTERROGATE = 1, /* use this to read information elements */ + CMD_CONFIGURE = 2, /* use this to write information elements */ + CMD_ENABLE_RX = 3, + CMD_ENABLE_TX = 4, + CMD_DISABLE_RX = 5, + CMD_DISABLE_TX = 6, + CMD_SCAN = 7, + CMD_STOP_SCAN = 8, + CMD_SET_KEYS = 9, + CMD_READ_MEMORY = 10, + CMD_WRITE_MEMORY = 11, + CMD_SET_TEMPLATE = 12, + CMD_TEST = 13, + CMD_NOISE_HIST = 14, + CMD_QUIET_ELEMENT_SET_STATE = 15, + CMD_SET_BCN_MODE = 16, + + CMD_MEASUREMENT = 17, + CMD_STOP_MEASUREMENT = 18, + CMD_SET_PS_MODE = 19, + CMD_CHANNEL_SWITCH = 20, + CMD_STOP_CHANNEL_SWICTH = 21, + CMD_AP_DISCOVERY = 22, + CMD_STOP_AP_DISCOVERY = 23, + CMD_HEALTH_CHECK = 24, + CMD_DEBUG = 25, + CMD_TRIGGER_SCAN_TO = 26, + CMD_CONNECTION_SCAN_CFG = 27, + CMD_CONNECTION_SCAN_SSID_CFG = 28, + CMD_START_PERIODIC_SCAN = 29, + CMD_STOP_PERIODIC_SCAN = 30, + CMD_SET_PEER_STATE = 31, + CMD_REMAIN_ON_CHANNEL = 32, + CMD_CANCEL_REMAIN_ON_CHANNEL = 33, + CMD_CONFIG_FWLOGGER = 34, + CMD_START_FWLOGGER = 35, + CMD_STOP_FWLOGGER = 36, + + /* Access point commands */ + CMD_ADD_PEER = 37, + CMD_REMOVE_PEER = 38, /* Role API */ - CMD_ROLE_ENABLE = 70, - CMD_ROLE_DISABLE = 71, - CMD_ROLE_START = 72, - CMD_ROLE_STOP = 73, + CMD_ROLE_ENABLE = 39, + CMD_ROLE_DISABLE = 40, + CMD_ROLE_START = 41, + CMD_ROLE_STOP = 42, - /* WIFI Direct */ - CMD_WFD_START_DISCOVERY = 80, - CMD_WFD_STOP_DISCOVERY = 81, - CMD_WFD_ATTRIBUTE_CONFIG = 82, + /* DFS */ + CMD_START_RADAR_DETECTION = 43, + CMD_STOP_RADAR_DETECTION = 44, - CMD_NOP = 100, + /* WIFI Direct */ + CMD_WFD_START_DISCOVERY = 45, + CMD_WFD_STOP_DISCOVERY = 46, + CMD_WFD_ATTRIBUTE_CONFIG = 47, + CMD_NOP = 48, + CMD_LAST_COMMAND, - NUM_COMMANDS, MAX_COMMAND_ID = 0xFFFF, }; @@ -341,7 +345,9 @@ struct wl12xx_cmd_role_start { u8 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 padding_1[5]; + u8 reset_tsf; + + u8 padding_1[4]; } __packed ap; }; } __packed; @@ -701,14 +707,18 @@ struct wl12xx_cmd_stop_fwlog { struct wl12xx_cmd_channel_switch { struct wl1271_cmd_header header; + u8 role_id; + /* The new serving channel */ u8 channel; /* Relative time of the serving channel switch in TBTT units */ u8 switch_time; - /* 1: Suspend TX till switch time; 0: Do not suspend TX */ - u8 tx_suspend; - /* 1: Flush TX at switch time; 0: Do not flush */ - u8 flush; + /* Stop the role TX, should expect it after radar detection */ + u8 stop_tx; + /* The target channel tx status 1-stopped 0-open*/ + u8 post_switch_tx_disable; + + u8 padding[3]; } __packed; struct wl12xx_cmd_stop_channel_switch { diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h index 1d878ba47bf4..b41f730f11dd 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -53,8 +53,8 @@ enum { AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12), PS_REPORT_EVENT_ID = BIT(13), PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14), - DISCONNECT_EVENT_COMPLETE_ID = BIT(15), - /* BIT(16) is reserved */ + ROLE_STOP_COMPLETE_EVENT_ID = BIT(15), + RADAR_DETECTED_EVENT_ID = BIT(16), CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17), BSS_LOSE_EVENT_ID = BIT(18), REGAINED_BSS_EVENT_ID = BIT(19), @@ -94,7 +94,7 @@ struct event_mailbox { u8 soft_gemini_sense_info; u8 soft_gemini_protective_info; s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; - u8 channel_switch_status; + u8 change_auto_mode_timeout; u8 scheduled_scan_status; u8 ps_status; /* tuned channel (roc) */ @@ -119,12 +119,17 @@ struct event_mailbox { u8 rx_ba_allowed; u8 reserved_6[2]; + /* Channel switch results */ + + u8 channel_switch_role_id; + u8 channel_switch_status; + u8 reserved_7[2]; + u8 ps_poll_delivery_failure_role_ids; u8 stopped_role_ids; u8 started_role_ids; - u8 change_auto_mode_timeout; - u8 reserved_7[12]; + u8 reserved_8[9]; } __packed; int wl1271_event_unmask(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 1cdffa88eb02..a339a85ecfb0 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -4409,7 +4409,7 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw, /* TODO: change mac80211 to pass vif as param */ wl12xx_for_each_wlvif_sta(wl, wlvif) { - ret = wl12xx_cmd_channel_switch(wl, ch_switch); + ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch); if (!ret) set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); -- 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/cmd.c') 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 5ec8a448e0e978103bc5ca7136084b5e2b36989e Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 2 Feb 2012 12:22:09 +0200 Subject: wl12xx: consider encryption and QoS in auto arp template When configuring the arp response template, and encryption is enabled, we should add some space and set the protected flag bit in the fc. In order to track the encryption type, set wlvif->encryption_type when setting an encryption key, and reconfigure the arp response. Clear this field on wl1271_join, as keys have to be re-configured anyway after a join command. Similarly, track whether QoS is configured. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 88 +++++++++++++++++++++++------- drivers/net/wireless/wl12xx/cmd.h | 3 +- drivers/net/wireless/wl12xx/debugfs.c | 1 + drivers/net/wireless/wl12xx/init.c | 6 +- drivers/net/wireless/wl12xx/main.c | 31 +++++++++-- drivers/net/wireless/wl12xx/tx.c | 11 ++-- drivers/net/wireless/wl12xx/tx.h | 4 +- drivers/net/wireless/wl12xx/wl12xx.h | 6 ++ drivers/net/wireless/wl12xx/wl12xx_80211.h | 2 +- 9 files changed, 117 insertions(+), 35 deletions(-) (limited to 'drivers/net/wireless/wl12xx/cmd.c') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index bf3c37bd1f12..b776d9d5efe8 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -1213,32 +1213,34 @@ out: return skb; } -int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif, - __be32 ip_addr) +int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif) { - int ret; + int ret, extra; + u16 fc; struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); - struct wl12xx_arp_rsp_template tmpl; + struct sk_buff *skb; + struct wl12xx_arp_rsp_template *tmpl; struct ieee80211_hdr_3addr *hdr; struct arphdr *arp_hdr; - memset(&tmpl, 0, sizeof(tmpl)); + skb = dev_alloc_skb(sizeof(*hdr) + sizeof(__le16) + sizeof(*tmpl) + + WL1271_EXTRA_SPACE_MAX); + if (!skb) { + wl1271_error("failed to allocate buffer for arp rsp template"); + return -ENOMEM; + } - /* mac80211 header */ - hdr = &tmpl.hdr; - hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_DATA | - IEEE80211_FCTL_TODS); - memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN); - memcpy(hdr->addr2, vif->addr, ETH_ALEN); - memset(hdr->addr3, 0xff, ETH_ALEN); + skb_reserve(skb, sizeof(*hdr) + WL1271_EXTRA_SPACE_MAX); + + tmpl = (struct wl12xx_arp_rsp_template *)skb_put(skb, sizeof(*tmpl)); + memset(tmpl, 0, sizeof(tmpl)); /* llc layer */ - memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header)); - tmpl.llc_type = cpu_to_be16(ETH_P_ARP); + memcpy(tmpl->llc_hdr, rfc1042_header, sizeof(rfc1042_header)); + tmpl->llc_type = cpu_to_be16(ETH_P_ARP); /* arp header */ - arp_hdr = &tmpl.arp_hdr; + arp_hdr = &tmpl->arp_hdr; arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER); arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP); arp_hdr->ar_hln = ETH_ALEN; @@ -1246,13 +1248,59 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif, arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY); /* arp payload */ - memcpy(tmpl.sender_hw, vif->addr, ETH_ALEN); - tmpl.sender_ip = ip_addr; + memcpy(tmpl->sender_hw, vif->addr, ETH_ALEN); + tmpl->sender_ip = wlvif->ip_addr; + + /* encryption space */ + switch (wlvif->encryption_type) { + case KEY_TKIP: + extra = WL1271_EXTRA_SPACE_TKIP; + break; + case KEY_AES: + extra = WL1271_EXTRA_SPACE_AES; + break; + case KEY_NONE: + case KEY_WEP: + case KEY_GEM: + extra = 0; + break; + default: + wl1271_warning("Unknown encryption type: %d", + wlvif->encryption_type); + ret = -EINVAL; + goto out; + } + + if (extra) { + u8 *space = skb_push(skb, extra); + memset(space, 0, extra); + } + + /* QoS header - BE */ + if (wlvif->sta.qos) + memset(skb_push(skb, sizeof(__le16)), 0, sizeof(__le16)); + + /* mac80211 header */ + hdr = (struct ieee80211_hdr_3addr *)skb_push(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(hdr)); + fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS; + if (wlvif->sta.qos) + fc |= IEEE80211_STYPE_QOS_DATA; + else + fc |= IEEE80211_STYPE_DATA; + if (wlvif->encryption_type != KEY_NONE) + fc |= IEEE80211_FCTL_PROTECTED; + + hdr->frame_control = cpu_to_le16(fc); + memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN); + memcpy(hdr->addr2, vif->addr, ETH_ALEN); + memset(hdr->addr3, 0xff, ETH_ALEN); ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP, - &tmpl, sizeof(tmpl), 0, + skb->data, skb->len, 0, wlvif->basic_rate); - +out: + dev_kfree_skb(skb); return ret; } diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 83a2a2edb261..de217d92516b 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -67,8 +67,7 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb); -int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif, - __be32 ip_addr); +int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif); int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif); diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 1c2623850eae..f063c704ebcf 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -579,6 +579,7 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, VIF_STATE_PRINT_INT(sta.basic_rate_idx); VIF_STATE_PRINT_INT(sta.ap_rate_idx); VIF_STATE_PRINT_INT(sta.p2p_rate_idx); + VIF_STATE_PRINT_INT(sta.qos); } else { VIF_STATE_PRINT_INT(ap.global_hlid); VIF_STATE_PRINT_INT(ap.bcast_hlid); diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index fcf2b128f9a4..acc03799fc66 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -37,6 +37,7 @@ int wl1271_init_templates_config(struct wl1271 *wl) { int ret, i; + size_t max_size; /* send empty templates for fw memory reservation */ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, @@ -89,10 +90,11 @@ int wl1271_init_templates_config(struct wl1271 *wl) if (ret < 0) return ret; + max_size = sizeof(struct wl12xx_arp_rsp_template) + + WL1271_EXTRA_SPACE_MAX; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_ARP_RSP, NULL, - sizeof - (struct wl12xx_arp_rsp_template), + max_size, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index b828149a1655..a84fe28873ea 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2324,6 +2324,9 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) wl1271_info("JOIN while associated."); + /* clear encryption type */ + wlvif->encryption_type = KEY_NONE; + if (set_assoc) set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags); @@ -2981,6 +2984,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, wl1271_error("Could not add or replace key"); goto out_sleep; } + + /* + * reconfiguring arp response if the unicast (or common) + * encryption key type was changed + */ + if (wlvif->bss_type == BSS_TYPE_STA_BSS && + (sta || key_type == KEY_WEP) && + wlvif->encryption_type != key_type) { + wlvif->encryption_type = key_type; + ret = wl1271_cmd_build_arp_rsp(wl, wlvif); + if (ret < 0) { + wl1271_warning("build arp rsp failed: %d", ret); + goto out_sleep; + } + } break; case DISABLE_KEY: @@ -3822,19 +3840,22 @@ sta_not_found: if (ret < 0) goto out; - if (changed & BSS_CHANGED_ARP_FILTER) { + if ((changed & BSS_CHANGED_ARP_FILTER) || + (!is_ibss && (changed & BSS_CHANGED_QOS))) { __be32 addr = bss_conf->arp_addr_list[0]; + wlvif->sta.qos = bss_conf->qos; WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS); if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled) { + wlvif->ip_addr = addr; /* * The template should have been configured only upon * association. however, it seems that the correct ip * isn't being set (when sending), so we have to * reconfigure the template upon every ip change. */ - ret = wl1271_cmd_build_arp_rsp(wl, wlvif, addr); + ret = wl1271_cmd_build_arp_rsp(wl, wlvif); if (ret < 0) { wl1271_warning("build arp rsp failed: %d", ret); goto out; @@ -3843,8 +3864,10 @@ sta_not_found: ret = wl1271_acx_arp_ip_filter(wl, wlvif, ACX_ARP_FILTER_ARP_FILTERING, addr); - } else + } else { + wlvif->ip_addr = 0; ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr); + } if (ret < 0) goto out; @@ -5007,7 +5030,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) }; /* The tx descriptor buffer and the TKIP space. */ - wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE + + wl->hw->extra_tx_headroom = WL1271_EXTRA_SPACE_TKIP + sizeof(struct wl1271_tx_hw_descr); /* unit us */ diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 1f5cc2af5bc6..6446e4d3e8f9 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -386,7 +386,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) - extra = WL1271_TKIP_IV_SPACE; + extra = WL1271_EXTRA_SPACE_TKIP; if (info->control.hw_key) { bool is_wep; @@ -861,8 +861,9 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, if (info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); - memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen); - skb_pull(skb, WL1271_TKIP_IV_SPACE); + memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, + hdrlen); + skb_pull(skb, WL1271_EXTRA_SPACE_TKIP); } wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" @@ -1004,9 +1005,9 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); - memmove(skb->data + WL1271_TKIP_IV_SPACE, + memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen); - skb_pull(skb, WL1271_TKIP_IV_SPACE); + skb_pull(skb, WL1271_EXTRA_SPACE_TKIP); } info->status.rates[0].idx = -1; diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 7ceb3ceaa648..e3977b55a710 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -52,7 +52,9 @@ #define TX_HW_RESULT_QUEUE_LEN_MASK 0xf #define WL1271_TX_ALIGN_TO 4 -#define WL1271_TKIP_IV_SPACE 4 +#define WL1271_EXTRA_SPACE_TKIP 4 +#define WL1271_EXTRA_SPACE_AES 8 +#define WL1271_EXTRA_SPACE_MAX 8 /* Used for management frames and dummy packets */ #define WL1271_TID_MGMT 7 diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 61c58c13fa90..81af416d17da 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -507,6 +507,8 @@ struct wl12xx_vif { u8 basic_rate_idx; u8 ap_rate_idx; u8 p2p_rate_idx; + + bool qos; } sta; struct { u8 global_hlid; @@ -573,6 +575,10 @@ struct wl12xx_vif { int rssi_thold; int last_rssi_event; + /* save the current encryption type for auto-arp config */ + u8 encryption_type; + __be32 ip_addr; + /* RX BA constraint value */ bool ba_support; bool ba_allowed; diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index 8f0ffaf62309..22b0bc98d7b5 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -117,7 +117,7 @@ struct wl12xx_ps_poll_template { } __packed; struct wl12xx_arp_rsp_template { - struct ieee80211_hdr_3addr hdr; + /* not including ieee80211 header */ u8 llc_hdr[sizeof(rfc1042_header)]; __be16 llc_type; -- cgit v1.2.3 From 0b0e32b792b4077c113ae70d6de12b5f301e0882 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 28 Feb 2012 00:41:29 +0200 Subject: wl12xx: change bits in the link_map under spin lock These bits are used in op_tx to determine if a packet should be dropped. As such we should use the spin lock to sync the state. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/net/wireless/wl12xx/cmd.c') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index b776d9d5efe8..ef994dacf8d2 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -459,23 +459,32 @@ out: int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) { + unsigned long flags; u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS); if (link >= WL12XX_MAX_LINKS) return -EBUSY; + /* these bits are used by op_tx */ + spin_lock_irqsave(&wl->wl_lock, flags); __set_bit(link, wl->links_map); __set_bit(link, wlvif->links_map); + spin_unlock_irqrestore(&wl->wl_lock, flags); *hlid = link; return 0; } void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) { + unsigned long flags; + if (*hlid == WL12XX_INVALID_LINK_ID) return; + /* these bits are used by op_tx */ + spin_lock_irqsave(&wl->wl_lock, flags); __clear_bit(*hlid, wl->links_map); __clear_bit(*hlid, wlvif->links_map); + spin_unlock_irqrestore(&wl->wl_lock, flags); *hlid = WL12XX_INVALID_LINK_ID; } -- cgit v1.2.3 From 6246ca003f2560e30b9696757efc271284b809f6 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 28 Feb 2012 00:41:30 +0200 Subject: wl12xx: reset link Tx queues when freeing it Before, the link was first freed (invalidating it in the map), and later on vif removal, all valid wlvif-related links were reset. Since these links were already invalid, we failed to reset them. The bug was made worse by op_stop, which set the tx_queue_count to 0 arbitrarily. This resulted in a negative tx_queue_count in some scenarios. Fix this by resetting the Tx-queues of a link when freeing it. Add a WARN_ON and reset all link Tx-queues in op_stop, to avoid a negative tx_queue_count. [changed WARN_ON to WARN_ON_ONCE -- Luca] Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 7 +++++++ drivers/net/wireless/wl12xx/main.c | 1 - drivers/net/wireless/wl12xx/tx.c | 13 ++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/wl12xx/cmd.c') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index ef994dacf8d2..ae1f3d7f34d2 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -485,6 +485,13 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) __clear_bit(*hlid, wl->links_map); __clear_bit(*hlid, wlvif->links_map); spin_unlock_irqrestore(&wl->wl_lock, flags); + + /* + * At this point op_tx() will not add more packets to the queues. We + * can purge them. + */ + wl1271_tx_reset_link_queues(wl, *hlid); + *hlid = WL12XX_INVALID_LINK_ID; } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index adf9bbcf88fd..712e38530153 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -4228,7 +4228,6 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) clear_bit(hlid, wlvif->ap.sta_hlid_map); memset(wl->links[hlid].addr, 0, ETH_ALEN); wl->links[hlid].ba_bitmap = 0; - wl1271_tx_reset_link_queues(wl, hlid); __clear_bit(hlid, &wl->ap_ps_map); __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); wl12xx_free_link(wl, wlvif, &hlid); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 6446e4d3e8f9..8f5ab047aec8 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -527,6 +527,7 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl, if (skb) { int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); spin_lock_irqsave(&wl->wl_lock, flags); + WARN_ON_ONCE(wl->tx_queue_count[q] <= 0); wl->tx_queue_count[q]--; spin_unlock_irqrestore(&wl->wl_lock, flags); } @@ -602,6 +603,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) skb = wl->dummy_packet; q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); spin_lock_irqsave(&wl->wl_lock, flags); + WARN_ON_ONCE(wl->tx_queue_count[q] <= 0); wl->tx_queue_count[q]--; spin_unlock_irqrestore(&wl->wl_lock, flags); } @@ -959,7 +961,6 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) else wlvif->sta.ba_rx_bitmap = 0; - wl1271_tx_reset_link_queues(wl, i); wl->links[i].allocated_pkts = 0; wl->links[i].prev_freed_pkts = 0; } @@ -973,8 +974,14 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) struct sk_buff *skb; struct ieee80211_tx_info *info; - for (i = 0; i < NUM_TX_QUEUES; i++) - wl->tx_queue_count[i] = 0; + /* only reset the queues if something bad happened */ + if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) { + for (i = 0; i < WL12XX_MAX_LINKS; i++) + wl1271_tx_reset_link_queues(wl, i); + + for (i = 0; i < NUM_TX_QUEUES; i++) + wl->tx_queue_count[i] = 0; + } wl->stopped_queues_map = 0; -- cgit v1.2.3 From b32b2b0f8ea82cb51d70b88815c25e0f1c0cf7a5 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 4 Mar 2012 10:55:49 +0200 Subject: wl12xx: increment session_counter for device role as well The sesssion_counter has to be incremented each time the dev is started (similar to sta role). Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx/cmd.c') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index ae1f3d7f34d2..1ef212f6440f 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -531,7 +531,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, goto out_free; } cmd->device.hlid = wlvif->dev_hlid; - cmd->device.session = wlvif->session_counter; + cmd->device.session = wl12xx_get_new_session_id(wl, wlvif); wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d", cmd->role_id, cmd->device.hlid, cmd->device.session); -- 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/cmd.c') 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