From: Zhu Yi <yi.zhu@intel.com>
To: linux-wireless@vger.kernel.org,
"John W. Linville" <linville@tuxdriver.com>
Subject: [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
Date: Mon, 14 May 2007 13:15:26 +0800 [thread overview]
Message-ID: <20070514051526.GA25582@mail.intel.com> (raw)
This patch adds IEEE802.11e/WMM Traffic Stream (TS) Management and
Direct Link Setup (DLS) non-AP QSTA mode support for mac80211.
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
--
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 873ccb0..3b5717b 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -1557,11 +1557,18 @@ static int ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 30;
} else if (sdata->type == IEEE80211_IF_TYPE_STA) {
- fc |= IEEE80211_FCTL_TODS;
- /* BSSID SA DA */
- memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN);
- memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ if (dls_link_status(&sdata->u.sta, hdr.addr1) == DLS_STATUS_OK){
+ /* DA SA BSSID */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(hdr.addr3, sdata->u.sta.bssid, ETH_ALEN);
+ } else {
+ fc |= IEEE80211_FCTL_TODS;
+ /* BSSID SA DA */
+ memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ }
hdrlen = 24;
} else if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
/* DA SA BSSID */
@@ -2281,6 +2288,8 @@ static void ieee80211_if_shutdown(struct net_device *dev)
case IEEE80211_IF_TYPE_IBSS:
sdata->u.sta.state = IEEE80211_DISABLED;
del_timer_sync(&sdata->u.sta.timer);
+ del_timer_sync(&sdata->u.sta.admit_timer);
+ dls_info_stop(&sdata->u.sta);
skb_queue_purge(&sdata->u.sta.skb_queue);
if (!local->ops->hw_scan &&
local->scan_dev == sdata->dev) {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 8b939d0..c273afe 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -59,6 +60,10 @@ struct ieee80211_local;
* increased memory use (about 2 kB of RAM per entry). */
#define IEEE80211_FRAGMENT_MAX 4
+/* Minimum and Maximum TSID used by EDCA. EDCA uses 0~7; HCCA uses 8~15 */
+#define EDCA_TSID_MIN 0
+#define EDCA_TSID_MAX 7
+
struct ieee80211_fragment_entry {
unsigned long first_frag_time;
unsigned int seq;
@@ -178,6 +183,31 @@ struct ieee80211_tx_stored_packet {
unsigned int last_frag_rate_ctrl_probe:1;
};
+struct sta_ts_data {
+ enum {
+ TS_STATUS_UNUSED = 0,
+ TS_STATUS_ACTIVE = 1,
+ TS_STATUS_INACTIVE = 2,
+ TS_STATUS_THROTTLING = 3,
+ } status;
+ u8 dialog_token;
+ u8 up;
+ u32 admitted_time_usec;
+ u32 used_time_usec;
+};
+
+#define DLS_STATUS_OK 0
+#define DLS_STATUS_NOLINK 1
+#define DLS_STATUS_SETUP 2
+struct dls_info {
+ atomic_t refcnt;
+ int status;
+ u8 addr[ETH_ALEN];
+ struct dls_info *hnext; /* next entry in hash table list */
+ u32 timeout;
+ u32 supp_rates;
+};
+
typedef ieee80211_txrx_result (*ieee80211_tx_handler)
(struct ieee80211_txrx_data *tx);
@@ -222,6 +252,7 @@ struct ieee80211_if_sta {
} state;
struct timer_list timer;
struct work_struct work;
+ struct timer_list admit_timer; /* Recompute EDCA admitted time */
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
size_t ssid_len;
@@ -271,6 +302,16 @@ struct ieee80211_if_sta {
u32 supp_rates_bits;
int wmm_last_param_set;
+
+ u32 dot11EDCAAveragingPeriod;
+ u32 MPDUExchangeTime;
+#define STA_TSID_NUM 16
+#define STA_TSDIR_NUM 2
+ /* EDCA: 0~7, HCCA: 8~15 */
+ struct sta_ts_data ts_data[STA_TSID_NUM][STA_TSDIR_NUM];
+
+ struct dls_info *dls_hash[STA_HASH_SIZE];
+ spinlock_t dls_lock;
};
@@ -640,6 +714,11 @@ struct ieee80211_local {
#endif
};
+enum sta_link_direction {
+ STA_TS_UPLINK = 0,
+ STA_TS_DOWNLINK = 1,
+};
+
static inline struct ieee80211_local *hw_to_local(
struct ieee80211_hw *hw)
{
@@ -774,6 +853,7 @@ int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq);
/* ieee80211_sta.c */
void ieee80211_sta_timer(unsigned long data);
void ieee80211_sta_work(struct work_struct *work);
+void ieee80211_admit_refresh(unsigned long ptr);
void ieee80211_sta_scan_work(struct work_struct *work);
void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
struct ieee80211_rx_status *rx_status);
@@ -794,6 +874,27 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
u8 *addr);
int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason);
int ieee80211_sta_disassociate(struct net_device *dev, u16 reason);
+void ieee80211_send_addts(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_elem_tspec *tspec);
+void wmm_send_addts(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_elem_tspec *tspec);
+void ieee80211_send_delts(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_elem_tspec *tp);
+void wmm_send_delts(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_elem_tspec *tp);
+void ieee80211_send_dls_req(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct dls_info *dls);
+void ieee80211_send_dls_teardown(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ u8 *mac, u16 reason);
+void dls_info_add(struct ieee80211_if_sta *ifsta, struct dls_info *dls);
+void dls_info_stop(struct ieee80211_if_sta *ifsta);
+int dls_link_status(struct ieee80211_if_sta *ifsta, u8 *addr);
/* ieee80211_iface.c */
int ieee80211_if_add(struct net_device *dev, const char *name,
diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c
index a8612a8..9a83dd2 100644
--- a/net/mac80211/ieee80211_iface.c
+++ b/net/mac80211/ieee80211_iface.c
@@ -182,6 +182,10 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
(unsigned long) sdata);
skb_queue_head_init(&ifsta->skb_queue);
+ init_timer(&ifsta->admit_timer);
+ ifsta->admit_timer.data = (unsigned long) dev;
+ ifsta->admit_timer.function = ieee80211_admit_refresh;
+
ifsta->capab = WLAN_CAPABILITY_ESS;
ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
IEEE80211_AUTH_ALG_SHARED_KEY;
@@ -191,6 +195,11 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
ifsta->auto_channel_sel = 1;
ifsta->auto_bssid_sel = 1;
+ /* Initialize non-AP QSTA QoS Params */
+ ifsta->dot11EDCAAveragingPeriod = 5;
+ ifsta->MPDUExchangeTime = 0;
+ spin_lock_init(&ifsta->dls_lock);
+
msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev);
sdata->bss = &msdata->u.ap;
break;
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index b003912..53f61bf 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -111,6 +111,8 @@ struct ieee802_11_elems {
u8 wmm_info_len;
u8 *wmm_param;
u8 wmm_param_len;
+ u8 *tspec;
+ u8 tspec_len;
};
typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -179,17 +181,34 @@ static ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
pos[2] == 0xf2) {
/* Microsoft OUI (00:50:F2) */
- if (pos[3] == 1) {
+ if (pos[3] == WIFI_OUI_TYPE_WPA) {
/* OUI Type 1 - WPA IE */
elems->wpa = pos;
elems->wpa_len = elen;
- } else if (elen >= 5 && pos[3] == 2) {
- if (pos[4] == 0) {
+ } else if (elen >= 5 &&
+ pos[3] == WIFI_OUI_TYPE_WMM) {
+ switch (pos[4]) {
+ case WIFI_OUI_STYPE_WMM_INFO:
elems->wmm_info = pos;
elems->wmm_info_len = elen;
- } else if (pos[4] == 1) {
+ break;
+ case WIFI_OUI_STYPE_WMM_PARAM:
elems->wmm_param = pos;
elems->wmm_param_len = elen;
+ break;
+ case WIFI_OUI_STYPE_WMM_TSPEC:
+ if (elen != 61) {
+ printk(KERN_ERR "Wrong "
+ "TSPEC size.\n");
+ break;
+ }
+ elems->tspec = pos + 6;
+ elems->tspec_len = elen - 6;
+ break;
+ default:
+ //printk(KERN_ERR "Unsupported "
+ // "WiFi OUI %d\n", pos[4]);
+ break;
}
}
}
@@ -214,6 +233,14 @@ static ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
elems->ht_extra_param = pos;
elems->ht_extra_param_len = elen;
break;
+ case WLAN_EID_TSPEC:
+ if (elen != 55) {
+ printk(KERN_ERR "Wrong TSPEC size.\n");
+ break;
+ }
+ elems->tspec = pos;
+ elems->tspec_len = elen;
+ break;
default:
#if 0
printk(KERN_DEBUG "IEEE 802.11 element parse ignored "
@@ -687,6 +714,402 @@ static void ieee80211_send_disassoc(struct net_device *dev,
}
+int ieee80211_ts_index(u8 direction)
+{
+ if (direction == WLAN_TSINFO_DOWNLINK ||
+ direction == WLAN_TSINFO_DIRECTLINK)
+ return STA_TS_DOWNLINK;
+ return STA_TS_UPLINK; /* UP and Bidirectional LINK */
+}
+
+
+void ieee80211_send_addts(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_elem_tspec *tspec)
+{
+ struct ieee80211_mgmt *mgmt;
+ struct sk_buff *skb;
+ static u8 token;
+ struct ieee80211_elem_tspec *ptspec;
+ u8 *pos;
+
+ skb = dev_alloc_skb(sizeof(*mgmt) + sizeof(*tspec));
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for addts "
+ "frame\n", dev->name);
+ return;
+ }
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.addts_req));
+ mgmt->u.action.category = WLAN_CATEGORY_QOS;
+ mgmt->u.action.u.addts_req.action_code = WLAN_ACTION_QOS_ADDTS_REQ;
+ mgmt->u.action.u.addts_req.dialog_token = ++token % 127;
+
+ skb_put(skb, 2 + sizeof(*tspec));
+ pos = mgmt->u.action.u.addts_req.variable;
+ pos[0] = WLAN_EID_TSPEC;
+ pos[1] = sizeof(*tspec);
+ pos += 2;
+ ptspec = (struct ieee80211_elem_tspec *)pos;
+ memcpy(ptspec, tspec, sizeof(*tspec));
+
+ ieee80211_sta_tx(dev, skb, 0);
+}
+
+
+void wmm_send_addts(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_elem_tspec *tspec)
+{
+ struct ieee80211_mgmt *mgmt;
+ struct sk_buff *skb;
+ static u8 token;
+ struct ieee80211_elem_tspec *ptspec;
+ u8 *pos;
+
+ skb = dev_alloc_skb(sizeof(*mgmt) + 2 + 6 + sizeof(*tspec));
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for addts "
+ "frame\n", dev->name);
+ return;
+ }
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.wme_action));
+ mgmt->u.action.category = WLAN_CATEGORY_WMM;
+ mgmt->u.action.u.wme_action.action_code = WLAN_ACTION_QOS_ADDTS_REQ;
+ mgmt->u.action.u.wme_action.dialog_token = ++token % 127;
+ mgmt->u.action.u.wme_action.status_code = 0;
+
+ skb_put(skb, 2 + 6 + sizeof(*tspec));
+ pos = mgmt->u.action.u.wme_action.variable;
+ pos[0] = WLAN_EID_GENERIC;
+ pos[1] = 61;
+ pos += 2;
+ pos[0] = 0x00; pos[1] = 0x50; pos[2] = 0xf2; /* Wi-Fi OUI (00:50:F2)*/
+ pos += 3;
+ pos[0] = WIFI_OUI_TYPE_WMM;
+ pos[1] = WIFI_OUI_STYPE_WMM_TSPEC;
+ pos[2] = 1; /* Version */
+ pos += 3;
+ ptspec = (struct ieee80211_elem_tspec *)pos;
+ memcpy(ptspec, tspec, sizeof(*tspec));
+
+ ieee80211_sta_tx(dev, skb, 0);
+}
+
+
+void ieee80211_send_delts(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_elem_tspec *tp)
+{
+ struct ieee80211_mgmt *mgmt;
+ struct sk_buff *skb;
+ u8 tsid = IEEE80211_TSINFO_TSID(tp->ts_info);
+ u8 direction = IEEE80211_TSINFO_DIR(tp->ts_info);
+ u32 medium_time = tp->medium_time;
+ u8 index = ieee80211_ts_index(direction);
+
+ if (ifsta->ts_data[tsid][index].status == TS_STATUS_UNUSED) {
+ printk(KERN_DEBUG "%s: Tring to delete an ACM disabled TS "
+ "(%u:%u)\n", dev->name, tsid, direction);
+ return;
+ }
+ skb = dev_alloc_skb(sizeof(*mgmt));
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for delts "
+ "frame\n", dev->name);
+ return;
+ }
+
+ /* recompute admitted time */
+ ifsta->ts_data[tsid][index].admitted_time_usec -=
+ ifsta->dot11EDCAAveragingPeriod * medium_time * 32;
+ if ((s32)(ifsta->ts_data[tsid][index].admitted_time_usec) < 0)
+ ifsta->ts_data[tsid][index].admitted_time_usec = 0;
+
+ ifsta->ts_data[tsid][index].status = TS_STATUS_INACTIVE;
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+ IEEE80211_STYPE_ACTION);
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.delts));
+ mgmt->u.action.category = WLAN_CATEGORY_QOS;
+ mgmt->u.action.u.delts.action_code = WLAN_ACTION_QOS_DELTS;
+ mgmt->u.action.u.delts.reason_code = 0;
+ memset(&mgmt->u.action.u.delts.ts_info, 0,
+ sizeof(struct ieee80211_ts_info));
+
+ SET_TSINFO_TSID(tp->ts_info, tsid);
+ SET_TSINFO_DIR(tp->ts_info, direction);
+ SET_TSINFO_POLICY(tp->ts_info, WLAN_TSINFO_EDCA);
+ SET_TSINFO_APSD(tp->ts_info, WLAN_TSINFO_PSB_LEGACY);
+ SET_TSINFO_UP(tp->ts_info, ifsta->ts_data[tsid][index].up);
+
+ ieee80211_sta_tx(dev, skb, 0);
+}
+
+
+void wmm_send_delts(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_elem_tspec *tp)
+{
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_elem_tspec *tspec;
+ struct sk_buff *skb;
+ u8 tsid = IEEE80211_TSINFO_TSID(tp->ts_info);
+ u8 direction = IEEE80211_TSINFO_DIR(tp->ts_info);
+ u32 medium_time = tp->medium_time;
+ u8 index = ieee80211_ts_index(direction);
+ u8 *pos;
+
+ if (ifsta->ts_data[tsid][index].status == TS_STATUS_UNUSED) {
+ printk(KERN_DEBUG "%s: Tring to delete a non-Actived TS "
+ "(%u %u)\n", dev->name, tsid, direction);
+ return;
+ }
+ skb = dev_alloc_skb(sizeof(*mgmt) + 2 + 6 + sizeof(*tspec));
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for delts "
+ "frame\n", dev->name);
+ return;
+ }
+
+ /* recompute admitted time */
+ ifsta->ts_data[tsid][index].admitted_time_usec -=
+ ifsta->dot11EDCAAveragingPeriod * medium_time * 32;
+ if ((s32)(ifsta->ts_data[tsid][index].admitted_time_usec < 0))
+ ifsta->ts_data[tsid][index].admitted_time_usec = 0;
+
+ ifsta->ts_data[tsid][index].status = TS_STATUS_INACTIVE;
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.wme_action));
+ mgmt->u.action.category = WLAN_CATEGORY_WMM;
+ mgmt->u.action.u.wme_action.action_code = WLAN_ACTION_QOS_DELTS;
+ mgmt->u.action.u.wme_action.dialog_token = 0;
+ mgmt->u.action.u.wme_action.status_code = 0;
+
+ skb_put(skb, 2 + 6 + sizeof(*tspec));
+ pos = mgmt->u.action.u.wme_action.variable;
+ pos[0] = WLAN_EID_GENERIC;
+ pos[1] = 61;
+ pos += 2;
+ pos[0] = 0x00; pos[1] = 0x50; pos[2] = 0xf2; /* Wi-Fi OUI (00:50:F2)*/
+ pos += 3;
+ pos[0] = WIFI_OUI_TYPE_WMM;
+ pos[1] = WIFI_OUI_STYPE_WMM_TSPEC;
+ pos[2] = 1; /* Version */
+ pos += 3;
+ tspec = (struct ieee80211_elem_tspec *)pos;
+ memset(tspec, 0, sizeof(*tspec));
+
+ SET_TSINFO_TSID(tspec->ts_info, tsid);
+ SET_TSINFO_DIR(tspec->ts_info, direction);
+ SET_TSINFO_POLICY(tspec->ts_info, WLAN_TSINFO_EDCA);
+ SET_TSINFO_APSD(tspec->ts_info, WLAN_TSINFO_PSB_LEGACY);
+ SET_TSINFO_UP(tspec->ts_info, ifsta->ts_data[tsid][index].up);
+
+ ieee80211_sta_tx(dev, skb, 0);
+}
+
+
+void ieee80211_send_dls_req(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct dls_info *dls)
+{
+ struct ieee80211_hw_mode *mode;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ u8 *pos, *supp_rates, *esupp_rates = NULL;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(*mgmt) + 200 /* rates + ext_rates Size */);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for DLS REQ "
+ "frame\n", dev->name);
+ return;
+ }
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.dls_req));
+ mgmt->u.action.category = WLAN_CATEGORY_DLS;
+ mgmt->u.action.u.dls_req.action_code = WLAN_ACTION_DLS_REQ;
+ memcpy(mgmt->u.action.u.dls_req.dest, dls->addr, ETH_ALEN);
+ memcpy(mgmt->u.action.u.dls_req.src, dev->dev_addr, ETH_ALEN);
+ mgmt->u.action.u.dls_req.capab_info = cpu_to_le16(ifsta->ap_capab);
+ mgmt->u.action.u.dls_req.timeout = dls->timeout;
+
+ /* Add supported rates and extended supported rates */
+ supp_rates = skb_put(skb, 2);
+ supp_rates[0] = WLAN_EID_SUPP_RATES;
+ supp_rates[1] = 0;
+ mode = local->oper_hw_mode;
+ for (i = 0; i < mode->num_rates; i++) {
+ struct ieee80211_rate *rate = &mode->rates[i];
+ if (!(rate->flags & IEEE80211_RATE_SUPPORTED))
+ continue;
+ if (esupp_rates) {
+ pos = skb_put(skb, 1);
+ esupp_rates[1]++;
+ } else if (supp_rates[1] == 8) {
+ esupp_rates = skb_put(skb, 3);
+ esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
+ esupp_rates[1] = 1;
+ pos = &esupp_rates[2];
+ } else {
+ pos = skb_put(skb, 1);
+ supp_rates[1]++;
+ }
+ if (local->hw.conf.phymode == MODE_ATHEROS_TURBO)
+ *pos = rate->rate / 10;
+ else
+ *pos = rate->rate / 5;
+ }
+
+ ieee80211_sta_tx(dev, skb, 0);
+}
+
+
+static void ieee80211_send_dls_resp(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ u8 *mac_addr, u16 status)
+{
+ struct ieee80211_hw_mode *mode;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ u8 *pos, *supp_rates, *esupp_rates = NULL;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(*mgmt) + 200 /* rates + ext_rates Size */);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for dls resp "
+ "frame\n", dev->name);
+ return;
+ }
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.dls_resp));
+ mgmt->u.action.category = WLAN_CATEGORY_DLS;
+ mgmt->u.action.u.dls_resp.action_code = WLAN_ACTION_DLS_RESP;
+ memcpy(mgmt->u.action.u.dls_resp.dest, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->u.action.u.dls_resp.src, mac_addr, ETH_ALEN);
+ mgmt->u.action.u.dls_resp.status_code = cpu_to_le16(status);
+
+ if (!mgmt->u.action.u.dls_resp.status_code) {
+ ieee80211_sta_tx(dev, skb, 0);
+ return;
+ }
+
+ /* Add capability information */
+ pos = skb_put(skb, 2);
+ *(__le16 *)pos = cpu_to_le16(ifsta->ap_capab);
+
+ /* Add supported rates and extended supported rates */
+ supp_rates = skb_put(skb, 2);
+ supp_rates[0] = WLAN_EID_SUPP_RATES;
+ supp_rates[1] = 0;
+ mode = local->oper_hw_mode;
+ for (i = 0; i < mode->num_rates; i++) {
+ struct ieee80211_rate *rate = &mode->rates[i];
+ if (!(rate->flags & IEEE80211_RATE_SUPPORTED))
+ continue;
+ if (esupp_rates) {
+ pos = skb_put(skb, 1);
+ esupp_rates[1]++;
+ } else if (supp_rates[1] == 8) {
+ esupp_rates = skb_put(skb, 3);
+ esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
+ esupp_rates[1] = 1;
+ pos = &esupp_rates[2];
+ } else {
+ pos = skb_put(skb, 1);
+ supp_rates[1]++;
+ }
+ if (local->hw.conf.phymode == MODE_ATHEROS_TURBO)
+ *pos = rate->rate / 10;
+ else
+ *pos = rate->rate / 5;
+ }
+
+ ieee80211_sta_tx(dev, skb, 0);
+}
+
+
+void ieee80211_send_dls_teardown(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ u8 *mac_addr, u16 reason)
+{
+ struct ieee80211_mgmt *mgmt;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(sizeof(*mgmt));
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for DLS "
+ "Teardown frame\n", dev->name);
+ return;
+ }
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+ IEEE80211_STYPE_ACTION);
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.dls_teardown));
+ mgmt->u.action.category = WLAN_CATEGORY_DLS;
+ mgmt->u.action.u.dls_teardown.action_code = WLAN_ACTION_DLS_TEARDOWN;
+ memcpy(mgmt->u.action.u.dls_teardown.dest, mac_addr, ETH_ALEN);
+ memcpy(mgmt->u.action.u.dls_teardown.src, dev->dev_addr, ETH_ALEN);
+ mgmt->u.action.u.dls_teardown.reason_code = cpu_to_le16(reason);
+
+ ieee80211_sta_tx(dev, skb, 0);
+}
+
+
static int ieee80211_privacy_mismatch(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
@@ -1282,6 +1705,364 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
ieee80211_associated(dev, ifsta);
}
+static u32 calculate_mpdu_exchange_time(struct ieee80211_local *local,
+ struct ieee80211_elem_tspec *tspec)
+{
+ /*
+ * FIXME: MPDUExchangeTime = duration(Nominal MSDU Size, Min PHY Rate) +
+ * SIFS + ACK duration
+ */
+ int extra = 0; /* SIFS + ACK */
+
+ switch (local->hw.conf.phymode) {
+ case MODE_IEEE80211A:
+ extra = 16 + 24;
+ break;
+ case MODE_IEEE80211B:
+ extra = 10 + 203;
+ break;
+ case MODE_IEEE80211G:
+ default:
+ extra = 10 + 30;
+ break;
+ }
+ return (tspec->nominal_msdu_size * 8) /
+ (tspec->min_phy_rate / 1000000) + extra;
+}
+
+static void sta_update_tspec(struct ieee80211_local *local,
+ struct ieee80211_if_sta *ifsta,
+ int action, struct ieee80211_elem_tspec *tspec)
+{
+ u8 tsid = IEEE80211_TSINFO_TSID(tspec->ts_info);
+ u8 index = ieee80211_ts_index(IEEE80211_TSINFO_DIR(tspec->ts_info));
+
+ switch (action) {
+ case WLAN_ACTION_QOS_ADDTS_RESP:
+ ifsta->ts_data[tsid][index].status = TS_STATUS_ACTIVE;
+ ifsta->ts_data[tsid][index].up =
+ IEEE80211_TSINFO_UP(tspec->ts_info);
+ ifsta->ts_data[tsid][index].used_time_usec = 0;
+ ifsta->ts_data[tsid][index].admitted_time_usec +=
+ ifsta->dot11EDCAAveragingPeriod * tspec->medium_time * 32;
+ ifsta->MPDUExchangeTime =
+ calculate_mpdu_exchange_time(local, tspec);
+ break;
+ case WLAN_ACTION_QOS_DELTS:
+ ifsta->ts_data[tsid][index].status = TS_STATUS_INACTIVE;
+ ifsta->ts_data[tsid][index].used_time_usec = 0;
+ ifsta->ts_data[tsid][index].admitted_time_usec -=
+ ifsta->dot11EDCAAveragingPeriod * tspec->medium_time * 32;
+ if (ifsta->ts_data[tsid][index].admitted_time_usec < 0)
+ ifsta->ts_data[tsid][index].admitted_time_usec = 0;
+ ifsta->MPDUExchangeTime = 0;
+ break;
+ default:
+ printk(KERN_ERR "%s: invalid action type %d\n", __FUNCTION__,
+ action);
+ break;
+ }
+}
+
+static void sta_parse_tspec(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_mgmt *mgmt, size_t len, u8 prefix,
+ struct ieee80211_elem_tspec *tspec)
+{
+ struct ieee802_11_elems elems;
+ u8 *pos;
+
+ /*
+ printk(KERN_DEBUG "Dialog_token: %d, TID: %u, Direction: %u, PSB: %d, "
+ "UP: %d\n", mgmt->u.action.u.wme_action.dialog_token,
+ IEEE80211_TSINFO_TSID(tspec->ts_info),
+ IEEE80211_TSINFO_DIR(tspec->ts_info),
+ IEEE80211_TSINFO_APSD(tspec->ts_info),
+ IEEE80211_TSINFO_UP(tspec->ts_info));
+ */
+
+ if (mgmt->u.action.category == WLAN_CATEGORY_QOS)
+ pos = mgmt->u.action.u.addts_resp.variable + prefix;
+ else
+ pos = mgmt->u.action.u.wme_action.variable + prefix;
+
+ if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems)
+ == ParseFailed) {
+ printk(KERN_DEBUG "%s: failed to parse TSPEC\n", dev->name);
+ return;
+ }
+ memcpy(tspec, elems.tspec, sizeof(*tspec));
+}
+
+
+/* must be called with ifsta->dls_lock held */
+static struct dls_info * __dls_info_get(struct ieee80211_if_sta *ifsta,
+ u8 *addr)
+{
+ struct dls_info *dls;
+
+ dls = ifsta->dls_hash[STA_HASH(addr)];
+ while (dls) {
+ if (memcmp(dls->addr, addr, ETH_ALEN) == 0) {
+ atomic_inc(&dls->refcnt);
+ break;
+ }
+ dls = dls->hnext;
+ }
+
+ return dls;
+}
+
+struct dls_info * dls_info_get(struct ieee80211_if_sta *ifsta, u8 *addr)
+{
+ struct dls_info *dls;
+
+ spin_lock_bh(&ifsta->dls_lock);
+ dls = __dls_info_get(ifsta, addr);
+ spin_unlock_bh(&ifsta->dls_lock);
+
+ return dls;
+}
+
+static void dls_info_put(struct dls_info *dls)
+{
+ if ((atomic_read(&dls->refcnt) == 1) ||
+ (atomic_dec_and_test(&dls->refcnt)))
+ kfree(dls);
+}
+
+/* must be called with ifsta->dls_lock held */
+static void __dls_info_hash_del(struct ieee80211_if_sta *ifsta,
+ struct dls_info *dls)
+{
+ struct dls_info *d;
+
+ d = ifsta->dls_hash[STA_HASH(dls->addr)];
+ if (!d)
+ return;
+ if (memcmp(d->addr, dls->addr, ETH_ALEN) == 0) {
+ ifsta->dls_hash[STA_HASH(dls->addr)] = d->hnext;
+ return;
+ }
+ while (d->hnext && memcmp(d->hnext->addr, dls->addr, ETH_ALEN) != 0)
+ d = d->hnext;
+ if (d->hnext)
+ d->hnext = d->hnext->hnext;
+
+}
+
+static void dls_info_del(struct ieee80211_if_sta *ifsta, u8 *addr)
+{
+ struct dls_info *dls;
+
+ spin_lock_bh(&ifsta->dls_lock);
+ dls = __dls_info_get(ifsta, addr);
+ if (!dls)
+ goto unlock;
+
+ __dls_info_hash_del(ifsta, dls);
+ atomic_dec(&dls->refcnt);
+ dls_info_put(dls);
+unlock:
+ spin_unlock_bh(&ifsta->dls_lock);
+}
+
+void dls_info_add(struct ieee80211_if_sta *ifsta, struct dls_info *dls)
+{
+ struct dls_info *d;
+
+ spin_lock_bh(&ifsta->dls_lock);
+ if ((d = __dls_info_get(ifsta, dls->addr)) != NULL) {
+ __dls_info_hash_del(ifsta, d);
+ atomic_dec(&dls->refcnt);
+ dls_info_put(dls);
+ }
+ dls->hnext = ifsta->dls_hash[STA_HASH(dls->addr)];
+ ifsta->dls_hash[STA_HASH(dls->addr)] = dls;
+ spin_unlock_bh(&ifsta->dls_lock);
+}
+
+int dls_link_status(struct ieee80211_if_sta *ifsta, u8 *addr)
+{
+ struct dls_info *d;
+ int ret = DLS_STATUS_NOLINK;
+
+ spin_lock_bh(&ifsta->dls_lock);
+ if ((d = __dls_info_get(ifsta, addr)) != NULL) {
+ ret = d->status;
+ dls_info_put(d);
+ }
+ spin_unlock_bh(&ifsta->dls_lock);
+ return ret;
+}
+
+void dls_info_stop(struct ieee80211_if_sta *ifsta)
+{
+ struct dls_info *n, *d = ifsta->dls_hash[0];
+ int i;
+
+ spin_lock_bh(&ifsta->dls_lock);
+ for (i = 0; i < STA_HASH_SIZE; d = ifsta->dls_hash[++i]) {
+ while (d) {
+ n = d->hnext;
+ dls_info_put(d);
+ d = n;
+ }
+ }
+ spin_unlock_bh(&ifsta->dls_lock);
+
+}
+
+static void sta_process_dls_req(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct dls_info *dls;
+ u8 *src = mgmt->u.action.u.dls_req.src;
+ struct ieee802_11_elems elems;
+ struct ieee80211_rate *rates;
+ size_t baselen, num_rates;
+ int i, j;
+ struct ieee80211_hw_mode *mode;
+ u32 supp_rates = 0;
+
+ printk(KERN_DEBUG "Receive DLS request from "
+ "%02X:%02X:%02X:%02X:%02X:%02X\n",
+ src[0], src[1], src[2], src[3], src[4], src[5]);
+
+ baselen = (u8 *)mgmt->u.action.u.dls_req.variable - (u8 *)mgmt;
+ if (baselen > len)
+ return;
+
+ if (ieee802_11_parse_elems(mgmt->u.action.u.dls_req.variable,
+ len - baselen, &elems) == ParseFailed) {
+ printk(KERN_ERR "DLS Parse support rates failed.\n");
+ return;
+ }
+ mode = local->sta_scanning ?
+ local->scan_hw_mode : local->oper_hw_mode;
+ rates = mode->rates;
+ num_rates = mode->num_rates;
+
+ for (i = 0; i < elems.supp_rates_len + elems.ext_supp_rates_len; i++) {
+ u8 rate = 0;
+ if (i < elems.supp_rates_len)
+ rate = elems.supp_rates[i];
+ else if (elems.ext_supp_rates)
+ rate = elems.ext_supp_rates[i - elems.supp_rates_len];
+ rate = 5 * (rate & 0x7f);
+ if (mode->mode == MODE_ATHEROS_TURBO)
+ rate *= 2;
+ for (j = 0; j < num_rates; j++)
+ if (rates[j].rate == rate)
+ supp_rates |= BIT(j);
+ }
+ if (supp_rates == 0) {
+ /* Send DLS failed Response to the peer because
+ * the supported rates are mismatch */
+ ieee80211_send_dls_resp(dev, ifsta, src,
+ WLAN_REASON_QSTA_NOT_USE);
+ return;
+ }
+
+ dls = kzalloc(sizeof(struct dls_info), GFP_ATOMIC);
+ if (!dls) {
+ printk(KERN_ERR "No memory for dls_info allocation.\n");
+ return;
+ }
+ atomic_set(&dls->refcnt, 1);
+ dls->status = DLS_STATUS_OK;
+ dls->timeout = le16_to_cpu(mgmt->u.action.u.dls_req.timeout);
+ memcpy(dls->addr, src, ETH_ALEN);
+ dls->supp_rates = supp_rates;
+ dls_info_add(ifsta, dls);
+
+ /* Send DLS successful Response to the peer */
+ ieee80211_send_dls_resp(dev, ifsta, src, 0);
+}
+
+
+static void sta_process_dls_resp(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct dls_info *dls;
+ u8 *src = mgmt->u.action.u.dls_resp.src;
+ struct ieee802_11_elems elems;
+ struct ieee80211_rate *rates;
+ size_t baselen, num_rates;
+ int i, j;
+ struct ieee80211_hw_mode *mode;
+ u32 supp_rates = 0;
+
+ printk(KERN_DEBUG "Receive DLS response from "
+ "%02X:%02X:%02X:%02X:%02X:%02X\n",
+ src[0], src[1], src[2], src[3], src[4], src[5]);
+
+ dls = dls_info_get(ifsta, src);
+ if (!dls) {
+ printk(KERN_ERR "Cannot find dls_info for address %02X:%02X:"
+ "%02X:%02X:%02X:%02X. Invalid DLS response received.\n",
+ src[0], src[1], src[2], src[3], src[4], src[5]);
+ return;
+ }
+ if (mgmt->u.action.u.dls_resp.status_code) {
+ printk(KERN_ERR "DLS setup refused by peer. Reason %d\n",
+ mgmt->u.action.u.dls_resp.status_code);
+ dls_info_del(ifsta, src);
+ return;
+ }
+
+ baselen = (u8 *)mgmt->u.action.u.dls_resp.variable - (u8 *)mgmt;
+ if (baselen > len)
+ return;
+
+ if (ieee802_11_parse_elems(mgmt->u.action.u.dls_resp.variable,
+ len - baselen, &elems) == ParseFailed) {
+ printk(KERN_ERR "DLS Parse support rates failed.\n");
+ return;
+ }
+ mode = local->sta_scanning ?
+ local->scan_hw_mode : local->oper_hw_mode;
+ rates = mode->rates;
+ num_rates = mode->num_rates;
+
+ for (i = 0; i < elems.supp_rates_len + elems.ext_supp_rates_len; i++) {
+ u8 rate = 0;
+ if (i < elems.supp_rates_len)
+ rate = elems.supp_rates[i];
+ else if (elems.ext_supp_rates)
+ rate = elems.ext_supp_rates[i - elems.supp_rates_len];
+ rate = 5 * (rate & 0x7f);
+ if (mode->mode == MODE_ATHEROS_TURBO)
+ rate *= 2;
+ for (j = 0; j < num_rates; j++)
+ if (rates[j].rate == rate)
+ supp_rates |= BIT(j);
+ }
+ dls->supp_rates = supp_rates;
+ dls->status = DLS_STATUS_OK;
+ dls_info_put(dls);
+}
+
+
+static void sta_process_dls_teardown(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ struct ieee80211_mgmt *mgmt, size_t len)
+{
+ u8 *src = mgmt->u.action.u.dls_teardown.src;
+
+ printk(KERN_DEBUG "DLS Teardown received from "
+ "%02X:%02X:%02X:%02X:%02X:%02X. Reason %d\n",
+ src[0], src[1], src[2], src[3], src[4], src[5],
+ mgmt->u.action.u.dls_teardown.reason_code);
+
+ dls_info_del(ifsta, src);
+ return;
+}
+
/* Caller must hold local->sta_bss_lock */
static void __ieee80211_rx_bss_hash_add(struct net_device *dev,
@@ -1803,7 +2584,7 @@ static void ieee80211_send_addba_resp(struct net_device *dev,
skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer "
- "for addts frame\n", dev->name);
+ "for addba resp frame\n", dev->name);
return;
}
@@ -1837,12 +2618,82 @@ static void ieee80211_rx_mgmt_action(struct net_device *dev,
struct ieee80211_mgmt *mgmt,
size_t len)
{
+ u8 prefix = 0;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_elem_tspec tspec;
if (len < IEEE80211_MIN_ACTION_SIZE)
return;
switch (mgmt->u.action.category) {
+ case WLAN_CATEGORY_QOS:
+ case WLAN_CATEGORY_WMM:
+ if (len < 24 + 4) {
+ printk(KERN_DEBUG "%s: too short (%zd) QoS category "
+ "frame received from " MAC_FMT " - ignored\n",
+ dev->name, len, MAC_ARG(mgmt->sa));
+ return;
+ }
+ switch (mgmt->u.action.u.wme_action.action_code) {
+ case WLAN_ACTION_QOS_ADDTS_REQ:
+ printk(KERN_DEBUG "%s: WLAN_ACTION_QOS_ADDTS_REQ "
+ "received in Non-AP STA mode!\n", dev->name);
+ return;
+ case WLAN_ACTION_QOS_ADDTS_RESP:
+ if (mgmt->u.action.u.wme_action.status_code == 47) {
+ /* TODO: handle TS Delay */
+ prefix = 6;
+ }
+ /* TODO: handle TCLAS, TCLAS Porcessing here */
+
+ if (mgmt->u.action.u.wme_action.status_code == 0) {
+ /* TODO: handle Schedule */
+ sta_parse_tspec(dev, ifsta, mgmt, len,
+ prefix, &tspec);
+ sta_update_tspec(local, ifsta,
+ WLAN_ACTION_QOS_ADDTS_RESP,
+ &tspec);
+ mod_timer(&ifsta->admit_timer, jiffies +
+ ifsta->dot11EDCAAveragingPeriod * HZ);
+ }
+ break;
+ case WLAN_ACTION_QOS_DELTS:
+ sta_parse_tspec(dev, ifsta, mgmt, len, prefix, &tspec);
+ sta_update_tspec(local, ifsta,
+ WLAN_ACTION_QOS_DELTS, &tspec);
+ break;
+ default:
+ printk(KERN_ERR "%s: unsupported QoS action code %d\n",
+ dev->name,
+ mgmt->u.action.u.wme_action.action_code);
+ break;
+ }
+ break;
+
+ case WLAN_CATEGORY_DLS:
+ if (len < 24 + 16) {
+ printk(KERN_DEBUG "%s: too short (%zd) DLS category "
+ "frame received from " MAC_FMT " - ignored\n",
+ dev->name, len, MAC_ARG(mgmt->sa));
+ return;
+ }
+ switch (mgmt->u.action.u.dls_req.action_code) {
+ case WLAN_ACTION_DLS_REQ:
+ sta_process_dls_req(dev, ifsta, mgmt, len);
+ break;
+ case WLAN_ACTION_DLS_RESP:
+ sta_process_dls_resp(dev, ifsta, mgmt, len);
+ break;
+ case WLAN_ACTION_DLS_TEARDOWN:
+ sta_process_dls_teardown(dev, ifsta, mgmt, len);
+ break;
+ default:
+ printk(KERN_ERR "%s: unsupported DLS action code %d\n",
+ dev->name, mgmt->u.action.u.dls_req.action_code);
+ break;
+ }
+ break;
+
case WLAN_CATEGORY_BACK:
switch (mgmt->u.action.u.addba_req.action_code) {
case WLAN_ACTION_ADDBA_REQ:
@@ -2148,6 +2999,43 @@ void ieee80211_sta_work(struct work_struct *work)
}
+void ieee80211_admit_refresh(unsigned long ptr)
+{
+ struct net_device *dev;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_sta *ifsta;
+ int i, j, find = 0;
+
+ dev = (struct net_device *) ptr;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ ifsta = &sdata->u.sta;
+
+ for (i = 0; i < STA_TSID_NUM; i++) {
+ for (j = 0; j < STA_TSDIR_NUM; j++) {
+ if ((ifsta->ts_data[i][j].status != TS_STATUS_ACTIVE) &&
+ (ifsta->ts_data[i][j].status != TS_STATUS_THROTTLING))
+ continue;
+ find = 1;
+
+ ifsta->ts_data[i][j].used_time_usec -=
+ ifsta->ts_data[i][j].admitted_time_usec;
+ if ((s32)(ifsta->ts_data[i][j].used_time_usec) < 0)
+ ifsta->ts_data[i][j].used_time_usec = 0;
+
+ ifsta->ts_data[i][j].status =
+ (ifsta->ts_data[i][j].used_time_usec >=
+ ifsta->ts_data[i][j].admitted_time_usec) ?
+ TS_STATUS_THROTTLING :
+ TS_STATUS_ACTIVE;
+ }
+ }
+
+ if (find)
+ mod_timer(&ifsta->admit_timer, jiffies +
+ ifsta->dot11EDCAAveragingPeriod * HZ);
+}
+
+
static void ieee80211_sta_reset_auth(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index ef3b3b0..9ff35e8 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -166,11 +166,13 @@ static inline int wme_downgrade_ac(struct sk_buff *skb)
static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd)
{
struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(qd->dev);
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct ieee80211_tx_packet_data *pkt_data =
(struct ieee80211_tx_packet_data *) skb->cb;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
unsigned short fc = le16_to_cpu(hdr->frame_control);
- int qos;
+ int qos, tsid, dir;
const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 };
/* see if frame is data or non data frame */
@@ -197,14 +199,38 @@ static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd)
}
/* use the data classifier to determine what 802.1d tag the
- * data frame has */
+ * data frame has */
skb->priority = classify_1d(skb, qd);
+ tsid = 8 + skb->priority;
+
+ /* FIXME: only uplink needs to be checked for Tx */
+ dir = STA_TS_UPLINK;
+
+ if ((sdata->type == IEEE80211_IF_TYPE_STA) &&
+ (local->wmm_acm & BIT(skb->priority))) {
+ switch (ifsta->ts_data[tsid][dir].status) {
+ case TS_STATUS_ACTIVE:
+ /* if TS Management is enabled, update used_time */
+ ifsta->ts_data[tsid][dir].used_time_usec +=
+ ifsta->MPDUExchangeTime;
+ break;
+ case TS_STATUS_THROTTLING:
+ /* if admitted time is used up, refuse to send more */
+ if (net_ratelimit())
+ printk(KERN_DEBUG "QoS packet throttling\n");
+ break;
+ default:
+ break;
+ }
+ }
- /* incase we are a client verify acm is not set for this ac */
- while (unlikely(local->wmm_acm & BIT(skb->priority))) {
+ /* in case we are a client verify acm is not set for this ac */
+ while ((local->wmm_acm & BIT(skb->priority)) &&
+ !((sdata->type == IEEE80211_IF_TYPE_STA) &&
+ (ifsta->ts_data[skb->priority + EDCA_TSID_MIN][dir].status
+ == TS_STATUS_ACTIVE))) {
if (wme_downgrade_ac(skb)) {
- /* No AC with lower priority has acm=0,
- * drop packet. */
+ /* No AC with lower priority has acm=0, drop packet. */
return -1;
}
}
next reply other threads:[~2007-05-14 5:15 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-05-14 5:15 Zhu Yi [this message]
2007-05-24 6:42 ` [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support Michael Wu
2007-05-24 7:34 ` Zhu Yi
2007-05-24 7:39 ` Michael Wu
2007-05-24 7:56 ` Zhu Yi
-- strict thread matches above, loose matches on Subject: below --
2007-05-25 11:52 Zhu Yi
2007-06-06 8:22 Zhu Yi
2007-06-06 19:18 ` Johannes Berg
2007-06-11 4:11 ` Zhu Yi
2007-06-11 8:25 ` Johannes Berg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20070514051526.GA25582@mail.intel.com \
--to=yi.zhu@intel.com \
--cc=linux-wireless@vger.kernel.org \
--cc=linville@tuxdriver.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).