forked from Minki/linux
wl12xx: support FW TX inactivity triggers
In AP mode we register for the MAX_TX_RETRY and INACTIVE_STA events. Both are reported to the upper layers as a TX failure in the offending stations. In STA mode we register only for the MAX_TX_RETRY event. A TX failure is interpreted as a loss of connection. Support for IEEE80211_HW_REPORTS_TX_ACK_STATUS has been removed to avoid the inherent race condition of a mac80211 TX failure counter in addition to the FW counter. This patch depends on "mac80211: allow low level driver to report packet loss" Signed-off-by: Arik Nemtsov <arik@wizery.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
a039a99349
commit
47684808fd
@ -1524,22 +1524,46 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wl1271_acx_max_tx_retry(struct wl1271 *wl)
|
int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl)
|
||||||
{
|
{
|
||||||
struct wl1271_acx_max_tx_retry *acx = NULL;
|
struct wl1271_acx_ap_max_tx_retry *acx = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
wl1271_debug(DEBUG_ACX, "acx max tx retry");
|
wl1271_debug(DEBUG_ACX, "acx ap max tx retry");
|
||||||
|
|
||||||
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
|
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
|
||||||
if (!acx)
|
if (!acx)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
acx->max_tx_retry = cpu_to_le16(wl->conf.tx.ap_max_tx_retries);
|
acx->max_tx_retry = cpu_to_le16(wl->conf.tx.max_tx_retries);
|
||||||
|
|
||||||
ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx));
|
ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
wl1271_warning("acx max tx retry failed: %d", ret);
|
wl1271_warning("acx ap max tx retry failed: %d", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
kfree(acx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wl1271_acx_sta_max_tx_retry(struct wl1271 *wl)
|
||||||
|
{
|
||||||
|
struct wl1271_acx_sta_max_tx_retry *acx = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
wl1271_debug(DEBUG_ACX, "acx sta max tx retry");
|
||||||
|
|
||||||
|
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
|
||||||
|
if (!acx)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
acx->max_tx_retry = wl->conf.tx.max_tx_retries;
|
||||||
|
|
||||||
|
ret = wl1271_cmd_configure(wl, ACX_CONS_TX_FAILURE, acx, sizeof(*acx));
|
||||||
|
if (ret < 0) {
|
||||||
|
wl1271_warning("acx sta max tx retry failed: %d", ret);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1145,7 +1145,7 @@ struct wl1271_acx_fw_tsf_information {
|
|||||||
u8 padding[3];
|
u8 padding[3];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
struct wl1271_acx_max_tx_retry {
|
struct wl1271_acx_ap_max_tx_retry {
|
||||||
struct acx_header header;
|
struct acx_header header;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1156,6 +1156,13 @@ struct wl1271_acx_max_tx_retry {
|
|||||||
u8 padding_1[2];
|
u8 padding_1[2];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct wl1271_acx_sta_max_tx_retry {
|
||||||
|
struct acx_header header;
|
||||||
|
|
||||||
|
u8 max_tx_retry;
|
||||||
|
u8 padding_1[3];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
struct wl1271_acx_config_ps {
|
struct wl1271_acx_config_ps {
|
||||||
struct acx_header header;
|
struct acx_header header;
|
||||||
|
|
||||||
@ -1307,7 +1314,8 @@ int wl1271_acx_set_ba_session(struct wl1271 *wl,
|
|||||||
int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
|
int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
|
||||||
bool enable);
|
bool enable);
|
||||||
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
|
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
|
||||||
int wl1271_acx_max_tx_retry(struct wl1271 *wl);
|
int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl);
|
||||||
|
int wl1271_acx_sta_max_tx_retry(struct wl1271 *wl);
|
||||||
int wl1271_acx_config_ps(struct wl1271 *wl);
|
int wl1271_acx_config_ps(struct wl1271 *wl);
|
||||||
int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
|
int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
|
||||||
|
|
||||||
|
@ -478,10 +478,12 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
|
|||||||
DISCONNECT_EVENT_COMPLETE_ID |
|
DISCONNECT_EVENT_COMPLETE_ID |
|
||||||
RSSI_SNR_TRIGGER_0_EVENT_ID |
|
RSSI_SNR_TRIGGER_0_EVENT_ID |
|
||||||
PSPOLL_DELIVERY_FAILURE_EVENT_ID |
|
PSPOLL_DELIVERY_FAILURE_EVENT_ID |
|
||||||
SOFT_GEMINI_SENSE_EVENT_ID;
|
SOFT_GEMINI_SENSE_EVENT_ID |
|
||||||
|
MAX_TX_RETRY_EVENT_ID;
|
||||||
|
|
||||||
if (wl->bss_type == BSS_TYPE_AP_BSS)
|
if (wl->bss_type == BSS_TYPE_AP_BSS)
|
||||||
wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID;
|
wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID |
|
||||||
|
INACTIVE_STA_EVENT_ID;
|
||||||
else
|
else
|
||||||
wl->event_mask |= DUMMY_PACKET_EVENT_ID;
|
wl->event_mask |= DUMMY_PACKET_EVENT_ID;
|
||||||
|
|
||||||
|
@ -1070,7 +1070,7 @@ int wl1271_cmd_start_bss(struct wl1271 *wl)
|
|||||||
|
|
||||||
memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN);
|
memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN);
|
||||||
|
|
||||||
cmd->aging_period = cpu_to_le16(WL1271_AP_DEF_INACTIV_SEC);
|
cmd->aging_period = cpu_to_le16(wl->conf.tx.ap_aging_period);
|
||||||
cmd->bss_index = WL1271_AP_BSS_INDEX;
|
cmd->bss_index = WL1271_AP_BSS_INDEX;
|
||||||
cmd->global_hlid = WL1271_AP_GLOBAL_HLID;
|
cmd->global_hlid = WL1271_AP_GLOBAL_HLID;
|
||||||
cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID;
|
cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID;
|
||||||
|
@ -683,10 +683,18 @@ struct conf_tx_settings {
|
|||||||
struct conf_tx_rate_class ap_bcst_conf;
|
struct conf_tx_rate_class ap_bcst_conf;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AP-mode - allow this number of TX retries to a station before an
|
* Allow this number of TX retries to a connected station/AP before an
|
||||||
* event is triggered from FW.
|
* event is triggered from FW.
|
||||||
|
* In AP-mode the hlids of unreachable stations are given in the
|
||||||
|
* "sta_tx_retry_exceeded" member in the event mailbox.
|
||||||
*/
|
*/
|
||||||
u16 ap_max_tx_retries;
|
u8 max_tx_retries;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AP-mode - after this number of seconds a connected station is
|
||||||
|
* considered inactive.
|
||||||
|
*/
|
||||||
|
u16 ap_aging_period;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configuration for TID parameters.
|
* Configuration for TID parameters.
|
||||||
|
@ -174,6 +174,8 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
|
|||||||
u32 vector;
|
u32 vector;
|
||||||
bool beacon_loss = false;
|
bool beacon_loss = false;
|
||||||
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
|
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
|
||||||
|
bool disconnect_sta = false;
|
||||||
|
unsigned long sta_bitmap = 0;
|
||||||
|
|
||||||
wl1271_event_mbox_dump(mbox);
|
wl1271_event_mbox_dump(mbox);
|
||||||
|
|
||||||
@ -235,9 +237,54 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
|
|||||||
wl1271_tx_dummy_packet(wl);
|
wl1271_tx_dummy_packet(wl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "TX retries exceeded" has a different meaning according to mode.
|
||||||
|
* In AP mode the offending station is disconnected. In STA mode we
|
||||||
|
* report connection loss.
|
||||||
|
*/
|
||||||
|
if (vector & MAX_TX_RETRY_EVENT_ID) {
|
||||||
|
wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID");
|
||||||
|
if (is_ap) {
|
||||||
|
sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded);
|
||||||
|
disconnect_sta = true;
|
||||||
|
} else {
|
||||||
|
beacon_loss = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((vector & INACTIVE_STA_EVENT_ID) && is_ap) {
|
||||||
|
wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
|
||||||
|
sta_bitmap |= le16_to_cpu(mbox->sta_aging_status);
|
||||||
|
disconnect_sta = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (wl->vif && beacon_loss)
|
if (wl->vif && beacon_loss)
|
||||||
ieee80211_connection_loss(wl->vif);
|
ieee80211_connection_loss(wl->vif);
|
||||||
|
|
||||||
|
if (is_ap && disconnect_sta) {
|
||||||
|
u32 num_packets = wl->conf.tx.max_tx_retries;
|
||||||
|
struct ieee80211_sta *sta;
|
||||||
|
const u8 *addr;
|
||||||
|
int h;
|
||||||
|
|
||||||
|
for (h = find_first_bit(&sta_bitmap, AP_MAX_LINKS);
|
||||||
|
h < AP_MAX_LINKS;
|
||||||
|
h = find_next_bit(&sta_bitmap, AP_MAX_LINKS, h+1)) {
|
||||||
|
if (!wl1271_is_active_sta(wl, h))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
addr = wl->links[h].addr;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
sta = ieee80211_find_sta(wl->vif, addr);
|
||||||
|
if (sta) {
|
||||||
|
wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
|
||||||
|
ieee80211_report_low_ack(sta, num_packets);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,13 +58,16 @@ enum {
|
|||||||
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17),
|
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17),
|
||||||
BSS_LOSE_EVENT_ID = BIT(18),
|
BSS_LOSE_EVENT_ID = BIT(18),
|
||||||
REGAINED_BSS_EVENT_ID = BIT(19),
|
REGAINED_BSS_EVENT_ID = BIT(19),
|
||||||
ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20),
|
MAX_TX_RETRY_EVENT_ID = BIT(20),
|
||||||
/* STA: dummy paket for dynamic mem blocks */
|
/* STA: dummy paket for dynamic mem blocks */
|
||||||
DUMMY_PACKET_EVENT_ID = BIT(21),
|
DUMMY_PACKET_EVENT_ID = BIT(21),
|
||||||
/* AP: STA remove complete */
|
/* AP: STA remove complete */
|
||||||
STA_REMOVE_COMPLETE_EVENT_ID = BIT(21),
|
STA_REMOVE_COMPLETE_EVENT_ID = BIT(21),
|
||||||
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
|
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
|
||||||
|
/* STA: SG prediction */
|
||||||
SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23),
|
SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23),
|
||||||
|
/* AP: Inactive STA */
|
||||||
|
INACTIVE_STA_EVENT_ID = BIT(23),
|
||||||
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
|
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
|
||||||
PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25),
|
PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25),
|
||||||
DBG_EVENT_ID = BIT(26),
|
DBG_EVENT_ID = BIT(26),
|
||||||
@ -119,7 +122,11 @@ struct event_mailbox {
|
|||||||
|
|
||||||
/* AP FW only */
|
/* AP FW only */
|
||||||
u8 hlid_removed;
|
u8 hlid_removed;
|
||||||
|
|
||||||
|
/* a bitmap of hlids for stations that have been inactive too long */
|
||||||
__le16 sta_aging_status;
|
__le16 sta_aging_status;
|
||||||
|
|
||||||
|
/* a bitmap of hlids for stations which didn't respond to TX */
|
||||||
__le16 sta_tx_retry_exceeded;
|
__le16 sta_tx_retry_exceeded;
|
||||||
|
|
||||||
u8 reserved_5[24];
|
u8 reserved_5[24];
|
||||||
@ -130,4 +137,7 @@ void wl1271_event_mbox_config(struct wl1271 *wl);
|
|||||||
int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
|
int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
|
||||||
void wl1271_pspoll_work(struct work_struct *work);
|
void wl1271_pspoll_work(struct work_struct *work);
|
||||||
|
|
||||||
|
/* Functions from main.c */
|
||||||
|
bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -375,6 +375,10 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
ret = wl1271_acx_sta_max_tx_retry(wl);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = wl1271_acx_sta_mem_cfg(wl);
|
ret = wl1271_acx_sta_mem_cfg(wl);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
@ -441,7 +445,7 @@ static int wl1271_ap_hw_init(struct wl1271 *wl)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = wl1271_acx_max_tx_retry(wl);
|
ret = wl1271_acx_ap_max_tx_retry(wl);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -192,7 +192,8 @@ static struct conf_drv_settings default_conf = {
|
|||||||
.long_retry_limit = 10,
|
.long_retry_limit = 10,
|
||||||
.aflags = 0,
|
.aflags = 0,
|
||||||
},
|
},
|
||||||
.ap_max_tx_retries = 100,
|
.max_tx_retries = 100,
|
||||||
|
.ap_aging_period = 300,
|
||||||
.tid_conf_count = 4,
|
.tid_conf_count = 4,
|
||||||
.tid_conf = {
|
.tid_conf = {
|
||||||
[CONF_TX_AC_BE] = {
|
[CONF_TX_AC_BE] = {
|
||||||
@ -2953,6 +2954,12 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
|
|||||||
__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
|
__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid)
|
||||||
|
{
|
||||||
|
int id = hlid - WL1271_AP_STA_HLID_START;
|
||||||
|
return test_bit(id, wl->ap_hlid_map);
|
||||||
|
}
|
||||||
|
|
||||||
static int wl1271_op_sta_add(struct ieee80211_hw *hw,
|
static int wl1271_op_sta_add(struct ieee80211_hw *hw,
|
||||||
struct ieee80211_vif *vif,
|
struct ieee80211_vif *vif,
|
||||||
struct ieee80211_sta *sta)
|
struct ieee80211_sta *sta)
|
||||||
@ -3535,7 +3542,6 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
|
|||||||
IEEE80211_HW_HAS_RATE_CONTROL |
|
IEEE80211_HW_HAS_RATE_CONTROL |
|
||||||
IEEE80211_HW_CONNECTION_MONITOR |
|
IEEE80211_HW_CONNECTION_MONITOR |
|
||||||
IEEE80211_HW_SUPPORTS_CQM_RSSI |
|
IEEE80211_HW_SUPPORTS_CQM_RSSI |
|
||||||
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
|
|
||||||
IEEE80211_HW_AP_LINK_PS;
|
IEEE80211_HW_AP_LINK_PS;
|
||||||
|
|
||||||
wl->hw->wiphy->cipher_suites = cipher_suites;
|
wl->hw->wiphy->cipher_suites = cipher_suites;
|
||||||
|
@ -172,7 +172,6 @@ extern u32 wl12xx_debug_level;
|
|||||||
#define WL1271_PS_STA_MAX_BLOCKS (2 * 9)
|
#define WL1271_PS_STA_MAX_BLOCKS (2 * 9)
|
||||||
|
|
||||||
#define WL1271_AP_BSS_INDEX 0
|
#define WL1271_AP_BSS_INDEX 0
|
||||||
#define WL1271_AP_DEF_INACTIV_SEC 300
|
|
||||||
#define WL1271_AP_DEF_BEACON_EXP 20
|
#define WL1271_AP_DEF_BEACON_EXP 20
|
||||||
|
|
||||||
#define ACX_TX_DESCRIPTORS 32
|
#define ACX_TX_DESCRIPTORS 32
|
||||||
|
Loading…
Reference in New Issue
Block a user