mac80211 client U-APSD support
v2 cover mac80211 client U-APSD support
Hello,
here is my suggestion how to add U-APSD client support to mac80211. I also included example driver implementation for wl1251.
Most probably I will drop patch 1 and fold patches 2 and 3, but I keep them all in for now to make it easier to change things.
Please comment. Also I would like to get comments how U-APSD needs to be implemented in different hardware.
v2:
o remove cfg80211 changes
o rename IEEE80211_QOS_IE to IEEE80211_WMM_IE_STA in ieee80211.h
---
Kalle Valo (3):
mac80211: add U-APSD client support
mac80211: add debugfs interface for U-APSD queue configuration
wl1251: add U-APSD support
drivers/net/wireless/wl12xx/wl1251_main.c | 12 ++++++-- include/linux/ieee80211.h | 5 ++++ include/net/mac80211.h | 6 ++++ net/mac80211/cfg.c | 7 +++++ net/mac80211/debugfs.c | 41 +++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 5 ++++ net/mac80211/main.c | 1 + net/mac80211/mlme.c | 36 ++++++++++++++++++++++--- net/mac80211/scan.c | 18 +++++++++++++ net/mac80211/util.c | 2 + 10 files changed, 125 insertions(+), 8 deletions(-)
PATCH v2 1/3 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 | 5 +++++ include/net/mac80211.h | 6 ++++++ net/mac80211/cfg.c | 7 +++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 40 +++++++++++++++++++++++++++++++++++----- net/mac80211/scan.c | 18 ++++++++++++++++++ net/mac80211/util.c | 2 ++ 7 files changed, 75 insertions(+), 5 deletions(-)
include/linux/ieee80211.h +5 -0
@@ -120,6 +120,11 @@#define IEEE80211_QOS_CTL_TID_MASK 0x000F#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 +#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) +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 +35 -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 )) { +/* FIXME: configure queues from debugfs */ + qos_info =IEEE80211_DEFAULT_UAPSD_QUEUES ; + 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;/* 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,11 @@ 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 ) +/* FIXME: use debugfs */ + uapsd_queues =IEEE80211_DEFAULT_UAPSD_QUEUES ; + count = wmm_param[6 ] &0x0f ;if (count == ifmgd->wmm_last_param_set)return ;
@@ -810,6 +828,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 +836,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 +867,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 v2 2/3 mac80211: add debugfs interface for U-APSD queue configuration
In reply to Kalle ValoFrom: Kalle Valo <[email protected]>
Because it's not yet decided how to configure which queues are U-APSD enabled, add a debugfs interface for testing purposes.
--- net/mac80211/debugfs.c | 41 +++++++++++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 3 +++ net/mac80211/main.c | 1 + net/mac80211/mlme.c | 8 ++------ 4 files changed, 47 insertions(+), 6 deletions(-)
net/mac80211/debugfs.c +41 -0
@@ -158,6 +158,47 @@ static const struct file_operations noack_ops = { .open = mac80211_open_file_generic }; +static ssize_t uapsd_queues_read (struct file *file,char __user *user_buf, +size_t count,loff_t *ppos) +{ +struct ieee80211_local *local = file->private_data; +int res; +char buf[10 ]; + + res =scnprintf (buf,sizeof (buf),"%d\n" , local->uapsd_queues); + +return simple_read_from_buffer (user_buf, count, ppos, buf, res); +} + +static ssize_t uapsd_queues_write (struct file *file, +const char __user *user_buf, +size_t count,loff_t *ppos) +{ +struct ieee80211_local *local = file->private_data; +unsigned long val; +char buf[10 ]; +size_t len; +int ret; + + len =min (count,sizeof (buf) -1 ); +if (copy_from_user (buf, user_buf, len)) +return -EFAULT ; + buf[len] ='\0' ; + + ret =strict_strtoul (buf,10 , &val); + +if (ret ==0 ) + local->uapsd_queues = val; + +return count; +} + +static const struct file_operations uapsd_queues_ops = { + .read = uapsd_queues_read, + .write = uapsd_queues_write, + .open = mac80211_open_file_generic +}; +static ssize_t queues_read (struct file *file,char __user *user_buf,size_t count,loff_t *ppos) {
net/mac80211/ieee80211_i.h +3 -0
@@ -58,6 +58,8 @@ struct ieee80211_local;#define TU_TO_EXP_TIME (x) (jiffies + usecs_to_jiffies((x) * 1024)) +#define IEEE80211_DEFAULT_UAPSD_QUEUES IEEE80211_WMM_IE_STA_AC_VO +struct ieee80211_fragment_entry {unsigned long first_frag_time;unsigned int seq;
@@ -746,6 +748,7 @@ struct ieee80211_local {*/ int wifi_wme_noack_test;unsigned int wmm_acm;/* bit field of ACM bits (BIT(802.1D tag)) */ +unsigned int uapsd_queues;bool pspolling;/*
net/mac80211/main.c +1 -0
@@ -358,6 +358,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; local->user_power_level =-1 ; + local->uapsd_queues =IEEE80211_DEFAULT_UAPSD_QUEUES ;INIT_LIST_HEAD (&local->interfaces);mutex_init (&local->iflist_mtx);
net/mac80211/mlme.c +2 -6
@@ -82,8 +82,6 @@ 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) {
@@ -373,8 +371,7 @@ 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 )) { -/* FIXME: configure queues from debugfs */ - qos_info =IEEE80211_DEFAULT_UAPSD_QUEUES ; + qos_info = local->uapsd_queues; ifmgd->flags |=IEEE80211_STA_UAPSD_ENABLED ; }else { qos_info =0 ;
@@ -811,8 +808,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,return ;if (ifmgd->flags &IEEE80211_STA_UAPSD_ENABLED ) -/* FIXME: use debugfs */ - uapsd_queues =IEEE80211_DEFAULT_UAPSD_QUEUES ; + uapsd_queues = local->uapsd_queues; count = wmm_param[6 ] &0x0f ;if (count == ifmgd->wmm_last_param_set)
PATCH v2 3/3 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 ;