aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKalle Valo <kalle.valo@nokia.com>2010-01-12 03:42:31 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-01-12 14:20:58 -0500
commitab13315af97919fae0e014748105fdc2e30afb2d (patch)
treebefa549272ecff20b2839bd6671e4cccbce448f9
parent2d46d7c121436f1dafe91b0a8d9b99e534cfa5f8 (diff)
mac80211: add U-APSD client support
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 <kalle.valo@nokia.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--include/linux/ieee80211.h18
-rw-r--r--include/net/mac80211.h7
-rw-r--r--net/mac80211/cfg.c7
-rw-r--r--net/mac80211/ieee80211_i.h13
-rw-r--r--net/mac80211/main.c4
-rw-r--r--net/mac80211/mlme.c31
-rw-r--r--net/mac80211/scan.c18
-rw-r--r--net/mac80211/util.c2
-rw-r--r--net/mac80211/work.c12
9 files changed, 106 insertions, 6 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 602c0692c3fc..a8c6069a0d9f 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -120,6 +120,24 @@
120#define IEEE80211_QOS_CTL_TID_MASK 0x000F 120#define IEEE80211_QOS_CTL_TID_MASK 0x000F
121#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 121#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007
122 122
123/* U-APSD queue for WMM IEs sent by AP */
124#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7)
125
126/* U-APSD queues for WMM IEs sent by STA */
127#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO (1<<0)
128#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI (1<<1)
129#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK (1<<2)
130#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE (1<<3)
131#define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK 0x0f
132
133/* U-APSD max SP length for WMM IEs sent by STA */
134#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0x00
135#define IEEE80211_WMM_IE_STA_QOSINFO_SP_2 0x01
136#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02
137#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03
138#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03
139#define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5
140
123struct ieee80211_hdr { 141struct ieee80211_hdr {
124 __le16 frame_control; 142 __le16 frame_control;
125 __le16 duration_id; 143 __le16 duration_id;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index e1e73c6abeff..f313a3cbabda 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -113,6 +113,7 @@ struct ieee80211_tx_queue_params {
113 u16 cw_min; 113 u16 cw_min;
114 u16 cw_max; 114 u16 cw_max;
115 u8 aifs; 115 u8 aifs;
116 bool uapsd;
116}; 117};
117 118
118/** 119/**
@@ -929,6 +930,11 @@ enum ieee80211_tkip_key_type {
929 * Hardware supports dynamic spatial multiplexing powersave, 930 * Hardware supports dynamic spatial multiplexing powersave,
930 * ie. can turn off all but one chain and then wake the rest 931 * ie. can turn off all but one chain and then wake the rest
931 * up as required after, for example, rts/cts handshake. 932 * up as required after, for example, rts/cts handshake.
933 *
934 * @IEEE80211_HW_SUPPORTS_UAPSD:
935 * Hardware supports Unscheduled Automatic Power Save Delivery
936 * (U-APSD) in managed mode. The mode is configured with
937 * conf_tx() operation.
932 */ 938 */
933enum ieee80211_hw_flags { 939enum ieee80211_hw_flags {
934 IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, 940 IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -948,6 +954,7 @@ enum ieee80211_hw_flags {
948 IEEE80211_HW_BEACON_FILTER = 1<<14, 954 IEEE80211_HW_BEACON_FILTER = 1<<14,
949 IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15, 955 IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
950 IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, 956 IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
957 IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
951}; 958};
952 959
953/** 960/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index dc12e9466ffd..8286df5822d5 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1128,6 +1128,13 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
1128 p.cw_max = params->cwmax; 1128 p.cw_max = params->cwmax;
1129 p.cw_min = params->cwmin; 1129 p.cw_min = params->cwmin;
1130 p.txop = params->txop; 1130 p.txop = params->txop;
1131
1132 /*
1133 * Setting tx queue params disables u-apsd because it's only
1134 * called in master mode.
1135 */
1136 p.uapsd = false;
1137
1131 if (drv_conf_tx(local, params->queue, &p)) { 1138 if (drv_conf_tx(local, params->queue, &p)) {
1132 printk(KERN_DEBUG "%s: failed to set TX queue " 1139 printk(KERN_DEBUG "%s: failed to set TX queue "
1133 "parameters for queue %d\n", 1140 "parameters for queue %d\n",
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 3e4ac3f30857..3468e378509a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -58,6 +58,15 @@ struct ieee80211_local;
58 58
59#define TU_TO_EXP_TIME(x) (jiffies + usecs_to_jiffies((x) * 1024)) 59#define TU_TO_EXP_TIME(x) (jiffies + usecs_to_jiffies((x) * 1024))
60 60
61#define IEEE80211_DEFAULT_UAPSD_QUEUES \
62 (IEEE80211_WMM_IE_STA_QOSINFO_AC_BK | \
63 IEEE80211_WMM_IE_STA_QOSINFO_AC_BE | \
64 IEEE80211_WMM_IE_STA_QOSINFO_AC_VI | \
65 IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
66
67#define IEEE80211_DEFAULT_MAX_SP_LEN \
68 IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL
69
61struct ieee80211_fragment_entry { 70struct ieee80211_fragment_entry {
62 unsigned long first_frag_time; 71 unsigned long first_frag_time;
63 unsigned int seq; 72 unsigned int seq;
@@ -78,6 +87,7 @@ struct ieee80211_bss {
78 u8 dtim_period; 87 u8 dtim_period;
79 88
80 bool wmm_used; 89 bool wmm_used;
90 bool uapsd_supported;
81 91
82 unsigned long last_probe_resp; 92 unsigned long last_probe_resp;
83 93
@@ -285,7 +295,7 @@ struct ieee80211_work {
285 u8 ssid[IEEE80211_MAX_SSID_LEN]; 295 u8 ssid[IEEE80211_MAX_SSID_LEN];
286 u8 ssid_len; 296 u8 ssid_len;
287 u8 supp_rates_len; 297 u8 supp_rates_len;
288 bool wmm_used, use_11n; 298 bool wmm_used, use_11n, uapsd_used;
289 } assoc; 299 } assoc;
290 struct { 300 struct {
291 u32 duration; 301 u32 duration;
@@ -306,6 +316,7 @@ enum ieee80211_sta_flags {
306 IEEE80211_STA_DISABLE_11N = BIT(4), 316 IEEE80211_STA_DISABLE_11N = BIT(4),
307 IEEE80211_STA_CSA_RECEIVED = BIT(5), 317 IEEE80211_STA_CSA_RECEIVED = BIT(5),
308 IEEE80211_STA_MFP_ENABLED = BIT(6), 318 IEEE80211_STA_MFP_ENABLED = BIT(6),
319 IEEE80211_STA_UAPSD_ENABLED = BIT(7),
309}; 320};
310 321
311struct ieee80211_if_managed { 322struct ieee80211_if_managed {
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 468829143991..0054bba08ce1 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -491,6 +491,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
491 else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) 491 else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
492 local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; 492 local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
493 493
494 WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
495 && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
496 "U-APSD not supported with HW_PS_NULLFUNC_STACK\n");
497
494 /* 498 /*
495 * Calculate scan IE length -- we need this to alloc 499 * Calculate scan IE length -- we need this to alloc
496 * memory and to subtract from the driver limit. It 500 * memory and to subtract from the driver limit. It
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 86f025bc9456..39c27d83a4f2 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -569,7 +569,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
569 struct ieee80211_tx_queue_params params; 569 struct ieee80211_tx_queue_params params;
570 size_t left; 570 size_t left;
571 int count; 571 int count;
572 u8 *pos; 572 u8 *pos, uapsd_queues = 0;
573 573
574 if (local->hw.queues < 4) 574 if (local->hw.queues < 4)
575 return; 575 return;
@@ -579,6 +579,10 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
579 579
580 if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) 580 if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
581 return; 581 return;
582
583 if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
584 uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
585
582 count = wmm_param[6] & 0x0f; 586 count = wmm_param[6] & 0x0f;
583 if (count == ifmgd->wmm_last_param_set) 587 if (count == ifmgd->wmm_last_param_set)
584 return; 588 return;
@@ -593,6 +597,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
593 for (; left >= 4; left -= 4, pos += 4) { 597 for (; left >= 4; left -= 4, pos += 4) {
594 int aci = (pos[0] >> 5) & 0x03; 598 int aci = (pos[0] >> 5) & 0x03;
595 int acm = (pos[0] >> 4) & 0x01; 599 int acm = (pos[0] >> 4) & 0x01;
600 bool uapsd = false;
596 int queue; 601 int queue;
597 602
598 switch (aci) { 603 switch (aci) {
@@ -600,22 +605,30 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
600 queue = 3; 605 queue = 3;
601 if (acm) 606 if (acm)
602 local->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ 607 local->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
608 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
609 uapsd = true;
603 break; 610 break;
604 case 2: /* AC_VI */ 611 case 2: /* AC_VI */
605 queue = 1; 612 queue = 1;
606 if (acm) 613 if (acm)
607 local->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ 614 local->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
615 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
616 uapsd = true;
608 break; 617 break;
609 case 3: /* AC_VO */ 618 case 3: /* AC_VO */
610 queue = 0; 619 queue = 0;
611 if (acm) 620 if (acm)
612 local->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ 621 local->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
622 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
623 uapsd = true;
613 break; 624 break;
614 case 0: /* AC_BE */ 625 case 0: /* AC_BE */
615 default: 626 default:
616 queue = 2; 627 queue = 2;
617 if (acm) 628 if (acm)
618 local->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ 629 local->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
630 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
631 uapsd = true;
619 break; 632 break;
620 } 633 }
621 634
@@ -623,11 +636,14 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
623 params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); 636 params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
624 params.cw_min = ecw2cw(pos[1] & 0x0f); 637 params.cw_min = ecw2cw(pos[1] & 0x0f);
625 params.txop = get_unaligned_le16(pos + 2); 638 params.txop = get_unaligned_le16(pos + 2);
639 params.uapsd = uapsd;
640
626#ifdef CONFIG_MAC80211_VERBOSE_DEBUG 641#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
627 printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d " 642 printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d "
628 "cWmin=%d cWmax=%d txop=%d\n", 643 "cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
629 wiphy_name(local->hw.wiphy), queue, aci, acm, 644 wiphy_name(local->hw.wiphy), queue, aci, acm,
630 params.aifs, params.cw_min, params.cw_max, params.txop); 645 params.aifs, params.cw_min, params.cw_max, params.txop,
646 params.uapsd);
631#endif 647#endif
632 if (drv_conf_tx(local, queue, &params) && local->ops->conf_tx) 648 if (drv_conf_tx(local, queue, &params) && local->ops->conf_tx)
633 printk(KERN_DEBUG "%s: failed to set TX queue " 649 printk(KERN_DEBUG "%s: failed to set TX queue "
@@ -1906,6 +1922,15 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
1906 wk->assoc.ht_information_ie = 1922 wk->assoc.ht_information_ie =
1907 ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION); 1923 ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);
1908 1924
1925 if (bss->wmm_used && bss->uapsd_supported &&
1926 (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
1927 wk->assoc.uapsd_used = true;
1928 ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
1929 } else {
1930 wk->assoc.uapsd_used = false;
1931 ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
1932 }
1933
1909 ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); 1934 ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
1910 memcpy(wk->assoc.ssid, ssid + 2, ssid[1]); 1935 memcpy(wk->assoc.ssid, ssid + 2, ssid[1]);
1911 wk->assoc.ssid_len = ssid[1]; 1936 wk->assoc.ssid_len = ssid[1];
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 30cb62bb45b3..9afe2f9885dc 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -54,6 +54,23 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local,
54 cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv)); 54 cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv));
55} 55}
56 56
57static bool is_uapsd_supported(struct ieee802_11_elems *elems)
58{
59 u8 qos_info;
60
61 if (elems->wmm_info && elems->wmm_info_len == 7
62 && elems->wmm_info[5] == 1)
63 qos_info = elems->wmm_info[6];
64 else if (elems->wmm_param && elems->wmm_param_len == 24
65 && elems->wmm_param[5] == 1)
66 qos_info = elems->wmm_param[6];
67 else
68 /* no valid wmm information or parameter element found */
69 return false;
70
71 return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD;
72}
73
57struct ieee80211_bss * 74struct ieee80211_bss *
58ieee80211_bss_info_update(struct ieee80211_local *local, 75ieee80211_bss_info_update(struct ieee80211_local *local,
59 struct ieee80211_rx_status *rx_status, 76 struct ieee80211_rx_status *rx_status,
@@ -117,6 +134,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
117 } 134 }
118 135
119 bss->wmm_used = elems->wmm_param || elems->wmm_info; 136 bss->wmm_used = elems->wmm_param || elems->wmm_info;
137 bss->uapsd_supported = is_uapsd_supported(elems);
120 138
121 if (!beacon) 139 if (!beacon)
122 bss->last_probe_resp = jiffies; 140 bss->last_probe_resp = jiffies;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index a2ba6e29bd9a..e278f97c8305 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)
792 break; 792 break;
793 } 793 }
794 794
795 qparam.uapsd = false;
796
795 drv_conf_tx(local, queue, &qparam); 797 drv_conf_tx(local, queue, &qparam);
796 } 798 }
797} 799}
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 7c5d95b1bc04..a74fd6ee0083 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,
202 struct ieee80211_local *local = sdata->local; 202 struct ieee80211_local *local = sdata->local;
203 struct sk_buff *skb; 203 struct sk_buff *skb;
204 struct ieee80211_mgmt *mgmt; 204 struct ieee80211_mgmt *mgmt;
205 u8 *pos; 205 u8 *pos, qos_info;
206 const u8 *ies; 206 const u8 *ies;
207 size_t offset = 0, noffset; 207 size_t offset = 0, noffset;
208 int i, len, count, rates_len, supp_rates_len; 208 int i, len, count, rates_len, supp_rates_len;
@@ -375,6 +375,14 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
375 } 375 }
376 376
377 if (wk->assoc.wmm_used && local->hw.queues >= 4) { 377 if (wk->assoc.wmm_used && local->hw.queues >= 4) {
378 if (wk->assoc.uapsd_used) {
379 qos_info = IEEE80211_DEFAULT_UAPSD_QUEUES;
380 qos_info |= (IEEE80211_DEFAULT_MAX_SP_LEN <<
381 IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT);
382 } else {
383 qos_info = 0;
384 }
385
378 pos = skb_put(skb, 9); 386 pos = skb_put(skb, 9);
379 *pos++ = WLAN_EID_VENDOR_SPECIFIC; 387 *pos++ = WLAN_EID_VENDOR_SPECIFIC;
380 *pos++ = 7; /* len */ 388 *pos++ = 7; /* len */
@@ -384,7 +392,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
384 *pos++ = 2; /* WME */ 392 *pos++ = 2; /* WME */
385 *pos++ = 0; /* WME info */ 393 *pos++ = 0; /* WME info */
386 *pos++ = 1; /* WME ver */ 394 *pos++ = 1; /* WME ver */
387 *pos++ = 0; 395 *pos++ = qos_info;
388 } 396 }
389 397
390 /* add any remaining custom (i.e. vendor specific here) IEs */ 398 /* add any remaining custom (i.e. vendor specific here) IEs */