* [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
@ 2007-05-14 5:15 Zhu Yi
2007-05-24 6:42 ` Michael Wu
0 siblings, 1 reply; 10+ messages in thread
From: Zhu Yi @ 2007-05-14 5:15 UTC (permalink / raw)
To: linux-wireless, John W. Linville
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;
}
}
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
2007-05-14 5:15 Zhu Yi
@ 2007-05-24 6:42 ` Michael Wu
2007-05-24 7:34 ` Zhu Yi
0 siblings, 1 reply; 10+ messages in thread
From: Michael Wu @ 2007-05-24 6:42 UTC (permalink / raw)
To: yi.zhu; +Cc: linux-wireless, John W. Linville, Jiri Benc
[-- Attachment #1: Type: text/plain, Size: 1533 bytes --]
On Sunday 13 May 2007 22:15, Zhu Yi wrote:
> +#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;
> +};
> +
The dls_info code looks like it was copied from the sta_info code and modified
to store what you need. No good.
Instead, you should modify struct sta_info to hold the information necessary.
This also allows the rate control to work properly. AFAICT, mac80211 will
send all frames in DLS at the lowest rate because this code does not set up a
sta_info for its destination. An appropriate sta_info entry is also necessary
to be able to set up encryption properly. (of course, user space can also set
up a sta_info entry after setting up a DLS entry and make things work
correctly.. but that's dumb)
There is also a need for some way to notify the low level driver that it
should not attempt any power management after a direct link is established
(or that it should, once all DLS links are torn down). This callback/hook is
also necessary to configure PS in hardware correctly in general and get rid
of the management frame sniffing that iwlwifi is currently doing to achieve
this.
> + if (dls_link_status(&sdata->u.sta, hdr.addr1) == DLS_STATUS_OK){
How is this line suppose to work? I don't see hdr.addr1 initialized at any
point before this.
-Michael Wu
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
2007-05-24 6:42 ` Michael Wu
@ 2007-05-24 7:34 ` Zhu Yi
2007-05-24 7:39 ` Michael Wu
0 siblings, 1 reply; 10+ messages in thread
From: Zhu Yi @ 2007-05-24 7:34 UTC (permalink / raw)
To: Michael Wu; +Cc: linux-wireless, John W. Linville, Jiri Benc
On Wed, 2007-05-23 at 23:42 -0700, Michael Wu wrote:
> The dls_info code looks like it was copied from the sta_info code and modified
> to store what you need. No good.
>
> Instead, you should modify struct sta_info to hold the information necessary.
OK.
> > + if (dls_link_status(&sdata->u.sta, hdr.addr1) == DLS_STATUS_OK){
> How is this line suppose to work? I don't see hdr.addr1 initialized at any
> point before this.
DLS must be setup before it can be used. The addr1 is inserted into the
dls hash table when the QSTA received a successful DLS response or
accepted a DLS request.
Thanks,
-yi
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
2007-05-24 7:34 ` Zhu Yi
@ 2007-05-24 7:39 ` Michael Wu
2007-05-24 7:56 ` Zhu Yi
0 siblings, 1 reply; 10+ messages in thread
From: Michael Wu @ 2007-05-24 7:39 UTC (permalink / raw)
To: Zhu Yi; +Cc: linux-wireless, John W. Linville, Jiri Benc
[-- Attachment #1: Type: text/plain, Size: 615 bytes --]
On Thursday 24 May 2007 00:34, Zhu Yi wrote:
> > > + if (dls_link_status(&sdata->u.sta, hdr.addr1) == DLS_STATUS_OK){
> >
> > How is this line suppose to work? I don't see hdr.addr1 initialized at
> > any point before this.
>
> DLS must be setup before it can be used. The addr1 is inserted into the
> dls hash table when the QSTA received a successful DLS response or
> accepted a DLS request.
>
AFAICT, dls_link_status uses the second argument to look up the dls entry for
the destination address. At this point, hdr.addr1 is not initialized to
anything, so something is wrong here.
-Michael Wu
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
2007-05-24 7:39 ` Michael Wu
@ 2007-05-24 7:56 ` Zhu Yi
0 siblings, 0 replies; 10+ messages in thread
From: Zhu Yi @ 2007-05-24 7:56 UTC (permalink / raw)
To: Michael Wu; +Cc: linux-wireless, John W. Linville, Jiri Benc
On Thu, 2007-05-24 at 00:39 -0700, Michael Wu wrote:
> AFAICT, dls_link_status uses the second argument to look up the dls
> entry for the destination address. At this point, hdr.addr1 is not
> initialized to anything, so something is wrong here.
You are right. It should be
dls_link_status(&sdata->u.sta, skb->data);
Thanks,
-yi
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
@ 2007-05-25 11:52 Zhu Yi
0 siblings, 0 replies; 10+ messages in thread
From: Zhu Yi @ 2007-05-25 11:52 UTC (permalink / raw)
To: linux-wireless, John W. Linville, Michael Wu
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>
---
net/mac80211/ieee80211.c | 18 +-
net/mac80211/ieee80211_i.h | 53 +++
net/mac80211/ieee80211_iface.c | 8 +
net/mac80211/ieee80211_sta.c | 792 +++++++++++++++++++++++++++++++++++++++-
net/mac80211/sta_info.c | 34 ++-
net/mac80211/sta_info.h | 10 +-
net/mac80211/wme.c | 38 ++-
7 files changed, 931 insertions(+), 22 deletions(-)
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 805c49a..e903197 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(local, skb->data) == 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,7 @@ 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);
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..3d40645 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -59,6 +59,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 +182,19 @@ 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;
+};
+
typedef ieee80211_txrx_result (*ieee80211_tx_handler)
(struct ieee80211_txrx_data *tx);
@@ -222,6 +239,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 +289,13 @@ 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];
};
@@ -640,6 +665,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 +804,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 +825,28 @@ 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,
+ u8 *addr, u32 timeout);
+void ieee80211_send_dls_teardown(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ u8 *mac, u16 reason);
+struct sta_info *dls_info_get(struct ieee80211_local *local, u8 *addr);
+void dls_info_add(struct ieee80211_if_sta *ifsta, struct sta_info *dls);
+void dls_info_stop(struct ieee80211_if_sta *ifsta);
+int dls_link_status(struct ieee80211_local *local, 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..c5c8bab 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,10 @@ 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;
+
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 6036f08..34f50bc 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,
+ u8 *addr, u32 timeout)
+{
+ 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, 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 = 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)
{
@@ -1284,6 +1707,258 @@ 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));
+}
+
+int dls_link_status(struct ieee80211_local *local, u8 *addr)
+{
+ struct sta_info *dls;
+ int ret = DLS_STATUS_NOLINK;
+
+ if ((dls = dls_info_get(local, addr)) != NULL) {
+ ret = dls->dls_status;
+ sta_info_put(dls);
+ }
+ return ret;
+}
+
+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 sta_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 = dls_info_get(local, src);
+ if (!dls)
+ dls = sta_info_add(local, dev, src, GFP_ATOMIC);
+ if (!dls)
+ return;
+
+ dls->dls_status = DLS_STATUS_OK;
+ dls->dls_timeout = le16_to_cpu(mgmt->u.action.u.dls_req.timeout);
+ dls->supp_rates = supp_rates;
+
+ /* 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 sta_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]);
+
+ 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);
+ 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 = dls_info_get(local, src);
+ if (!dls)
+ dls = sta_info_add(local, dev, src, GFP_ATOMIC);
+ if (!dls)
+ return;
+
+ dls->supp_rates = supp_rates;
+ dls->dls_status = DLS_STATUS_OK;
+ sta_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)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ u8 *src = mgmt->u.action.u.dls_teardown.src;
+ struct sta_info *dls;
+
+ 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 = dls_info_get(local, src);
+ if (dls)
+ sta_info_free(dls, 0);
+ return;
+}
+
/* Caller must hold local->sta_bss_lock */
static void __ieee80211_rx_bss_hash_add(struct net_device *dev,
@@ -1805,7 +2480,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;
}
@@ -1839,12 +2514,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:
@@ -2150,6 +2895,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/sta_info.c b/net/mac80211/sta_info.c
index ab7b1f0..3d21ea0 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -33,7 +33,7 @@ static void sta_info_hash_add(struct ieee80211_local *local,
/* Caller must hold local->sta_lock */
static void sta_info_hash_del(struct ieee80211_local *local,
- struct sta_info *sta)
+ struct sta_info *sta, int dls)
{
struct sta_info *s;
@@ -41,15 +41,19 @@ static void sta_info_hash_del(struct ieee80211_local *local,
if (!s)
return;
if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) {
+ if (dls && !s->dls_sta)
+ return;
local->sta_hash[STA_HASH(sta->addr)] = s->hnext;
return;
}
while (s->hnext && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
s = s->hnext;
- if (s->hnext)
+ if (s->hnext) {
+ if (dls && !s->hnext->dls_sta)
+ return;
s->hnext = s->hnext->hnext;
- else
+ } else
printk(KERN_ERR "%s: could not remove STA " MAC_FMT " from "
"hash table\n", local->mdev->name, MAC_ARG(sta->addr));
}
@@ -78,6 +82,28 @@ struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
}
EXPORT_SYMBOL(sta_info_get);
+struct sta_info *dls_info_get(struct ieee80211_local *local, u8 *addr)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&local->sta_lock);
+ sta = local->sta_hash[STA_HASH(addr)];
+ while (sta) {
+ if (memcmp(sta->addr, addr, ETH_ALEN) == 0) {
+ if (!sta->dls_sta) {
+ sta = NULL;
+ break;
+ }
+ __sta_info_get(sta);
+ break;
+ }
+ sta = sta->hnext;
+ }
+ spin_unlock_bh(&local->sta_lock);
+
+ return sta;
+}
+
int sta_info_min_txrate_get(struct ieee80211_local *local)
{
struct sta_info *sta;
@@ -218,7 +244,7 @@ static void sta_info_remove(struct sta_info *sta)
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata;
- sta_info_hash_del(local, sta);
+ sta_info_hash_del(local, sta, 0);
list_del(&sta->list);
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
if (sta->flags & WLAN_STA_PS) {
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 6a067a0..3eb3649 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -102,8 +102,14 @@ struct sta_info {
#ifdef CONFIG_MAC80211_DEBUGFS
int debugfs_registered;
#endif
- int assoc_ap; /* whether this is an AP that we are
- * associated with as a client */
+ int assoc_ap:1; /* whether this is an AP that we are
+ * associated with as a client */
+ int dls_sta:1; /* whether this stations is a DLS peer of us */
+
+#define DLS_STATUS_OK 0
+#define DLS_STATUS_NOLINK 1
+ int dls_status;
+ u32 dls_timeout;
#ifdef CONFIG_HOSTAPD_WPA_TESTING
u32 wpa_trigger;
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;
}
}
--
1.5.0.rc2.g73a2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
@ 2007-06-06 8:22 Zhu Yi
2007-06-06 19:18 ` Johannes Berg
0 siblings, 1 reply; 10+ messages in thread
From: Zhu Yi @ 2007-06-06 8:22 UTC (permalink / raw)
To: linux-wireless, John W. Linville, Michael Wu
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>
---
include/linux/ieee80211.h | 10 +
net/mac80211/ieee80211.c | 18 +-
net/mac80211/ieee80211_i.h | 53 +++
net/mac80211/ieee80211_iface.c | 8 +
net/mac80211/ieee80211_sta.c | 813 +++++++++++++++++++++++++++++++++++++++-
net/mac80211/sta_info.c | 34 ++-
net/mac80211/sta_info.h | 10 +-
net/mac80211/wme.c | 38 ++-
8 files changed, 963 insertions(+), 21 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index cd91ade..c83b8d3 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -401,6 +401,16 @@ enum ieee80211_reasoncode {
WLAN_REASON_QSTA_CIPHER_NOT_SUPP = 45,
};
+
+/* Category Code */
+enum ieee80211_category {
+ WLAN_CATEGORY_SPECTRUM_MGMT = 0,
+ WLAN_CATEGORY_QOS = 1,
+ WLAN_CATEGORY_DLS = 2,
+ WLAN_CATEGORY_BACK = 3,
+ WLAN_CATEGORY_WMM = 17,
+};
+
/* QoS Action Code */
enum ieee80211_qos_actioncode {
WLAN_ACTION_QOS_ADDTS_REQ = 0,
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 4e84f24..b0ff8ff 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(local, skb->data) == 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,7 @@ 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);
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 af4d14d..c6c3a25 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -59,6 +59,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;
@@ -171,6 +175,19 @@ 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;
+};
+
typedef ieee80211_txrx_result (*ieee80211_tx_handler)
(struct ieee80211_txrx_data *tx);
@@ -215,6 +232,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;
@@ -263,6 +281,13 @@ 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];
};
@@ -629,6 +654,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)
{
@@ -758,6 +788,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);
@@ -778,6 +809,28 @@ 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,
+ u8 *addr, u32 timeout);
+void ieee80211_send_dls_teardown(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ u8 *mac, u16 reason);
+struct sta_info *dls_info_get(struct ieee80211_local *local, u8 *addr);
+void dls_info_add(struct ieee80211_if_sta *ifsta, struct sta_info *dls);
+void dls_info_stop(struct ieee80211_if_sta *ifsta);
+int dls_link_status(struct ieee80211_local *local, 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 cf0f32e..9dbafdb 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;
@@ -190,6 +194,10 @@ 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;
+
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 9f30ae4..0fbfab6 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -59,6 +59,9 @@
#define ERP_INFO_USE_PROTECTION BIT(1)
+/* mgmt header + 1 byte action code */
+#define IEEE80211_MIN_ACTION_SIZE (24 + 1)
+
static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst,
u8 *ssid, size_t ssid_len);
static struct ieee80211_sta_bss *
@@ -104,6 +107,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;
@@ -172,17 +177,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;
}
}
}
@@ -199,6 +221,14 @@ static ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
elems->ext_supp_rates = pos;
elems->ext_supp_rates_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 "
@@ -660,6 +690,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,
+ u8 *addr, u32 timeout)
+{
+ 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, 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 = 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)
{
@@ -1243,6 +1669,258 @@ 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));
+}
+
+int dls_link_status(struct ieee80211_local *local, u8 *addr)
+{
+ struct sta_info *dls;
+ int ret = DLS_STATUS_NOLINK;
+
+ if ((dls = dls_info_get(local, addr)) != NULL) {
+ ret = dls->dls_status;
+ sta_info_put(dls);
+ }
+ return ret;
+}
+
+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 sta_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 = dls_info_get(local, src);
+ if (!dls)
+ dls = sta_info_add(local, dev, src, GFP_ATOMIC);
+ if (!dls)
+ return;
+
+ dls->dls_status = DLS_STATUS_OK;
+ dls->dls_timeout = le16_to_cpu(mgmt->u.action.u.dls_req.timeout);
+ dls->supp_rates = supp_rates;
+
+ /* 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 sta_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]);
+
+ 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);
+ 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 = dls_info_get(local, src);
+ if (!dls)
+ dls = sta_info_add(local, dev, src, GFP_ATOMIC);
+ if (!dls)
+ return;
+
+ dls->supp_rates = supp_rates;
+ dls->dls_status = DLS_STATUS_OK;
+ sta_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)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ u8 *src = mgmt->u.action.u.dls_teardown.src;
+ struct sta_info *dls;
+
+ 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 = dls_info_get(local, src);
+ if (dls)
+ sta_info_free(dls, 0);
+ return;
+}
+
/* Caller must hold local->sta_bss_lock */
static void __ieee80211_rx_bss_hash_add(struct net_device *dev,
@@ -1733,6 +2411,93 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
}
+static void ieee80211_rx_mgmt_action(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta,
+ 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;
+
+ default:
+ break;
+ }
+}
+
+
void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
struct ieee80211_rx_status *rx_status)
{
@@ -1818,6 +2583,9 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
case IEEE80211_STYPE_DISASSOC:
ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len);
break;
+ case IEEE80211_STYPE_ACTION:
+ ieee80211_rx_mgmt_action(dev, ifsta, mgmt, skb->len);
+ break;
}
kfree_skb(skb);
@@ -1991,6 +2759,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/sta_info.c b/net/mac80211/sta_info.c
index ab7b1f0..3d21ea0 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -33,7 +33,7 @@ static void sta_info_hash_add(struct ieee80211_local *local,
/* Caller must hold local->sta_lock */
static void sta_info_hash_del(struct ieee80211_local *local,
- struct sta_info *sta)
+ struct sta_info *sta, int dls)
{
struct sta_info *s;
@@ -41,15 +41,19 @@ static void sta_info_hash_del(struct ieee80211_local *local,
if (!s)
return;
if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) {
+ if (dls && !s->dls_sta)
+ return;
local->sta_hash[STA_HASH(sta->addr)] = s->hnext;
return;
}
while (s->hnext && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
s = s->hnext;
- if (s->hnext)
+ if (s->hnext) {
+ if (dls && !s->hnext->dls_sta)
+ return;
s->hnext = s->hnext->hnext;
- else
+ } else
printk(KERN_ERR "%s: could not remove STA " MAC_FMT " from "
"hash table\n", local->mdev->name, MAC_ARG(sta->addr));
}
@@ -78,6 +82,28 @@ struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
}
EXPORT_SYMBOL(sta_info_get);
+struct sta_info *dls_info_get(struct ieee80211_local *local, u8 *addr)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&local->sta_lock);
+ sta = local->sta_hash[STA_HASH(addr)];
+ while (sta) {
+ if (memcmp(sta->addr, addr, ETH_ALEN) == 0) {
+ if (!sta->dls_sta) {
+ sta = NULL;
+ break;
+ }
+ __sta_info_get(sta);
+ break;
+ }
+ sta = sta->hnext;
+ }
+ spin_unlock_bh(&local->sta_lock);
+
+ return sta;
+}
+
int sta_info_min_txrate_get(struct ieee80211_local *local)
{
struct sta_info *sta;
@@ -218,7 +244,7 @@ static void sta_info_remove(struct sta_info *sta)
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata;
- sta_info_hash_del(local, sta);
+ sta_info_hash_del(local, sta, 0);
list_del(&sta->list);
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
if (sta->flags & WLAN_STA_PS) {
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index b5591d2..8ed61a4 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -101,8 +101,14 @@ struct sta_info {
#ifdef CONFIG_MAC80211_DEBUGFS
int debugfs_registered;
#endif
- int assoc_ap; /* whether this is an AP that we are
- * associated with as a client */
+ int assoc_ap:1; /* whether this is an AP that we are
+ * associated with as a client */
+ int dls_sta:1; /* whether this stations is a DLS peer of us */
+
+#define DLS_STATUS_OK 0
+#define DLS_STATUS_NOLINK 1
+ int dls_status;
+ u32 dls_timeout;
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES];
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 89ce815..19acd1a 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -158,11 +158,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 */
@@ -189,14 +191,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;
}
}
--
1.5.0.rc2.g73a2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
2007-06-06 8:22 Zhu Yi
@ 2007-06-06 19:18 ` Johannes Berg
2007-06-11 4:11 ` Zhu Yi
0 siblings, 1 reply; 10+ messages in thread
From: Johannes Berg @ 2007-06-06 19:18 UTC (permalink / raw)
To: yi.zhu; +Cc: linux-wireless, John W. Linville, Michael Wu
[-- Attachment #1: Type: text/plain, Size: 3551 bytes --]
Some nitpicking below :)
> + if (dls_link_status(local, skb->data) == DLS_STATUS_OK){
missing space there, but maybe the line is too long with it?
> + case WIFI_OUI_STYPE_WMM_TSPEC:
> + if (elen != 61) {
> + printk(KERN_ERR "Wrong "
> + "TSPEC size.\n");
> + break;
That printk should be rate limited.
> + default:
> + //printk(KERN_ERR "Unsupported "
> + // "WiFi OUI %d\n", pos[4]);
> + break;
And maybe that should be there for the debug case (rate limited as
well)?
> + case WLAN_EID_TSPEC:
> + if (elen != 55) {
> + printk(KERN_ERR "Wrong TSPEC size.\n");
> + break;
Ditto.
> + if (ifsta->ts_data[tsid][index].status == TS_STATUS_UNUSED) {
> + printk(KERN_DEBUG "%s: Tring to delete an ACM disabled TS "
typo "Tring".
Can this be invoked by somebody requesting you to do it? If so it should
be rate limited as well. I haven't quite understood the code flow here
yet.
> + if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems)
> + == ParseFailed) {
> + printk(KERN_DEBUG "%s: failed to parse TSPEC\n", dev->name);
> + return;
Wants rate limiting too, afaict.
> + 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]);
Again?
> + 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;
and again?
> + 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]);
> +
> + 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);
> + return;
> + }
I suppose these are ok since we're not going to request DLS a lot,
right?
> + 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 = dls_info_get(local, src);
> + if (dls)
> + sta_info_free(dls, 0);
Do we really want to free the sta info here? Shouldn't we have a sta
info item for the DLS peer anyway, and then we can't free it here but
rather reset the DLS status?
> + 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));
rate limit.
> + printk(KERN_ERR "%s: unsupported QoS action code %d\n",
> + dev->name,
> + mgmt->u.action.u.wme_action.action_code);
rate limit.
> + printk(KERN_DEBUG "%s: too short (%zd) DLS category "
> + "frame received from " MAC_FMT " - ignored\n",
> + dev->name, len, MAC_ARG(mgmt->sa));
ditto.
> + printk(KERN_ERR "%s: unsupported DLS action code %d\n",
> + dev->name, mgmt->u.action.u.dls_req.action_code);
ditto.
> +struct sta_info *dls_info_get(struct ieee80211_local *local, u8 *addr)
Ok, I don't understand the DLS info stuff. Basically it's a sta info,
but why not just do sta_info_get() on the regular one and then set the
dls peer bit?
> + if (net_ratelimit())
> + printk(KERN_DEBUG "QoS packet throttling\n");
Hey! :)
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
2007-06-06 19:18 ` Johannes Berg
@ 2007-06-11 4:11 ` Zhu Yi
2007-06-11 8:25 ` Johannes Berg
0 siblings, 1 reply; 10+ messages in thread
From: Zhu Yi @ 2007-06-11 4:11 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, John W. Linville, Michael Wu
On Wed, 2007-06-06 at 21:18 +0200, Johannes Berg wrote:
> Some nitpicking below :)
>
> > + if (dls_link_status(local, skb->data) == DLS_STATUS_OK){
>
> missing space there, but maybe the line is too long with it?
Yup. The line happens to be 80 originally before Michael pointed a bug
here. I forgot to add the space back after that.
> > + case WIFI_OUI_STYPE_WMM_TSPEC:
> > + if (elen != 61) {
> > + printk(KERN_ERR "Wrong "
> > + "TSPEC size.\n");
> > + break;
>
> That printk should be rate limited.
Umm, I followed the same way as other MLME parsing functions ie.
ieee80211_rx_mgmt_assoc_resp. It seems they also didn't rate limit these
cases. This only happens on broken APs. Do you think we really need to
rate limit here?
> > + default:
> > + //printk(KERN_ERR "Unsupported "
> > + // "WiFi OUI %d\n", pos[4]);
> > + break;
>
> And maybe that should be there for the debug case (rate limited as
> well)?
I got a lot of this message during my testing. The APs use some
(private) OUIs we don't support now. I guess there will be quite a lot
messages even if we rate limit here.
> > + case WLAN_EID_TSPEC:
> > + if (elen != 55) {
> > + printk(KERN_ERR "Wrong TSPEC size.\n");
> > + break;
>
> Ditto.
>
> > + if (ifsta->ts_data[tsid][index].status == TS_STATUS_UNUSED) {
> > + printk(KERN_DEBUG "%s: Tring to delete an ACM disabled TS "
>
> typo "Tring".
Thanks!
> Can this be invoked by somebody requesting you to do it? If so it should
> be rate limited as well. I haven't quite understood the code flow here
> yet.
>
> > + if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems)
> > + == ParseFailed) {
> > + printk(KERN_DEBUG "%s: failed to parse TSPEC\n", dev->name);
> > + return;
>
> Wants rate limiting too, afaict.
mac80211 doesn't rate limit ParseFailed cases in other parts. ie.
ieee80211_rx_mgmt_assoc_resp, etc.
> > + 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]);
>
> Again?
This management packet is sent from the AP. Not from the STA user
requests DLS connection directly. So can I compare this to the following
line in ieee80211_rx_mgmt_assoc_resp().
printk(KERN_DEBUG "%s: RX %sssocResp from " MAC_FMT " (capab=0x%x "
"status=%d aid=%d)\n",
dev->name, reassoc ? "Rea" : "A", MAC_ARG(mgmt->sa),
capab_info, status_code, aid);
> > + 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;
>
> and again?
See above.
> > + 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]);
> > +
> > + 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);
> > + return;
> > + }
>
> I suppose these are ok since we're not going to request DLS a lot,
> right?
Yup.
> > + 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 = dls_info_get(local, src);
> > + if (dls)
> > + sta_info_free(dls, 0);
>
> Do we really want to free the sta info here? Shouldn't we have a sta
> info item for the DLS peer anyway, and then we can't free it here but
> rather reset the DLS status?
Yes we can do this. But free it here helps to shrink the hash table
size. The problem is: the original sta_info doesn't a status -- if a
sta_info appears in the hash table, it is active; otherwise it is not.
But since DLS requires setup stage, I added the status code to
distinguish setuping and active. But for inactive state, we can either
set the DLS status to inactive or remove the sta_info completely. Since
normally a DLS connection won't be setup and teardown that often, I
select to free the sta_info to indicate it as inactive.
> > + 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));
>
> rate limit.
See the following code in ieee80211_rx_mgmt_auth()
if (len < 24 + 6) {
printk(KERN_DEBUG "%s: too short (%zd) authentication
frame "
"received from " MAC_FMT " - ignored\n",
dev->name, len, MAC_ARG(mgmt->sa));
return;
}
> > + printk(KERN_ERR "%s: unsupported QoS action code %d\n",
> > + dev->name,
> > + mgmt->u.action.u.wme_action.action_code);
>
> rate limit.
There is no other QoS action code not supported by mac80211. If we see
any, we can fix it.
> > + printk(KERN_DEBUG "%s: too short (%zd) DLS category "
> > + "frame received from " MAC_FMT " - ignored\n",
> > + dev->name, len, MAC_ARG(mgmt->sa));
>
> ditto.
see above.
>
> > + printk(KERN_ERR "%s: unsupported DLS action code %d\n",
> > + dev->name, mgmt->u.action.u.dls_req.action_code);
>
> ditto.
see above.
> > +struct sta_info *dls_info_get(struct ieee80211_local *local, u8 *addr)
>
> Ok, I don't understand the DLS info stuff. Basically it's a sta info,
> but why not just do sta_info_get() on the regular one and then set the
> dls peer bit?
Do you mean below code instead?
struct sta_info *dls_info_get()
{
sta = sta_info_get(local, addr);
if (sta)
if (sta->dls_sta)
return sta;
else
sta_info_put(sta);
return NULL;
}
This is not optimized in the (sta && !sta->dls_sta) case.
> > + if (net_ratelimit())
> > + printk(KERN_DEBUG "QoS packet throttling\n");
>
> Hey! :)
Yeah, that's a hot path.
Thanks for your feedbacks!
-yi
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support
2007-06-11 4:11 ` Zhu Yi
@ 2007-06-11 8:25 ` Johannes Berg
0 siblings, 0 replies; 10+ messages in thread
From: Johannes Berg @ 2007-06-11 8:25 UTC (permalink / raw)
To: Zhu Yi; +Cc: linux-wireless, John W. Linville, Michael Wu
[-- Attachment #1: Type: text/plain, Size: 1913 bytes --]
On Mon, 2007-06-11 at 12:11 +0800, Zhu Yi wrote:
> > > + case WIFI_OUI_STYPE_WMM_TSPEC:
> > > + if (elen != 61) {
> > > + printk(KERN_ERR "Wrong "
> > > + "TSPEC size.\n");
> > > + break;
> >
> > That printk should be rate limited.
>
> Umm, I followed the same way as other MLME parsing functions ie.
> ieee80211_rx_mgmt_assoc_resp. It seems they also didn't rate limit these
> cases. This only happens on broken APs. Do you think we really need to
> rate limit here?
Ok, wrt. all the rate limiting I thought of. You rightfully pointed out
that the current code is just as broken, hence I'm ok with adding these
like this and I'll go through and fix them later. My rationale with many
of these is that somebody could inject random packets appearing to come
from our AP and cause us to write a log line for every packet they
inject by intentionally injecting malformed packets. We should protect
against that.
> I got a lot of this message during my testing. The APs use some
> (private) OUIs we don't support now. I guess there will be quite a lot
> messages even if we rate limit here.
Oh right, like those Broadcom IEs they use for a-mpdu even on .11G...
> Yes we can do this. But free it here helps to shrink the hash table
> size. The problem is: the original sta_info doesn't a status -- if a
> sta_info appears in the hash table, it is active; otherwise it is not.
> But since DLS requires setup stage, I added the status code to
> distinguish setuping and active. But for inactive state, we can either
> set the DLS status to inactive or remove the sta_info completely. Since
> normally a DLS connection won't be setup and teardown that often, I
> select to free the sta_info to indicate it as inactive.
Makes sense. I thought we had a sta info item for each peer we ever
discovered but I guess that isn't true.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2007-06-11 8:25 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-05-25 11:52 [PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support Zhu Yi
-- strict thread matches above, loose matches on Subject: below --
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
2007-05-14 5:15 Zhu Yi
2007-05-24 6:42 ` Michael Wu
2007-05-24 7:34 ` Zhu Yi
2007-05-24 7:39 ` Michael Wu
2007-05-24 7:56 ` Zhu Yi
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).