Loading drivers/net/wireless/wl12xx/acx.c +26 −2 Original line number Diff line number Diff line Loading @@ -1328,10 +1328,9 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, /* get data from A-MPDU parameters field */ acx->ampdu_max_length = ht_cap->ampdu_factor; acx->ampdu_min_spacing = ht_cap->ampdu_density; memcpy(acx->mac_address, mac_address, ETH_ALEN); } memcpy(acx->mac_address, mac_address, ETH_ALEN); acx->ht_capabilites = cpu_to_le32(ht_capabilites); ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx)); Loading Loading @@ -1542,3 +1541,28 @@ int wl1271_acx_config_ps(struct wl1271 *wl) kfree(config_ps); return ret; } int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr) { struct wl1271_acx_inconnection_sta *acx = NULL; int ret; wl1271_debug(DEBUG_ACX, "acx set inconnaction sta %pM", addr); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) return -ENOMEM; memcpy(acx->addr, addr, ETH_ALEN); ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx set inconnaction sta failed: %d", ret); goto out; } out: kfree(acx); return ret; } drivers/net/wireless/wl12xx/acx.h +9 −0 Original line number Diff line number Diff line Loading @@ -1155,6 +1155,13 @@ struct wl1271_acx_config_ps { __le32 null_data_rate; } __packed; struct wl1271_acx_inconnection_sta { struct acx_header header; u8 addr[ETH_ALEN]; u8 padding1[2]; } __packed; enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, Loading Loading @@ -1215,6 +1222,7 @@ enum { 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, Loading Loading @@ -1290,5 +1298,6 @@ int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_max_tx_retry(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); #endif /* __WL1271_ACX_H__ */ drivers/net/wireless/wl12xx/init.c +1 −1 Original line number Diff line number Diff line Loading @@ -483,7 +483,7 @@ static void wl1271_check_ba_support(struct wl1271 *wl) static int wl1271_set_ba_policies(struct wl1271 *wl) { u8 tid_index; u8 ret = 0; int ret = 0; /* Reset the BA RX indicators */ wl->ba_rx_bitmap = 0; Loading drivers/net/wireless/wl12xx/main.c +118 −29 Original line number Diff line number Diff line Loading @@ -482,6 +482,10 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; ret = wl1271_acx_sta_mem_cfg(wl); if (ret < 0) goto out_free_memmap; /* Default fragmentation threshold */ ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold); if (ret < 0) Loading Loading @@ -533,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) { Loading Loading @@ -570,6 +625,10 @@ static void wl1271_fw_status(struct wl1271 *wl, if (total) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); /* 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); wl->time_offset = (timespec_to_ns(&ts) >> 10) - Loading Loading @@ -980,14 +1039,32 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) struct wl1271 *wl = hw->priv; unsigned long flags; int q; u8 hlid = 0; spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count++; /* * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues"); ieee80211_stop_queues(wl->hw); set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); } spin_unlock_irqrestore(&wl->wl_lock, flags); /* queue the packet */ q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); if (wl->bss_type == BSS_TYPE_AP_BSS) { hlid = wl1271_tx_get_hlid(skb); wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q); skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); } else { skb_queue_tail(&wl->tx_queue[q], skb); } /* * The chip specific setup must run before the first TX packet - Loading @@ -997,19 +1074,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) ieee80211_queue_work(wl->hw, &wl->tx_work); /* * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues"); spin_lock_irqsave(&wl->wl_lock, flags); ieee80211_stop_queues(wl->hw); set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); spin_unlock_irqrestore(&wl->wl_lock, flags); } return NETDEV_TX_OK; } Loading Loading @@ -1221,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; Loading Loading @@ -2218,6 +2284,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, u32 sta_rate_set = 0; int ret; struct ieee80211_sta *sta; bool sta_exists = false; struct ieee80211_sta_ht_cap sta_ht_cap; if (is_ibss) { ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, Loading Loading @@ -2289,16 +2357,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (sta->ht_cap.ht_supported) sta_rate_set |= (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET); sta_ht_cap = sta->ht_cap; sta_exists = true; } rcu_read_unlock(); if (sta_exists) { /* handle new association with HT and HT information change */ if ((changed & BSS_CHANGED_HT) && (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, true); if (ret < 0) { wl1271_warning("Set ht cap true failed %d", ret); rcu_read_unlock(); goto out; } ret = wl1271_acx_set_ht_information(wl, Loading @@ -2306,23 +2378,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (ret < 0) { wl1271_warning("Set ht information failed %d", ret); rcu_read_unlock(); goto out; } } /* handle new association without HT and disassociation */ else if (changed & BSS_CHANGED_ASSOC) { ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, false); if (ret < 0) { wl1271_warning("Set ht cap false failed %d", ret); rcu_read_unlock(); goto out; } } } rcu_read_unlock(); if ((changed & BSS_CHANGED_ASSOC)) { if (bss_conf->assoc) { Loading Loading @@ -2612,7 +2681,7 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx, return 0; } static int wl1271_allocate_hlid(struct wl1271 *wl, static int wl1271_allocate_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 *hlid) { Loading @@ -2626,18 +2695,25 @@ static int wl1271_allocate_hlid(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; } static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid) static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) { int id = hlid - WL1271_AP_STA_HLID_START; if (WARN_ON(!test_bit(id, wl->ap_hlid_map))) 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, Loading @@ -2658,13 +2734,13 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw, wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid); ret = wl1271_allocate_hlid(wl, sta, &hlid); ret = wl1271_allocate_sta(wl, sta, &hlid); if (ret < 0) goto out; ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; goto out_free_sta; ret = wl1271_cmd_add_sta(wl, sta, hlid); if (ret < 0) Loading @@ -2673,6 +2749,10 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw, out_sleep: wl1271_ps_elp_sleep(wl); out_free_sta: if (ret < 0) wl1271_free_sta(wl, hlid); out: mutex_unlock(&wl->mutex); return ret; Loading Loading @@ -2709,7 +2789,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; wl1271_free_hlid(wl, wl_sta->hlid); wl1271_free_sta(wl, wl_sta->hlid); out_sleep: wl1271_ps_elp_sleep(wl); Loading Loading @@ -3212,7 +3292,9 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | 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; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); Loading Loading @@ -3264,7 +3346,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) struct ieee80211_hw *hw; struct platform_device *plat_dev = NULL; struct wl1271 *wl; int i, ret; int i, j, ret; unsigned int order; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); Loading Loading @@ -3292,6 +3374,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void) for (i = 0; i < NUM_TX_QUEUES; i++) skb_queue_head_init(&wl->tx_queue[i]); for (i = 0; i < NUM_TX_QUEUES; i++) for (j = 0; j < AP_MAX_LINKS; j++) skb_queue_head_init(&wl->links[j].tx_queue[i]); INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work); INIT_WORK(&wl->irq_work, wl1271_irq_work); Loading @@ -3317,6 +3403,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->bss_type = MAX_BSS_TYPE; 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++) Loading Loading @@ -3412,5 +3501,5 @@ module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); drivers/net/wireless/wl12xx/ps.c +78 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include "reg.h" #include "ps.h" #include "io.h" #include "tx.h" #define WL1271_WAKEUP_TIMEOUT 500 Loading Loading @@ -173,4 +174,81 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, return ret; } static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) { int i, filtered = 0; struct sk_buff *skb; struct ieee80211_tx_info *info; unsigned long flags; /* filter all frames currently the low level queus for this hlid */ for (i = 0; i < NUM_TX_QUEUES; i++) { while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { info = IEEE80211_SKB_CB(skb); info->flags |= IEEE80211_TX_STAT_TX_FILTERED; info->status.rates[0].idx = -1; ieee80211_tx_status(wl->hw, skb); filtered++; } } spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count -= filtered; spin_unlock_irqrestore(&wl->wl_lock, flags); wl1271_handle_tx_low_watermark(wl); } void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues) { struct ieee80211_sta *sta; if (test_bit(hlid, &wl->ap_ps_map)) return; wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d blks %d " "clean_queues %d", hlid, wl->links[hlid].allocated_blks, clean_queues); rcu_read_lock(); sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); if (!sta) { wl1271_error("could not find sta %pM for starting ps", wl->links[hlid].addr); rcu_read_unlock(); return; } ieee80211_sta_ps_transition_ni(sta, true); rcu_read_unlock(); /* do we want to filter all frames from this link's queues? */ if (clean_queues) wl1271_ps_filter_frames(wl, hlid); __set_bit(hlid, &wl->ap_ps_map); } void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid) { struct ieee80211_sta *sta; if (!test_bit(hlid, &wl->ap_ps_map)) return; wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid); __clear_bit(hlid, &wl->ap_ps_map); rcu_read_lock(); sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); if (!sta) { wl1271_error("could not find sta %pM for ending ps", wl->links[hlid].addr); goto end; } ieee80211_sta_ps_transition_ni(sta, false); end: rcu_read_unlock(); } Loading
drivers/net/wireless/wl12xx/acx.c +26 −2 Original line number Diff line number Diff line Loading @@ -1328,10 +1328,9 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, /* get data from A-MPDU parameters field */ acx->ampdu_max_length = ht_cap->ampdu_factor; acx->ampdu_min_spacing = ht_cap->ampdu_density; memcpy(acx->mac_address, mac_address, ETH_ALEN); } memcpy(acx->mac_address, mac_address, ETH_ALEN); acx->ht_capabilites = cpu_to_le32(ht_capabilites); ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx)); Loading Loading @@ -1542,3 +1541,28 @@ int wl1271_acx_config_ps(struct wl1271 *wl) kfree(config_ps); return ret; } int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr) { struct wl1271_acx_inconnection_sta *acx = NULL; int ret; wl1271_debug(DEBUG_ACX, "acx set inconnaction sta %pM", addr); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) return -ENOMEM; memcpy(acx->addr, addr, ETH_ALEN); ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("acx set inconnaction sta failed: %d", ret); goto out; } out: kfree(acx); return ret; }
drivers/net/wireless/wl12xx/acx.h +9 −0 Original line number Diff line number Diff line Loading @@ -1155,6 +1155,13 @@ struct wl1271_acx_config_ps { __le32 null_data_rate; } __packed; struct wl1271_acx_inconnection_sta { struct acx_header header; u8 addr[ETH_ALEN]; u8 padding1[2]; } __packed; enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, Loading Loading @@ -1215,6 +1222,7 @@ enum { 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, Loading Loading @@ -1290,5 +1298,6 @@ int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_max_tx_retry(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); #endif /* __WL1271_ACX_H__ */
drivers/net/wireless/wl12xx/init.c +1 −1 Original line number Diff line number Diff line Loading @@ -483,7 +483,7 @@ static void wl1271_check_ba_support(struct wl1271 *wl) static int wl1271_set_ba_policies(struct wl1271 *wl) { u8 tid_index; u8 ret = 0; int ret = 0; /* Reset the BA RX indicators */ wl->ba_rx_bitmap = 0; Loading
drivers/net/wireless/wl12xx/main.c +118 −29 Original line number Diff line number Diff line Loading @@ -482,6 +482,10 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; ret = wl1271_acx_sta_mem_cfg(wl); if (ret < 0) goto out_free_memmap; /* Default fragmentation threshold */ ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold); if (ret < 0) Loading Loading @@ -533,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) { Loading Loading @@ -570,6 +625,10 @@ static void wl1271_fw_status(struct wl1271 *wl, if (total) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); /* 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); wl->time_offset = (timespec_to_ns(&ts) >> 10) - Loading Loading @@ -980,14 +1039,32 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) struct wl1271 *wl = hw->priv; unsigned long flags; int q; u8 hlid = 0; spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count++; /* * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues"); ieee80211_stop_queues(wl->hw); set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); } spin_unlock_irqrestore(&wl->wl_lock, flags); /* queue the packet */ q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); if (wl->bss_type == BSS_TYPE_AP_BSS) { hlid = wl1271_tx_get_hlid(skb); wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q); skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); } else { skb_queue_tail(&wl->tx_queue[q], skb); } /* * The chip specific setup must run before the first TX packet - Loading @@ -997,19 +1074,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) ieee80211_queue_work(wl->hw, &wl->tx_work); /* * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues"); spin_lock_irqsave(&wl->wl_lock, flags); ieee80211_stop_queues(wl->hw); set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); spin_unlock_irqrestore(&wl->wl_lock, flags); } return NETDEV_TX_OK; } Loading Loading @@ -1221,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; Loading Loading @@ -2218,6 +2284,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, u32 sta_rate_set = 0; int ret; struct ieee80211_sta *sta; bool sta_exists = false; struct ieee80211_sta_ht_cap sta_ht_cap; if (is_ibss) { ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, Loading Loading @@ -2289,16 +2357,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (sta->ht_cap.ht_supported) sta_rate_set |= (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET); sta_ht_cap = sta->ht_cap; sta_exists = true; } rcu_read_unlock(); if (sta_exists) { /* handle new association with HT and HT information change */ if ((changed & BSS_CHANGED_HT) && (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, true); if (ret < 0) { wl1271_warning("Set ht cap true failed %d", ret); rcu_read_unlock(); goto out; } ret = wl1271_acx_set_ht_information(wl, Loading @@ -2306,23 +2378,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (ret < 0) { wl1271_warning("Set ht information failed %d", ret); rcu_read_unlock(); goto out; } } /* handle new association without HT and disassociation */ else if (changed & BSS_CHANGED_ASSOC) { ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, false); if (ret < 0) { wl1271_warning("Set ht cap false failed %d", ret); rcu_read_unlock(); goto out; } } } rcu_read_unlock(); if ((changed & BSS_CHANGED_ASSOC)) { if (bss_conf->assoc) { Loading Loading @@ -2612,7 +2681,7 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx, return 0; } static int wl1271_allocate_hlid(struct wl1271 *wl, static int wl1271_allocate_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 *hlid) { Loading @@ -2626,18 +2695,25 @@ static int wl1271_allocate_hlid(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; } static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid) static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) { int id = hlid - WL1271_AP_STA_HLID_START; if (WARN_ON(!test_bit(id, wl->ap_hlid_map))) 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, Loading @@ -2658,13 +2734,13 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw, wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid); ret = wl1271_allocate_hlid(wl, sta, &hlid); ret = wl1271_allocate_sta(wl, sta, &hlid); if (ret < 0) goto out; ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; goto out_free_sta; ret = wl1271_cmd_add_sta(wl, sta, hlid); if (ret < 0) Loading @@ -2673,6 +2749,10 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw, out_sleep: wl1271_ps_elp_sleep(wl); out_free_sta: if (ret < 0) wl1271_free_sta(wl, hlid); out: mutex_unlock(&wl->mutex); return ret; Loading Loading @@ -2709,7 +2789,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; wl1271_free_hlid(wl, wl_sta->hlid); wl1271_free_sta(wl, wl_sta->hlid); out_sleep: wl1271_ps_elp_sleep(wl); Loading Loading @@ -3212,7 +3292,9 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | 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; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); Loading Loading @@ -3264,7 +3346,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) struct ieee80211_hw *hw; struct platform_device *plat_dev = NULL; struct wl1271 *wl; int i, ret; int i, j, ret; unsigned int order; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); Loading Loading @@ -3292,6 +3374,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void) for (i = 0; i < NUM_TX_QUEUES; i++) skb_queue_head_init(&wl->tx_queue[i]); for (i = 0; i < NUM_TX_QUEUES; i++) for (j = 0; j < AP_MAX_LINKS; j++) skb_queue_head_init(&wl->links[j].tx_queue[i]); INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work); INIT_WORK(&wl->irq_work, wl1271_irq_work); Loading @@ -3317,6 +3403,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->bss_type = MAX_BSS_TYPE; 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++) Loading Loading @@ -3412,5 +3501,5 @@ module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
drivers/net/wireless/wl12xx/ps.c +78 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include "reg.h" #include "ps.h" #include "io.h" #include "tx.h" #define WL1271_WAKEUP_TIMEOUT 500 Loading Loading @@ -173,4 +174,81 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, return ret; } static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) { int i, filtered = 0; struct sk_buff *skb; struct ieee80211_tx_info *info; unsigned long flags; /* filter all frames currently the low level queus for this hlid */ for (i = 0; i < NUM_TX_QUEUES; i++) { while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { info = IEEE80211_SKB_CB(skb); info->flags |= IEEE80211_TX_STAT_TX_FILTERED; info->status.rates[0].idx = -1; ieee80211_tx_status(wl->hw, skb); filtered++; } } spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count -= filtered; spin_unlock_irqrestore(&wl->wl_lock, flags); wl1271_handle_tx_low_watermark(wl); } void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues) { struct ieee80211_sta *sta; if (test_bit(hlid, &wl->ap_ps_map)) return; wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d blks %d " "clean_queues %d", hlid, wl->links[hlid].allocated_blks, clean_queues); rcu_read_lock(); sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); if (!sta) { wl1271_error("could not find sta %pM for starting ps", wl->links[hlid].addr); rcu_read_unlock(); return; } ieee80211_sta_ps_transition_ni(sta, true); rcu_read_unlock(); /* do we want to filter all frames from this link's queues? */ if (clean_queues) wl1271_ps_filter_frames(wl, hlid); __set_bit(hlid, &wl->ap_ps_map); } void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid) { struct ieee80211_sta *sta; if (!test_bit(hlid, &wl->ap_ps_map)) return; wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid); __clear_bit(hlid, &wl->ap_ps_map); rcu_read_lock(); sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); if (!sta) { wl1271_error("could not find sta %pM for ending ps", wl->links[hlid].addr); goto end; } ieee80211_sta_ps_transition_ni(sta, false); end: rcu_read_unlock(); }