mac80211 client U-APSD support
v3 cover mac80211 client U-APSD support
Hello,
here is my proposal how to add U-APSD client support to mac80211. I also included example driver implementation for wl1251.
Please comment. Also I would like to get comments how U-APSD needs to be implemented in different hardware.
v3:
o remove unnecessary debugfs interface, people can recompile mac80211 in case they want to try enable more queues
o add max SP length defines to ieee80211.h
o removed unnecessary code comments
v2:
o remove cfg80211 changes
o rename IEEE80211_QOS_IE to IEEE80211_WMM_IE_STA in ieee80211.h
---
Kalle Valo (2):
mac80211: add U-APSD client support
wl1251: add U-APSD support
drivers/net/wireless/wl12xx/wl1251_main.c | 12 +++++++-- include/linux/ieee80211.h | 12 +++++++++ include/net/mac80211.h | 6 ++++ net/mac80211/cfg.c | 7 +++++ net/mac80211/ieee80211_i.h | 2 + net/mac80211/mlme.c | 39 +++++++++++++++++++++++++---- net/mac80211/scan.c | 18 +++++++++++++ net/mac80211/util.c | 2 + 8 files changed, 90 insertions(+), 8 deletions(-)
PATCH v3 1/2 mac80211: add U-APSD client support
From: Kalle Valo <[email protected]>
Add Unscheduled Automatic Power-Save Delivery (U-APSD) client support. The idea is that the data frames from the client trigger AP to send the buffered frames with ACs which have U-APSD enabled. This decreases latency and makes it possible to save even more power.
Driver needs to use IEEE80211_HW_UAPSD to enable the feature. The current implementation assumes that firmware takes care of the wakeup and hardware needing IEEE80211_HW_PS_NULLFUNC_STACK is not yet supported.
Tested with wl1251 and Cisco Aironet 1200 AP.
--- include/linux/ieee80211.h | 12 ++++++++++++ include/net/mac80211.h | 6 ++++++ net/mac80211/cfg.c | 7 +++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 39 ++++++++++++++++++++++++++++++++++----- net/mac80211/scan.c | 18 ++++++++++++++++++ net/mac80211/util.c | 2 ++ 7 files changed, 81 insertions(+), 5 deletions(-)
include/linux/ieee80211.h +12 -0
@@ -120,6 +120,18 @@#define IEEE80211_QOS_CTL_TID_MASK 0x000F#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 +/* U-APSD queues for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_AC_VO (1<<0) +#define IEEE80211_WMM_IE_STA_AC_VI (1<<1) +#define IEEE80211_WMM_IE_STA_AC_BK (1<<2) +#define IEEE80211_WMM_IE_STA_AC_BE (1<<3) + +/* U-APSD max SP length for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_SP_ALL (0<<5) +#define IEEE80211_WMM_IE_STA_SP_2 (2<<5) +#define IEEE80211_WMM_IE_STA_SP_4 (1<<5) +#define IEEE80211_WMM_IE_STA_SP_6 (3<<5) +struct ieee80211_hdr {__le16 frame_control;__le16 duration_id;
include/net/mac80211.h +6 -0
@@ -113,6 +113,7 @@ struct ieee80211_tx_queue_params {u16 cw_min;u16 cw_max;u8 aifs; +bool uapsd; };/**
@@ -928,6 +929,10 @@ enum ieee80211_tkip_key_type {* @IEEE80211_HW_BEACON_FILTER: * Hardware supports dropping of irrelevant beacon frames to * avoid waking up cpu. +* +* @IEEE80211_HW_UAPSD: +* Hardware supports Unscheduled Automatic Power Save Delivery +* (U-APSD). */ enum ieee80211_hw_flags {IEEE80211_HW_HAS_RATE_CONTROL =1 <<0 ,
@@ -945,6 +950,7 @@ enum ieee80211_hw_flags {IEEE80211_HW_SUPPORTS_DYNAMIC_PS =1 <<12 ,IEEE80211_HW_MFP_CAPABLE =1 <<13 ,IEEE80211_HW_BEACON_FILTER =1 <<14 , + IEEE80211_HW_UAPSD = 1<<15, };/**
net/mac80211/cfg.c +7 -0
@@ -1134,6 +1134,13 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, p.cw_max = params->cwmax; p.cw_min = params->cwmin; p.txop = params->txop; + +/* +* Setting tx queue params disables u-apsd because it's only +* called in master mode. +*/ + p.uapsd = false; +if (drv_conf_tx (local, params->queue, &p)) { printk(KERN_DEBUG "%s: failed to set TX queue " "parameters for queue %d\n",
net/mac80211/ieee80211_i.h +2 -0
@@ -81,6 +81,7 @@ struct ieee80211_bss {u8 dtim_period;bool wmm_used; +bool uapsd_supported;unsigned long last_probe_resp;
@@ -264,6 +265,7 @@ enum ieee80211_sta_flags {IEEE80211_STA_DISABLE_11N =BIT (4 ),IEEE80211_STA_CSA_RECEIVED =BIT (5 ),IEEE80211_STA_MFP_ENABLED =BIT (6 ), + IEEE80211_STA_UAPSD_ENABLED = BIT(7), };/* flags for MLME request */
net/mac80211/mlme.c +34 -5
@@ -82,6 +82,8 @@ enum rx_mgmt_action { RX_MGMT_CFG80211_ASSOC_TO, }; +#define IEEE80211_DEFAULT_UAPSD_QUEUES IEEE80211_WMM_IE_STA_AC_VO +/* utils */ static inline void ASSERT_MGD_MTX (struct ieee80211_if_managed *ifmgd) {
@@ -235,13 +237,14 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,struct ieee80211_local *local = sdata->local;struct sk_buff *skb;struct ieee80211_mgmt *mgmt; -u8 *pos; +u8 *pos, qos_info;const u8 *ies, *ht_ie;int i, len, count, rates_len, supp_rates_len;u16 capab;int wmm =0 ;struct ieee80211_supported_band *sband;u32 rates =0 ; +bool uapsd; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + wk->ie_len +
@@ -268,6 +271,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, capab |=WLAN_CAPABILITY_PRIVACY ;if (wk->bss->wmm_used) wmm =1 ; + uapsd = wk->bss->uapsd_supported;/* get all rates supported by the device and the AP as * some APs don't like getting a superset of their rates
@@ -368,6 +372,15 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, }if (wmm && (ifmgd->flags &IEEE80211_STA_WMM_ENABLED )) { +if (uapsd && (local->hw.flags &IEEE80211_HW_UAPSD )) { + qos_info =IEEE80211_DEFAULT_UAPSD_QUEUES ; + qos_info |=IEEE80211_WMM_IE_STA_SP_ALL ; + ifmgd->flags |=IEEE80211_STA_UAPSD_ENABLED ; + }else { + qos_info =0 ; + ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED ; + } + pos =skb_put (skb,9 ); *pos++ =WLAN_EID_VENDOR_SPECIFIC ; *pos++ =7 ;/* len */
@@ -377,7 +390,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, *pos++ =2 ;/* WME */ *pos++ =0 ;/* WME info */ *pos++ =1 ;/* WME ver */ - *pos++ =0 ; + *pos++ = qos_info; }/* wmm support is a must to HT */
@@ -786,7 +799,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,struct ieee80211_tx_queue_params params;size_t left;int count; -u8 *pos; +u8 *pos, uapsd_queues =0 ;if (!(ifmgd->flags &IEEE80211_STA_WMM_ENABLED ))return ;
@@ -796,6 +809,10 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,if (wmm_param_len <8 || wmm_param[5 ]/* version */ !=1 )return ; + +if (ifmgd->flags &IEEE80211_STA_UAPSD_ENABLED ) + uapsd_queues =IEEE80211_DEFAULT_UAPSD_QUEUES ; + count = wmm_param[6 ] &0x0f ;if (count == ifmgd->wmm_last_param_set)return ;
@@ -810,6 +827,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,for (; left >=4 ; left -=4 , pos +=4 ) {int aci = (pos[0 ] >>5 ) &0x03 ;int acm = (pos[0 ] >>4 ) &0x01 ; +bool uapsd = false;int queue;switch (aci) {
@@ -817,22 +835,30 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, queue =3 ;if (acm) local->wmm_acm |=BIT (1 ) |BIT (2 );/* BK/- */ +if (uapsd_queues &IEEE80211_WMM_IE_STA_AC_BK ) + uapsd = true;break ;case 2 :/* AC_VI */ queue =1 ;if (acm) local->wmm_acm |=BIT (4 ) |BIT (5 );/* CL/VI */ +if (uapsd_queues &IEEE80211_WMM_IE_STA_AC_VI ) + uapsd = true;break ;case 3 :/* AC_VO */ queue =0 ;if (acm) local->wmm_acm |=BIT (6 ) |BIT (7 );/* VO/NC */ +if (uapsd_queues &IEEE80211_WMM_IE_STA_AC_VO ) + uapsd = true;break ;case 0 :/* AC_BE */ default : queue =2 ;if (acm) local->wmm_acm |=BIT (0 ) |BIT (3 );/* BE/EE */ +if (uapsd_queues &IEEE80211_WMM_IE_STA_AC_BE ) + uapsd = true;break ; }
@@ -840,11 +866,14 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, params.cw_max =ecw2cw ((pos[1 ] &0xf0 ) >>4 ); params.cw_min =ecw2cw (pos[1 ] &0x0f ); params.txop =get_unaligned_le16 (pos +2 ); + params.uapsd = uapsd; +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk (KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d " -"cWmin=%d cWmax=%d txop=%d\n" , +"cWmin=%d cWmax=%d txop=%d uapsd=%d\n" ,wiphy_name (local->hw.wiphy), queue, aci, acm, - params.aifs, params.cw_min, params.cw_max, params.txop); + params.aifs, params.cw_min, params.cw_max, params.txop, + params.uapsd);#endif if (drv_conf_tx (local, queue, ¶ms) && local->ops->conf_tx) printk(KERN_DEBUG "%s: failed to set TX queue "
net/mac80211/scan.c +18 -0
@@ -50,6 +50,23 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local,cfg80211_put_bss ((struct cfg80211_bss *)bss); } +static bool is_uapsd_supported (struct ieee802_11_elems *elems) +{ +u8 qos_info; + +if (elems->wmm_info && elems->wmm_info_len ==7 + && elems->wmm_info[5 ] ==1 ) + qos_info = elems->wmm_info[6 ]; +else if (elems->wmm_param && elems->wmm_param_len ==24 + && elems->wmm_param[5 ] ==1 ) + qos_info = elems->wmm_param[6 ]; +else +/* no valid wmm information or parameter element found */ +return false; + +return qos_info &0x80 ; +} +struct ieee80211_bss * ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status,
@@ -111,6 +128,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, } bss->wmm_used = elems->wmm_param || elems->wmm_info; + bss->uapsd_supported =is_uapsd_supported (elems);if (!beacon) bss->last_probe_resp = jiffies;
net/mac80211/util.c +2 -0
@@ -781,6 +781,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)break ; } + qparam.uapsd = false; +drv_conf_tx (local, queue, &qparam); } }
PATCH v3 2/2 wl1251: add U-APSD support
In reply to Kalle ValoFrom: Kalle Valo <[email protected]>
wl1251 firmware supports U-APSD just with a simple queue configuration change so enable it.
--- drivers/net/wireless/wl12xx/wl1251_main.c | 12 +++++++++--- 1 files changed, 9 insertions(+), 3 deletions(-)
drivers/net/wireless/wl12xx/wl1251_main.c +9 -3
@@ -1282,6 +1282,7 @@ static struct ieee80211_channel wl1251_channels[] = {static int wl1251_op_conf_tx (struct ieee80211_hw *hw,u16 queue,const struct ieee80211_tx_queue_params *params) { +enum wl1251_acx_ps_scheme ps_scheme;struct wl1251 *wl = hw->priv;int ret;
@@ -1299,10 +1300,14 @@ static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue,if (ret <0 ) goto out_sleep; +if (params->uapsd) + ps_scheme =WL1251_ACX_PS_SCHEME_UPSD_TRIGGER ; +else + ps_scheme =WL1251_ACX_PS_SCHEME_LEGACY ; + ret =wl1251_acx_tid_cfg (wl,wl1251_tx_get_queue (queue),CHANNEL_TYPE_EDCF , -wl1251_tx_get_queue (queue), -WL1251_ACX_PS_SCHEME_LEGACY , +wl1251_tx_get_queue (queue), ps_scheme,WL1251_ACX_ACK_POLICY_LEGACY );if (ret <0 ) goto out_sleep;
@@ -1376,7 +1381,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl) wl->hw->flags =IEEE80211_HW_SIGNAL_DBM |IEEE80211_HW_NOISE_DBM |IEEE80211_HW_SUPPORTS_PS | -IEEE80211_HW_BEACON_FILTER ; +IEEE80211_HW_BEACON_FILTER | +IEEE80211_HW_UAPSD ; wl->hw->wiphy->interface_modes =BIT (NL80211_IFTYPE_STATION ); wl->hw->wiphy->max_scan_ssids =1 ;