2010-01-12 08:43:05

by Kalle Valo

[permalink] [raw]
Subject: [PATCH PATCH v6 0/7] 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.

Patches should be ready now.

v6:
o store max SP length value in unconverted format and shift is just
before use (suggested by Johannes)

v5:
o rename IEEE80211_HW_UAPSD to IEEE80211_HW_SUPPORTS_UAPSD
o U-APSD queue and max SP length configuration debugfs interface
(requested by Jason)
o dynamic ps support for U-APSD
o by default enable U-APSD in all queues
o back to RFC state due to new code
o add a warning if both PS_NULLFUNC_STACK and U-APSD are enabled
o wl1251: set qos null data template
o more defines to ieee80211.h

v4:
o use IEEE80211_WMM_IE_STA_QOSINFO prefix in ieee80211.h (Jason Young)
o fix VI and BK qosinfo values in ieee80211.h (Jason Young)
o add IEEE80211_WMM_IE_AP_QOSINFO_UAPSD

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 (7):
mac80211: add U-APSD client support
mac80211: add debugfs interface for U-APSD queue configuration
mac80211: create tx handler for dynamic ps
mac80211: check uapsd state for dynamic power save
ieee80211: add struct ieee80211_hdr_qos
wl1251: create qos null data template
wl1251: add U-APSD support


drivers/net/wireless/wl12xx/wl1251_main.c | 37 +++++++++-
include/linux/ieee80211.h | 28 ++++++++
include/net/mac80211.h | 7 ++
net/mac80211/cfg.c | 7 ++
net/mac80211/debugfs.c | 94 ++++++++++++++++++++++++++
net/mac80211/ieee80211_i.h | 27 +++++++
net/mac80211/main.c | 6 ++
net/mac80211/mlme.c | 31 ++++++++
net/mac80211/scan.c | 18 +++++
net/mac80211/tx.c | 106 ++++++++++++++++++-----------
net/mac80211/util.c | 2 +
net/mac80211/work.c | 12 +++
12 files changed, 326 insertions(+), 49 deletions(-)



2010-01-12 11:14:22

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH PATCH v6 1/7] mac80211: add U-APSD client support

Johannes Berg <[email protected]> writes:

> On Tue, 2010-01-12 at 10:42 +0200, Kalle Valo wrote:
>
>> + *
>> + * @IEEE80211_HW_SUPPORTS_UAPSD:
>> + * Hardware supports Unscheduled Automatic Power Save Delivery
>> + * (U-APSD) in managed mode. The mode is configured with
>> + * conf_tx() operation.
>
> I think a little more detail would be good here, for example adding that
> it must support staying awake with just the frame triggering it, and
> that it won't be asked to wake up by mac80211?

Good point. I was planning to improve power save documentation, I do
that in this patchset and I'll also add more documentation about
U-APSD. That should help to understand this more.

> Or will it, actually? We've had that discussion with PS-poll so ...

Yes, HW must stay awake until end of service period. I'll write more
documentation to clear up all this.

--
Kalle Valo

2010-01-12 08:44:06

by Kalle Valo

[permalink] [raw]
Subject: [PATCH PATCH v6 7/7] wl1251: add U-APSD support

From: Kalle Valo <[email protected]>

wl1251 firmware supports U-APSD just with a simple queue configuration
change so enable it.

Signed-off-by: Kalle Valo <[email protected]>
---
drivers/net/wireless/wl12xx/wl1251_main.c | 12 +++++++++---
1 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index 4cf3545..6be50aa 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -1307,6 +1307,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;

@@ -1324,10 +1325,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;
@@ -1401,7 +1406,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_SUPPORTS_UAPSD;

wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
wl->hw->wiphy->max_scan_ssids = 1;


2010-01-12 08:43:31

by Kalle Valo

[permalink] [raw]
Subject: [PATCH PATCH v6 6/7] wl1251: create qos null data template

From: Kalle Valo <[email protected]>

The qos null data template is needed for U-APSD.

Signed-off-by: Kalle Valo <[email protected]>
---
drivers/net/wireless/wl12xx/wl1251_main.c | 25 +++++++++++++++++++++++++
1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index 4e373f3..4cf3545 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -585,6 +585,27 @@ static int wl1251_build_null_data(struct wl1251 *wl)

}

+static int wl1251_build_qos_null_data(struct wl1251 *wl)
+{
+ struct ieee80211_qos_hdr template;
+
+ memset(&template, 0, sizeof(template));
+
+ memcpy(template.addr1, wl->bssid, ETH_ALEN);
+ memcpy(template.addr2, wl->mac_addr, ETH_ALEN);
+ memcpy(template.addr3, wl->bssid, ETH_ALEN);
+
+ template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_NULLFUNC |
+ IEEE80211_FCTL_TODS);
+
+ /* FIXME: not sure what priority to use here */
+ template.qos_ctrl = cpu_to_le16(0);
+
+ return wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, &template,
+ sizeof(template));
+}
+
static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid)
{
struct wl12xx_ps_poll_template template;
@@ -1119,6 +1140,10 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
if (ret < 0)
goto out;

+ ret = wl1251_build_qos_null_data(wl);
+ if (ret < 0)
+ goto out;
+
if (wl->bss_type != BSS_TYPE_IBSS) {
ret = wl1251_join(wl, wl->bss_type, wl->channel,
wl->beacon_int, wl->dtim_period);


2010-01-12 08:43:27

by Kalle Valo

[permalink] [raw]
Subject: [PATCH PATCH v6 5/7] ieee80211: add struct ieee80211_hdr_qos

From: Kalle Valo <[email protected]>

The header can be used to create qos nullfunc frames, for example.

Signed-off-by: Kalle Valo <[email protected]>
---
include/linux/ieee80211.h | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index b2b41f8..a602704 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -148,6 +148,16 @@ struct ieee80211_hdr {
u8 addr4[6];
} __attribute__ ((packed));

+struct ieee80211_qos_hdr {
+ __le16 frame_control;
+ __le16 duration_id;
+ u8 addr1[6];
+ u8 addr2[6];
+ u8 addr3[6];
+ __le16 seq_ctrl;
+ __le16 qos_ctrl;
+} __attribute__ ((packed));
+
/**
* ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set
* @fc: frame control bytes in little-endian byteorder


2010-01-12 08:43:22

by Kalle Valo

[permalink] [raw]
Subject: [PATCH PATCH v6 3/7] mac80211: create tx handler for dynamic ps

From: Kalle Valo <[email protected]>

Currently dynamic ps check is in ieee80211_xmit(), but it's cleaner
to have a separate tx handler for this. Also this is a prerequisite for
U-APSD client mode which needs to know the queue frame is in.

Also need_dynamic_ps() function is embedded to the tx handler.

No functional changes expect that the code is run in a later phase than
originally.

Signed-off-by: Kalle Valo <[email protected]>
---
net/mac80211/tx.c | 81 +++++++++++++++++++++++++++--------------------------
1 files changed, 41 insertions(+), 40 deletions(-)

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 140da4a..cf846a3 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -180,6 +180,46 @@ static int inline is_ieee80211_device(struct ieee80211_local *local,
}

/* tx handlers */
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
+{
+ struct ieee80211_local *local = tx->local;
+
+ /* driver doesn't support power save */
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
+ return TX_CONTINUE;
+
+ /* hardware does dynamic power save */
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
+ return TX_CONTINUE;
+
+ /* dynamic power save disabled */
+ if (local->hw.conf.dynamic_ps_timeout <= 0)
+ return TX_CONTINUE;
+
+ /* we are scanning, don't enable power save */
+ if (local->scanning)
+ return TX_CONTINUE;
+
+ if (!local->ps_sdata)
+ return TX_CONTINUE;
+
+ /* No point if we're going to suspend */
+ if (local->quiescing)
+ return TX_CONTINUE;
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ ieee80211_stop_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_PS);
+ ieee80211_queue_work(&local->hw,
+ &local->dynamic_ps_disable_work);
+ }
+
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
+
+ return TX_CONTINUE;
+}

static ieee80211_tx_result debug_noinline
ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
@@ -1215,6 +1255,7 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
goto txh_done; \
} while (0)

+ CALL_TXH(ieee80211_tx_h_dynamic_ps);
CALL_TXH(ieee80211_tx_h_check_assoc);
CALL_TXH(ieee80211_tx_h_ps_buf);
CALL_TXH(ieee80211_tx_h_select_key);
@@ -1397,34 +1438,6 @@ static int ieee80211_skb_resize(struct ieee80211_local *local,
return 0;
}

-static bool need_dynamic_ps(struct ieee80211_local *local)
-{
- /* driver doesn't support power save */
- if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
- return false;
-
- /* hardware does dynamic power save */
- if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
- return false;
-
- /* dynamic power save disabled */
- if (local->hw.conf.dynamic_ps_timeout <= 0)
- return false;
-
- /* we are scanning, don't enable power save */
- if (local->scanning)
- return false;
-
- if (!local->ps_sdata)
- return false;
-
- /* No point if we're going to suspend */
- if (local->quiescing)
- return false;
-
- return true;
-}
-
static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
@@ -1435,18 +1448,6 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
int headroom;
bool may_encrypt;

- if (need_dynamic_ps(local)) {
- if (local->hw.conf.flags & IEEE80211_CONF_PS) {
- ieee80211_stop_queues_by_reason(&local->hw,
- IEEE80211_QUEUE_STOP_REASON_PS);
- ieee80211_queue_work(&local->hw,
- &local->dynamic_ps_disable_work);
- }
-
- mod_timer(&local->dynamic_ps_timer, jiffies +
- msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
- }
-
rcu_read_lock();

if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) {


2010-01-12 08:43:22

by Kalle Valo

[permalink] [raw]
Subject: [PATCH PATCH v6 1/7] 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 on a Nokia N900 and Cisco Aironet 1231G AP and running
various test traffic with ping.

Signed-off-by: Kalle Valo <[email protected]>
---
include/linux/ieee80211.h | 18 ++++++++++++++++++
include/net/mac80211.h | 7 +++++++
net/mac80211/cfg.c | 7 +++++++
net/mac80211/ieee80211_i.h | 13 ++++++++++++-
net/mac80211/main.c | 4 ++++
net/mac80211/mlme.c | 31 ++++++++++++++++++++++++++++---
net/mac80211/scan.c | 18 ++++++++++++++++++
net/mac80211/util.c | 2 ++
net/mac80211/work.c | 12 ++++++++++--
9 files changed, 106 insertions(+), 6 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index aeea282..b2b41f8 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -120,6 +120,24 @@
#define IEEE80211_QOS_CTL_TID_MASK 0x000F
#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007

+/* U-APSD queue for WMM IEs sent by AP */
+#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7)
+
+/* U-APSD queues for WMM IEs sent by STA */
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO (1<<0)
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI (1<<1)
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK (1<<2)
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE (1<<3)
+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK 0x0f
+
+/* U-APSD max SP length for WMM IEs sent by STA */
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0x00
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_2 0x01
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03
+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5
+
struct ieee80211_hdr {
__le16 frame_control;
__le16 duration_id;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index f073a2a..f27cb4d 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -113,6 +113,7 @@ struct ieee80211_tx_queue_params {
u16 cw_min;
u16 cw_max;
u8 aifs;
+ bool uapsd;
};

/**
@@ -933,6 +934,11 @@ enum ieee80211_tkip_key_type {
* Hardware supports dynamic spatial multiplexing powersave,
* ie. can turn off all but one chain and then wake the rest
* up as required after, for example, rts/cts handshake.
+ *
+ * @IEEE80211_HW_SUPPORTS_UAPSD:
+ * Hardware supports Unscheduled Automatic Power Save Delivery
+ * (U-APSD) in managed mode. The mode is configured with
+ * conf_tx() operation.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -952,6 +958,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_BEACON_FILTER = 1<<14,
IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
+ IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
};

/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 2e5e841..0a432c4 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1128,6 +1128,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",
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a27921e..165de3d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -58,6 +58,15 @@ struct ieee80211_local;

#define TU_TO_EXP_TIME(x) (jiffies + usecs_to_jiffies((x) * 1024))

+#define IEEE80211_DEFAULT_UAPSD_QUEUES \
+ (IEEE80211_WMM_IE_STA_QOSINFO_AC_BK | \
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_BE | \
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_VI | \
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+
+#define IEEE80211_DEFAULT_MAX_SP_LEN \
+ IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL
+
struct ieee80211_fragment_entry {
unsigned long first_frag_time;
unsigned int seq;
@@ -78,6 +87,7 @@ struct ieee80211_bss {
u8 dtim_period;

bool wmm_used;
+ bool uapsd_supported;

unsigned long last_probe_resp;

@@ -285,7 +295,7 @@ struct ieee80211_work {
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len;
u8 supp_rates_len;
- bool wmm_used, use_11n;
+ bool wmm_used, use_11n, uapsd_used;
} assoc;
struct {
u32 duration;
@@ -306,6 +316,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),
};

struct ieee80211_if_managed {
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 4688291..0054bba 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -491,6 +491,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;

+ WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
+ && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
+ "U-APSD not supported with HW_PS_NULLFUNC_STACK\n");
+
/*
* Calculate scan IE length -- we need this to alloc
* memory and to subtract from the driver limit. It
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 97bcf22..e52ee47 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -567,7 +567,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 (local->hw.queues < 4)
return;
@@ -577,6 +577,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;
@@ -591,6 +595,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) {
@@ -598,22 +603,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_QOSINFO_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_QOSINFO_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_QOSINFO_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_QOSINFO_AC_BE)
+ uapsd = true;
break;
}

@@ -621,11 +634,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, &params) && local->ops->conf_tx)
printk(KERN_DEBUG "%s: failed to set TX queue "
@@ -1897,6 +1913,15 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
wk->assoc.ht_information_ie =
ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);

+ if (bss->wmm_used && bss->uapsd_supported &&
+ (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
+ wk->assoc.uapsd_used = true;
+ ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
+ } else {
+ wk->assoc.uapsd_used = false;
+ ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
+ }
+
ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
memcpy(wk->assoc.ssid, ssid + 2, ssid[1]);
wk->assoc.ssid_len = ssid[1];
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index a4c63d4..b7371a9 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -54,6 +54,23 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local,
cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv));
}

+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 & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD;
+}
+
struct ieee80211_bss *
ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status,
@@ -117,6 +134,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;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index bc73904..206aeed 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -792,6 +792,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
break;
}

+ qparam.uapsd = false;
+
drv_conf_tx(local, queue, &qparam);
}
}
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 5ba7599..ee6182d 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -202,7 +202,7 @@ 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;
size_t offset = 0, noffset;
int i, len, count, rates_len, supp_rates_len;
@@ -375,6 +375,14 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
}

if (wk->assoc.wmm_used && local->hw.queues >= 4) {
+ if (wk->assoc.uapsd_used) {
+ qos_info = IEEE80211_DEFAULT_UAPSD_QUEUES;
+ qos_info |= (IEEE80211_DEFAULT_MAX_SP_LEN <<
+ IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT);
+ } else {
+ qos_info = 0;
+ }
+
pos = skb_put(skb, 9);
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 7; /* len */
@@ -384,7 +392,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;
}

/* add any remaining custom (i.e. vendor specific here) IEs */


2010-01-13 14:50:43

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH PATCH v6 1/7] mac80211: add U-APSD client support

Kalle Valo <[email protected]> writes:

>> I think a little more detail would be good here, for example adding that
>> it must support staying awake with just the frame triggering it, and
>> that it won't be asked to wake up by mac80211?
>
> Good point. I was planning to improve power save documentation, I do
> that in this patchset and I'll also add more documentation about
> U-APSD. That should help to understand this more.
>
>> Or will it, actually? We've had that discussion with PS-poll so ...
>
> Yes, HW must stay awake until end of service period. I'll write more
> documentation to clear up all this.

I see that John already applied the patches to wireless-testing. I'll
create a separate patch which adds more documentation.

--
Kalle Valo

2010-01-12 08:43:32

by Kalle Valo

[permalink] [raw]
Subject: [PATCH PATCH v6 4/7] mac80211: check uapsd state for dynamic power save

From: Kalle Valo <[email protected]>

To make U-APSD client mode effective, we must not wake up from dynamic power
save when transmitting frames. So if dynamic power save is enabled, it needs
check the queue the transmitted packet is in and decide if we need to wake
up or not.

In a perfect world, where all packets would have correct QoS tags, U-APSD
enabled queues should not trigger wakeup from power save. But in the real
world, where very few packets have correct QoS tags, this won't work. For
example, if only voip class has U-APSD enabled and we send a packet in voip
class, but the packets we receive are in best effort class, we would receive
the packets with the legacy power save method. And that would increase
latencies too much from a voip application point of view.

The workaround is to enable U-APSD for all qeueus and still use dynamic ps
wakeup for all other queues except voip. That way we can still save power
with a voip application and not sacrifice latency. Normal traffic (in
background, best effort or video class) would still trigger wakeup from
dynamic power save.

Signed-off-by: Kalle Valo <[email protected]>
---
net/mac80211/tx.c | 25 +++++++++++++++++++++++++
1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index cf846a3..f2cbf9b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -184,6 +184,7 @@ static ieee80211_tx_result debug_noinline
ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
{
struct ieee80211_local *local = tx->local;
+ struct ieee80211_if_managed *ifmgd;

/* driver doesn't support power save */
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
@@ -208,6 +209,30 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
if (local->quiescing)
return TX_CONTINUE;

+ /* dynamic ps is supported only in managed mode */
+ if (tx->sdata->vif.type != NL80211_IFTYPE_STATION)
+ return TX_CONTINUE;
+
+ ifmgd = &tx->sdata->u.mgd;
+
+ /*
+ * Don't wakeup from power save if u-apsd is enabled, voip ac has
+ * u-apsd enabled and the frame is in voip class. This effectively
+ * means that even if all access categories have u-apsd enabled, in
+ * practise u-apsd is only used with the voip ac. This is a
+ * workaround for the case when received voip class packets do not
+ * have correct qos tag for some reason, due the network or the
+ * peer application.
+ *
+ * Note: local->uapsd_queues access is racy here. If the value is
+ * changed via debugfs, user needs to reassociate manually to have
+ * everything in sync.
+ */
+ if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
+ && (local->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+ && skb_get_queue_mapping(tx->skb) == 0)
+ return TX_CONTINUE;
+
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
ieee80211_stop_queues_by_reason(&local->hw,
IEEE80211_QUEUE_STOP_REASON_PS);


2010-01-12 10:31:32

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH PATCH v6 1/7] mac80211: add U-APSD client support

On Tue, 2010-01-12 at 10:42 +0200, Kalle Valo wrote:

> + *
> + * @IEEE80211_HW_SUPPORTS_UAPSD:
> + * Hardware supports Unscheduled Automatic Power Save Delivery
> + * (U-APSD) in managed mode. The mode is configured with
> + * conf_tx() operation.

I think a little more detail would be good here, for example adding that
it must support staying awake with just the frame triggering it, and
that it won't be asked to wake up by mac80211? Or will it, actually?
We've had that discussion with PS-poll so ...

johannes


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part

2010-01-12 08:43:34

by Kalle Valo

[permalink] [raw]
Subject: [PATCH PATCH v6 2/7] mac80211: add debugfs interface for U-APSD queue configuration

From: 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.

Signed-off-by: Kalle Valo <[email protected]>
---
net/mac80211/debugfs.c | 94 ++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/ieee80211_i.h | 14 +++++++
net/mac80211/main.c | 2 +
net/mac80211/mlme.c | 2 -
net/mac80211/work.c | 4 +-
5 files changed, 113 insertions(+), 3 deletions(-)

diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index e4b5409..b3bc32b 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -158,6 +158,98 @@ 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), "0x%x\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, 0, &val);
+
+ if (ret)
+ return -EINVAL;
+
+ if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+ return -ERANGE;
+
+ 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 uapsd_max_sp_len_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), "0x%x\n", local->uapsd_max_sp_len);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, res);
+}
+
+static ssize_t uapsd_max_sp_len_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, 0, &val);
+
+ if (ret)
+ return -EINVAL;
+
+ if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
+ return -ERANGE;
+
+ local->uapsd_max_sp_len = val;
+
+ return count;
+}
+
+static const struct file_operations uapsd_max_sp_len_ops = {
+ .read = uapsd_max_sp_len_read,
+ .write = uapsd_max_sp_len_write,
+ .open = mac80211_open_file_generic
+};
+
static ssize_t queues_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -314,6 +406,8 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(queues);
DEBUGFS_ADD_MODE(reset, 0200);
DEBUGFS_ADD(noack);
+ DEBUGFS_ADD(uapsd_queues);
+ DEBUGFS_ADD(uapsd_max_sp_len);

statsd = debugfs_create_dir("statistics", phyd);

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 165de3d..52f6a05 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -808,6 +808,20 @@ struct ieee80211_local {
int wifi_wme_noack_test;
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */

+ /*
+ * Bitmask of enabled u-apsd queues,
+ * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association
+ * to take effect.
+ */
+ unsigned int uapsd_queues;
+
+ /*
+ * Maximum number of buffered frames AP can deliver during a
+ * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar.
+ * Needs a new association to take effect.
+ */
+ unsigned int uapsd_max_sp_len;
+
bool pspolling;
bool offchannel_ps_enabled;
/*
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 0054bba..ec8f767 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -384,6 +384,8 @@ 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;
+ local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;

INIT_LIST_HEAD(&local->interfaces);
mutex_init(&local->iflist_mtx);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index e52ee47..064f39b 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -579,7 +579,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
return;

if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
- uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
+ uapsd_queues = local->uapsd_queues;

count = wmm_param[6] & 0x0f;
if (count == ifmgd->wmm_last_param_set)
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index ee6182d..efa6cde 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -376,8 +376,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,

if (wk->assoc.wmm_used && local->hw.queues >= 4) {
if (wk->assoc.uapsd_used) {
- qos_info = IEEE80211_DEFAULT_UAPSD_QUEUES;
- qos_info |= (IEEE80211_DEFAULT_MAX_SP_LEN <<
+ qos_info = local->uapsd_queues;
+ qos_info |= (local->uapsd_max_sp_len <<
IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT);
} else {
qos_info = 0;