Search Linux Wireless

[PATCH 2/3] mac80211: IEEE802.11e/WMM TS management and DLS support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxx>
---
 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
-
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux