diff options
author | Arik Nemtsov <arik@wizery.com> | 2011-02-23 00:22:31 +0200 |
---|---|---|
committer | Luciano Coelho <coelho@ti.com> | 2011-02-23 11:18:46 +0200 |
commit | b622d992c21a85ce590afe2c18977ed28b457e0e (patch) | |
tree | b5a5552141932de9628cc569d2f2dee4df9870dc /drivers/net/wireless/wl12xx/main.c | |
parent | 09039f42a24084c10e7761ab28ef22932c62a46f (diff) |
wl12xx: AP-mode - management of links in PS-mode
Update the PS mode of each link according to a bitmap polled from
fw_status. Manually notify mac80211 about PS mode changes in connected
stations.
mac80211 will only be notified about PS start when the station is in PS
and there is a small number of TX blocks from this link ready in HW.
This is required for waking up the remote station since the TIM is
updated entirely by FW.
When a station enters mac80211-PS-mode, we drop all the skbs in the
low-level TX queues belonging to this sta with STAT_TX_FILTERED
to keep our queues clean.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
Diffstat (limited to 'drivers/net/wireless/wl12xx/main.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/main.c | 73 |
1 files changed, 62 insertions, 11 deletions
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 5772a33d79e..95aa19ae84e 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -537,6 +537,57 @@ static int wl1271_plt_init(struct wl1271 *wl) return ret; } +static void wl1271_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_blks) +{ + bool fw_ps; + + /* only regulate station links */ + if (hlid < WL1271_AP_STA_HLID_START) + return; + + fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + + /* + * Wake up from high level PS if the STA is asleep with too little + * blocks in FW or if the STA is awake. + */ + if (!fw_ps || tx_blks < WL1271_PS_STA_MAX_BLOCKS) + wl1271_ps_link_end(wl, hlid); + + /* Start high-level PS if the STA is asleep with enough blocks in FW */ + else if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS) + wl1271_ps_link_start(wl, hlid, true); +} + +static void wl1271_irq_update_links_status(struct wl1271 *wl, + struct wl1271_fw_ap_status *status) +{ + u32 cur_fw_ps_map; + u8 hlid; + + cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap); + if (wl->ap_fw_ps_map != cur_fw_ps_map) { + wl1271_debug(DEBUG_PSM, + "link ps prev 0x%x cur 0x%x changed 0x%x", + wl->ap_fw_ps_map, cur_fw_ps_map, + wl->ap_fw_ps_map ^ cur_fw_ps_map); + + wl->ap_fw_ps_map = cur_fw_ps_map; + } + + for (hlid = WL1271_AP_STA_HLID_START; hlid < AP_MAX_LINKS; hlid++) { + u8 cnt = status->tx_lnk_free_blks[hlid] - + wl->links[hlid].prev_freed_blks; + + wl->links[hlid].prev_freed_blks = + status->tx_lnk_free_blks[hlid]; + wl->links[hlid].allocated_blks -= cnt; + + wl1271_irq_ps_regulate_link(wl, hlid, + wl->links[hlid].allocated_blks); + } +} + static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_full_status *full_status) { @@ -574,16 +625,9 @@ static void wl1271_fw_status(struct wl1271 *wl, if (total) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); - if (wl->bss_type == BSS_TYPE_AP_BSS) { - for (i = 0; i < AP_MAX_LINKS; i++) { - u8 cnt = status->tx_lnk_free_blks[i] - - wl->links[i].prev_freed_blks; - - wl->links[i].prev_freed_blks = - status->tx_lnk_free_blks[i]; - wl->links[i].allocated_blks -= cnt; - } - } + /* for AP update num of allocated TX blocks per link and ps status */ + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl1271_irq_update_links_status(wl, &full_status->ap); /* update the host-chipset time offset */ getnstimeofday(&ts); @@ -1241,6 +1285,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->filters = 0; wl1271_free_ap_keys(wl); memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); + wl->ap_fw_ps_map = 0; + wl->ap_ps_map = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -2649,10 +2695,10 @@ static int wl1271_allocate_sta(struct wl1271 *wl, } wl_sta = (struct wl1271_station *)sta->drv_priv; - __set_bit(id, wl->ap_hlid_map); wl_sta->hlid = WL1271_AP_STA_HLID_START + id; *hlid = wl_sta->hlid; + memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN); return 0; } @@ -2664,7 +2710,10 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) return; __clear_bit(id, wl->ap_hlid_map); + memset(wl->links[hlid].addr, 0, ETH_ALEN); wl1271_tx_reset_link_queues(wl, hlid); + __clear_bit(hlid, &wl->ap_ps_map); + __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); } static int wl1271_op_sta_add(struct ieee80211_hw *hw, @@ -3355,6 +3404,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->set_bss_type = MAX_BSS_TYPE; wl->fw_bss_type = MAX_BSS_TYPE; wl->last_tx_hlid = 0; + wl->ap_ps_map = 0; + wl->ap_fw_ps_map = 0; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) |