diff -ruN linux-2.6.22/include/linux/ieee80211.h linux-2.6.22-mac80211/include/linux/ieee80211.h
--- linux-2.6.22/include/linux/ieee80211.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/include/linux/ieee80211.h	2007-08-23 20:59:59.000000000 +0200
@@ -53,6 +53,8 @@
 #define IEEE80211_STYPE_ACTION		0x00D0
 
 /* control */
+#define IEEE80211_STYPE_BACK_REQ	0x0080
+#define IEEE80211_STYPE_BACK		0x0090
 #define IEEE80211_STYPE_PSPOLL		0x00A0
 #define IEEE80211_STYPE_RTS		0x00B0
 #define IEEE80211_STYPE_CTS		0x00C0
@@ -106,6 +108,83 @@
 } __attribute__ ((packed));
 
 
+struct ieee80211_ht_capability {
+	__le16 capabilities_info;
+	u8 mac_ht_params_info;
+	u8 supported_mcs_set[16];
+	__le16 extended_ht_capability_info;
+	__le32 tx_BF_capability_info;
+	u8 antenna_selection_info;
+}__attribute__ ((packed));
+
+struct ieee80211_ht_additional_info {
+	u8 control_chan;
+	u8 ht_param;
+	__le16 operation_mode;
+	__le16 stbc_param;
+	u8 basic_set[16];
+}__attribute__ ((packed));
+
+
+#define IEEE80211_TSINFO_TYPE(a)	((a.byte1 & 0x01) >> 0)
+#define IEEE80211_TSINFO_TSID(a)	((a.byte1 & 0x1E) >> 1)
+#define IEEE80211_TSINFO_DIR(a)		((a.byte1 & 0x60) >> 5)
+#define IEEE80211_TSINFO_POLICY(a)	((a.byte1 & 0x80) >> 7 + \
+					 (a.byte2 & 0x01) << 1)
+#define IEEE80211_TSINFO_AGG(a)		((a.byte2 & 0x02) >> 1)
+#define IEEE80211_TSINFO_APSD(a)	((a.byte2 & 0x04) >> 2)
+#define IEEE80211_TSINFO_UP(a)		((a.byte2 & 0x38) >> 3)
+#define IEEE80211_TSINFO_ACK(a)		((a.byte2 & 0xC0) >> 6)
+#define IEEE80211_TSINFO_SCHEDULE(a)	((a.byte3 & 0x01) >> 0)
+
+#define IEEE80211_SET_TSINFO_TYPE(i, d)		(i.byte1 |= (d << 0) & 0x01)
+#define IEEE80211_SET_TSINFO_TSID(i, d)		(i.byte1 |= (d << 1) & 0x1E)
+#define IEEE80211_SET_TSINFO_DIR(i, d)		(i.byte1 |= (d << 5) & 0x60)
+#define IEEE80211_SET_TSINFO_POLICY(i, d)	\
+do {						\
+						i.byte1 |= (d & 0x01) << 7; \
+						i.byte2 |= (d & 0x02) >> 1; \
+} while(0)
+#define IEEE80211_SET_TSINFO_AGG(i, d)		(i.byte2 |= (d << 1) & 0x02)
+#define IEEE80211_SET_TSINFO_APSD(i, d)		(i.byte2 |= (d << 2) & 0x04)
+#define IEEE80211_SET_TSINFO_UP(i, d)		(i.byte2 |= (d << 3) & 0x38)
+#define IEEE80211_SET_TSINFO_ACK(i, d)		(i.byte2 |= (d << 6) & 0xC0)
+#define IEEE80211_SET_TSINFO_SCHEDULE(i, d)	(i.byte3 |= (d << 0) & 0x01)
+
+struct ieee80211_ts_info {
+	u8 byte1;
+	u8 byte2;
+	u8 byte3;
+} __attribute__ ((packed));
+
+struct ieee80211_elem_tspec {
+	struct ieee80211_ts_info ts_info;
+	__le16 nominal_msdu_size;
+	__le16 max_msdu_size;
+	__le32 min_service_interval;
+	__le32 max_service_interval;
+	__le32 inactivity_interval;
+	__le32 suspension_interval;
+	__le32 service_start_time;
+	__le32 min_data_rate;
+	__le32 mean_data_rate;
+	__le32 peak_data_rate;
+	__le32 burst_size;
+	__le32 delay_bound;
+	__le32 min_phy_rate;
+	__le16 surplus_band_allow;
+	__le16 medium_time;
+} __attribute__ ((packed));
+
+struct ieee80211_bar {
+	__le16 frame_control;
+	__le16 duration;
+	__u8 ra[6];
+	__u8 ta[6];
+	__u16 control;
+	__u16 start_seq_num;
+} __attribute__((packed));
+
 struct ieee80211_mgmt {
 	__le16 frame_control;
 	__le16 duration;
@@ -173,9 +252,51 @@
 				struct {
 					u8 action_code;
 					u8 dialog_token;
+					u8 variable[0];
+				} __attribute__ ((packed)) addts_req;
+				struct {
+					u8 action_code;
+					u8 dialog_token;
+					__le16 status_code;
+					u8 variable[0];
+				} __attribute__ ((packed)) addts_resp;
+				struct {
+					u8 action_code;
+					struct ieee80211_ts_info ts_info;
+					__le16 reason_code;
+				} __attribute__ ((packed)) delts;
+				struct {
+					u8 action_code;
+					u8 dialog_token;
 					u8 status_code;
 					u8 variable[0];
 				} __attribute__ ((packed)) wme_action;
+				struct {
+					u8 action_code;
+					u8 dest[6];
+					u8 src[6];
+					__le16 capab_info;
+					__le16 timeout;
+					/* Followed by Supported Rates and
+					 * Extended Supported Rates */
+					u8 variable[0];
+				} __attribute__ ((packed)) dls_req;
+				struct {
+					u8 action_code;
+					__le16 status_code;
+					u8 dest[6];
+					u8 src[6];
+					/* Followed by Capability Information,
+					 * Supported Rates and Extended
+					 * Supported Rates */
+					u8 variable[0];
+				} __attribute__ ((packed)) dls_resp;
+				struct {
+					u8 action_code;
+					u8 dest[6];
+					u8 src[6];
+					__le16 reason_code;
+				} __attribute__ ((packed)) dls_teardown;
 				struct{
 					u8 action_code;
 					u8 element_id;
@@ -184,6 +305,25 @@
 					u8 new_chan;
 					u8 switch_count;
 				} __attribute__((packed)) chan_switch;
+				struct{
+					u8 action_code;
+					u8 dialog_token;
+					__le16 capab;
+					__le16 timeout;
+					__le16 start_seq_num;
+				} __attribute__((packed)) addba_req;
+				struct{
+					u8 action_code;
+					u8 dialog_token;
+					__le16 status;
+					__le16 capab;
+					__le16 timeout;
+				} __attribute__((packed)) addba_resp;
+				struct{
+					u8 action_code;
+					__le16 params;
+					__le16 reason_code;
+				}__attribute__((packed)) delba;
 			} u;
 		} __attribute__ ((packed)) action;
 	} u;
@@ -227,6 +367,17 @@
 #define WLAN_CAPABILITY_SHORT_SLOT_TIME	(1<<10)
 #define WLAN_CAPABILITY_DSSS_OFDM	(1<<13)
 
+/* 802.11g ERP information element */
+#define WLAN_ERP_NON_ERP_PRESENT (1<<0)
+#define WLAN_ERP_USE_PROTECTION (1<<1)
+#define WLAN_ERP_BARKER_PREAMBLE (1<<2)
+
+/* WLAN_ERP_BARKER_PREAMBLE values */
+enum {
+	WLAN_ERP_PREAMBLE_SHORT = 0,
+	WLAN_ERP_PREAMBLE_LONG = 1,
+};
+
 /* Status codes */
 enum ieee80211_statuscode {
 	WLAN_STATUS_SUCCESS = 0,
@@ -259,6 +410,18 @@
 	WLAN_STATUS_UNSUPP_RSN_VERSION = 44,
 	WLAN_STATUS_INVALID_RSN_IE_CAP = 45,
 	WLAN_STATUS_CIPHER_SUITE_REJECTED = 46,
+	/* 802.11e */
+	WLAN_STATUS_UNSPECIFIED_QOS = 32,
+	WLAN_STATUS_ASSOC_DENIED_NOBANDWIDTH = 33,
+	WLAN_STATUS_ASSOC_DENIED_LOWACK = 34,
+	WLAN_STATUS_ASSOC_DENIED_UNSUPP_QOS = 35,
+	WLAN_STATUS_REQUEST_DECLINED = 37,
+	WLAN_STATUS_INVALID_QOS_PARAM = 38,
+	WLAN_STATUS_CHANGE_TSPEC = 39,
+	WLAN_STATUS_WAIT_TS_DELAY = 47,
+	WLAN_STATUS_NO_DIRECT_LINK = 48,
+	WLAN_STATUS_STA_NOT_PRESENT = 49,
+	WLAN_STATUS_STA_NOT_QSTA = 50,
 };
 
 
@@ -289,8 +452,58 @@
 	WLAN_REASON_INVALID_RSN_IE_CAP = 22,
 	WLAN_REASON_IEEE8021X_FAILED = 23,
 	WLAN_REASON_CIPHER_SUITE_REJECTED = 24,
+	/* 802.11e */
+	WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32,
+	WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33,
+	WLAN_REASON_DISASSOC_LOW_ACK = 34,
+	WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP = 35,
+	WLAN_REASON_QSTA_LEAVE_QBSS = 36,
+	WLAN_REASON_QSTA_NOT_USE = 37,
+	WLAN_REASON_QSTA_REQUIRE_SETUP = 38,
+	WLAN_REASON_QSTA_TIMEOUT = 39,
+	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,
+};
+
+/* Spectrum Management Action Code */
+enum ieee80211_specmgmt_actioncode {
+	WLAN_ACTION_MEASURE_REQ = 0,
+	WLAN_ACTION_MEASURE_REPORT = 1,
+	WLAN_ACTION_TPC_REQ = 2,
+	WLAN_ACTION_TPC_REPORT = 3,
+	WLAN_ACTION_CSA = 4,
+};
+
+/* QoS Action Code */
+enum ieee80211_qos_actioncode {
+	WLAN_ACTION_QOS_ADDTS_REQ = 0,
+	WLAN_ACTION_QOS_ADDTS_RESP = 1,
+	WLAN_ACTION_QOS_DELTS = 2,
+	WLAN_ACTION_QOS_SCHEDULE = 3,
+};
+
+/* DLS Action Code */
+enum ieee80211_dls_actioncode {
+	WLAN_ACTION_DLS_REQ = 0,
+	WLAN_ACTION_DLS_RESP = 1,
+	WLAN_ACTION_DLS_TEARDOWN = 2,
 };
 
+/* BACK Action Code */
+enum ieee80211_back_actioncode {
+	WLAN_ACTION_ADDBA_REQ = 0,
+	WLAN_ACTION_ADDBA_RESP = 1,
+	WLAN_ACTION_DELBA = 2,
+};
 
 /* Information Element IDs */
 enum ieee80211_eid {
@@ -307,6 +520,15 @@
 	WLAN_EID_HP_PARAMS = 8,
 	WLAN_EID_HP_TABLE = 9,
 	WLAN_EID_REQUEST = 10,
+	/* 802.11e */
+	WLAN_EID_QBSS_LOAD = 11,
+	WLAN_EID_EDCA_PARAM_SET = 12,
+	WLAN_EID_TSPEC = 13,
+	WLAN_EID_TCLAS = 14,
+	WLAN_EID_SCHEDULE = 15,
+	WLAN_EID_TS_DELAY = 43,
+	WLAN_EID_TCLAS_PROCESSING = 44,
+	WLAN_EID_QOS_CAPA = 46,
 	/* 802.11h */
 	WLAN_EID_PWR_CONSTRAINT = 32,
 	WLAN_EID_PWR_CAPABILITY = 33,
@@ -321,6 +543,9 @@
 	/* 802.11g */
 	WLAN_EID_ERP_INFO = 42,
 	WLAN_EID_EXT_SUPP_RATES = 50,
+	/* 802.11n */
+	WLAN_EID_HT_CAPABILITY = 45,
+	WLAN_EID_HT_EXTRA_INFO = 61,
 	/* 802.11i */
 	WLAN_EID_RSN = 48,
 	WLAN_EID_WPA = 221,
@@ -329,6 +554,9 @@
 	WLAN_EID_QOS_PARAMETER = 222
 };
 
+/* 80211n */
+#define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080
+
 /* cipher suite selectors */
 #define WLAN_CIPHER_SUITE_USE_GROUP	0x000FAC00
 #define WLAN_CIPHER_SUITE_WEP40		0x000FAC01
@@ -339,4 +567,37 @@
 
 #define WLAN_MAX_KEY_LEN		32
 
+enum ieee80211_tsinfo_direction {
+	WLAN_TSINFO_UPLINK = 0,
+	WLAN_TSINFO_DOWNLINK = 1,
+	WLAN_TSINFO_DIRECTLINK = 2,
+	WLAN_TSINFO_BIDIRECTIONAL = 3,
+};
+
+enum ieee80211_tsinfo_access {
+	WLAN_TSINFO_EDCA = 1,
+	WLAN_TSINFO_HCCA = 2,
+	WLAN_TSINFO_HEMM = 3,
+};
+
+enum ieee80211_tsinfo_psb {
+	WLAN_TSINFO_PSB_LEGACY = 0,
+	WLAN_TSINFO_PSB_APSD = 1,
+};
+
+
+/* WI-FI Alliance OUI Type and Subtype */
+enum wifi_oui_type {
+	WIFI_OUI_TYPE_WPA = 1,
+	WIFI_OUI_TYPE_WMM = 2,
+	WIFI_OUI_TYPE_WSC = 4,
+	WIFI_OUI_TYPE_PSD = 6,
+};
+
+enum wifi_oui_stype_wmm {
+	WIFI_OUI_STYPE_WMM_INFO = 0,
+	WIFI_OUI_STYPE_WMM_PARAM = 1,
+	WIFI_OUI_STYPE_WMM_TSPEC = 2,
+};
+
 #endif /* IEEE80211_H */
diff -ruN linux-2.6.22/include/net/cfg80211.h linux-2.6.22-mac80211/include/net/cfg80211.h
--- linux-2.6.22/include/net/cfg80211.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/include/net/cfg80211.h	2007-08-23 20:59:59.000000000 +0200
@@ -11,6 +11,44 @@
  * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
  */
 
+
+/* Radiotap header iteration
+ *   implemented in net/wireless/radiotap.c
+ *   docs in Documentation/networking/radiotap-headers.txt
+ */
+/**
+ * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args
+ * @rtheader: pointer to the radiotap header we are walking through
+ * @max_length: length of radiotap header in cpu byte ordering
+ * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg
+ * @this_arg: pointer to current radiotap arg
+ * @arg_index: internal next argument index
+ * @arg: internal next argument pointer
+ * @next_bitmap: internal pointer to next present u32
+ * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ */
+
+struct ieee80211_radiotap_iterator {
+	struct ieee80211_radiotap_header *rtheader;
+	int max_length;
+	int this_arg_index;
+	u8 *this_arg;
+
+	int arg_index;
+	u8 *arg;
+	__le32 *next_bitmap;
+	u32 bitmap_shifter;
+};
+
+extern int ieee80211_radiotap_iterator_init(
+   struct ieee80211_radiotap_iterator *iterator,
+   struct ieee80211_radiotap_header *radiotap_header,
+   int max_length);
+
+extern int ieee80211_radiotap_iterator_next(
+   struct ieee80211_radiotap_iterator *iterator);
+
+
 /* from net/wireless.h */
 struct wiphy;
 
diff -ruN linux-2.6.22/include/net/mac80211.h linux-2.6.22-mac80211/include/net/mac80211.h
--- linux-2.6.22/include/net/mac80211.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/include/net/mac80211.h	2007-08-23 20:59:59.000000000 +0200
@@ -148,11 +148,12 @@
  * this struct need to have fixed values. As soon as it is removed, we can
  * fix these entries. */
 	IEEE80211_TX_QUEUE_AFTER_BEACON = 6,
-	IEEE80211_TX_QUEUE_BEACON = 7
+	IEEE80211_TX_QUEUE_BEACON = 7,
+	NUM_TX_DATA_QUEUES_11N = 16 /* adding more data queues for 802.11n */
 };
 
 struct ieee80211_tx_queue_stats {
-	struct ieee80211_tx_queue_stats_data data[NUM_TX_DATA_QUEUES];
+	struct ieee80211_tx_queue_stats_data data[NUM_TX_DATA_QUEUES_11N];
 };
 
 struct ieee80211_low_level_stats {
@@ -192,6 +193,7 @@
 #define IEEE80211_TXCTL_FIRST_FRAGMENT	(1<<8) /* this is a first fragment of
 						* the frame */
 #define IEEE80211_TXCTL_TKIP_NEW_PHASE1_KEY (1<<9)
+#define IEEE80211_TXCTL_HT_MPDU_AGG	(1<<10) /* MPDU aggregation */
 	u32 flags;			       /* tx control flags defined
 						* above */
 	u8 retry_limit;		/* 1 = only first attempt, 2 = one retry, .. */
@@ -236,6 +238,7 @@
 #define RX_FLAG_DECRYPTED	(1<<1)
 #define RX_FLAG_RADIOTAP	(1<<2)
 	int flag;
+	int ordered; /* used in ampdu reordering scheme */
 };
 
 /* Transmit status. The low-level driver should provide this information
@@ -283,6 +286,7 @@
 #define IEEE80211_CONF_SSID_HIDDEN	(1<<1) /* do not broadcast the ssid */
 #define IEEE80211_CONF_RADIOTAP		(1<<2) /* use radiotap if supported
 						  check this bit at RX time */
+#define IEEE80211_CONF_CHANNEL_SWITCH	(1<<3)
 	u32 flags;			/* configuration flags defined above */
 
 	u8 power_level;			/* transmit power limit for current
@@ -290,6 +294,8 @@
 	u8 antenna_max;			/* maximum antenna gain */
 	short tx_power_reduction; /* in 0.1 dBm */
 
+	u8 power_management_enable;     /* flag to enable/disable*/
+					/*power management*/
 	/* 0 = default/diversity, 1 = Ant0, 2 = Ant1 */
 	u8 antenna_sel_tx;
 	u8 antenna_sel_rx;
@@ -347,9 +353,16 @@
  * @mac_addr: pointer to MAC address of the interface. This pointer is valid
  *	until the interface is removed (i.e. it cannot be used after
  *	remove_interface() callback was called for this interface).
+ *	This pointer will be %NULL for monitor interfaces, be careful.
  *
  * This structure is used in add_interface() and remove_interface()
  * callbacks of &struct ieee80211_hw.
+ *
+ * When you allow multiple interfaces to be added to your PHY, take care
+ * that the hardware can actually handle multiple MAC addresses. However,
+ * also take care that when there's no interface left with mac_addr != %NULL
+ * you remove the MAC address from the device to avoid acknowledging packets
+ * in pure monitor mode.
  */
 struct ieee80211_if_init_conf {
 	int if_id;
@@ -514,6 +527,9 @@
 	 * per-packet RC4 key with each TX frame when doing hwcrypto */
 #define IEEE80211_HW_TKIP_REQ_PHASE2_KEY (1<<14)
 
+	/* The device capable of supporting 11n */
+#define IEEE80211_HW_SUPPORT_HT_MODE (1<<15)
+
 	u32 flags;			/* hardware flags defined above */
 
 	/* Set to the size of a needed device specific skb headroom for TX skbs. */
@@ -531,6 +547,9 @@
 	/* Number of available hardware TX queues for data packets.
 	 * WMM requires at least four queues. */
 	int queues;
+	/* Preferred rate control algorithm. Leave as NULL for stack
+	* to select algorithm */
+	char *preferred_rate_control;
 };
 
 static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev)
@@ -574,10 +593,11 @@
 	 * to returning zero. By returning non-zero addition of the interface
 	 * is inhibited. Unless monitor_during_oper is set, it is guaranteed
 	 * that monitor interfaces and normal interfaces are mutually
-	 * exclusive. The open() handler is called after add_interface()
-	 * if this is the first device added. At least one of the open()
-	 * open() and add_interface() callbacks has to be assigned. If
-	 * add_interface() is NULL, one STA interface is permitted only. */
+	 * exclusive. If assigned, the open() handler is called after
+	 * add_interface() if this is the first device added. The
+	 * add_interface() callback has to be assigned because it is the only
+	 * way to obtain the requested MAC address for any interface.
+	 */
 	int (*add_interface)(struct ieee80211_hw *hw,
 			     struct ieee80211_if_init_conf *conf);
 
@@ -715,13 +735,49 @@
 	 * IBSS uses a fixed beacon frame which is configured using this
 	 * function. This handler is required only for IBSS mode. */
 	int (*beacon_update)(struct ieee80211_hw *hw,
-			     struct sk_buff *skb,
+				struct sk_buff *skb,
 			     struct ieee80211_tx_control *control);
 
 	/* Determine whether the last IBSS beacon was sent by us. This is
 	 * needed only for IBSS mode and the result of this function is used to
 	 * determine whether to reply to Probe Requests. */
 	int (*tx_last_beacon)(struct ieee80211_hw *hw);
+
+	/* Configure ht parameters. */
+	int (*conf_ht)(struct ieee80211_hw *hw,
+			struct ieee80211_ht_capability *ht_cap_param,
+			struct ieee80211_ht_additional_info *ht_extra_param);
+
+	/* Get ht capabilities from the device */
+	void (*get_ht_capab)(struct ieee80211_hw *hw,
+				struct ieee80211_ht_capability *ht_cap_param);
+
+
+	/* move to Tx aggregation for a certain DA/TID.
+	 * return the starting frame's sequence number in start_seq_nums.
+	 * If aggregation can not be started on a certain TID return error,
+	 * else return success */
+	int (*ht_tx_agg_start)(struct ieee80211_hw *hw, u8 *da, u16 tid, u16 *start_seq_num);
+
+	/* stop Tx aggregation for a certain DA/TID.
+	 * If aggregation can not be stopped on a certain TID return error,
+	 * else return success. generator field gives the HW an indication who
+	 * requests to stop the aggregation - recipient (0) or initiator (1)
+	 */
+	int (*ht_tx_agg_stop)(struct ieee80211_hw *hw, u8 *da, u16 tid, int generator);
+
+	/* move to Rx aggregation for a certain DA/TID.
+	 * If aggregation can not be started on a certain TID return error,
+	 * else return success */
+	int (*ht_rx_agg_start)(struct ieee80211_hw *hw, u8 *da, u16 tid, u16 start_seq_num);
+
+	/* stop Rx aggregation for a certain DA/TID.
+	 * If aggregation can not be stopped on a certain TID return error,
+	 * else return success. generator field gives the HW an indication who
+	 * requests to stop the aggregation - recipient (0) or initiator (1)
+	 */
+	int (*ht_rx_agg_stop)(struct ieee80211_hw *hw, u8 *da, u16 tid, int generator);
+
 };
 
 /* Allocate a new hardware device. This must be called once for each
@@ -921,12 +977,6 @@
 ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id,
 			  struct ieee80211_tx_control *control);
 
-/* Low level drivers that have their own MLME and MAC indicate
- * the aid for an associating station with this call */
-int ieee80211_set_aid_for_sta(struct ieee80211_hw *hw,
-			      u8 *peer_address, u16 aid);
-
-
 /* Given an sk_buff with a raw 802.11 header at the data pointer this function
  * returns the 802.11 header length in bytes (not including encryption
  * headers). If the data in the sk_buff is too short to contain a valid 802.11
@@ -980,6 +1030,32 @@
 void ieee80211_wake_queues(struct ieee80211_hw *hw);
 
 /**
+ * ieee80211_start_BA_session - low level driver asks to start a BA session
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @da: destination address of the BA session recipient
+ * @tid: the desired TID to BA on.
+ * @return: error if no sta with matching da found, success otherwise
+ *
+ * although the low level driver can estimate the need to start aggregation on a
+ * certain DA/TID, the session itself should be managed by the stack. this function
+ * should be called by low level driver to start the BA session
+ */
+int ieee80211_start_BA_session(struct ieee80211_hw *hw, u8 *da, u16 tid);
+
+/**
+ * ieee80211_stop_BA_session - low level driver asks to stop a BA session
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @da: destination address of the BA session recipient
+ * @tid: the desired TID to stop BA.
+ * @return: error if no sta with matching da found, success otherwise
+ *
+ * although the low level driver can estimate the need to stop aggregation on a
+ * certain DA/TSID, the session itself should be managed by the stack. this function
+ * should be called by low level driver to stop the BA session
+ */
+int ieee80211_stop_BA_session(struct ieee80211_hw *hw, u8 *da, u16 tid);
+
+/**
  * ieee80211_get_mc_list_item - iteration over items in multicast list
  * @hw: pointer as obtained from ieee80211_alloc_hw().
  * @prev: value returned by previous call to ieee80211_get_mc_list_item() or
diff -ruN linux-2.6.22/kversion linux-2.6.22-mac80211/kversion
--- linux-2.6.22/kversion	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-mac80211/kversion	2007-08-23 20:59:59.000000000 +0200
@@ -0,0 +1 @@
+/usr/src/linux-2.6.22-mac80211
diff -ruN linux-2.6.22/net/mac80211/Makefile linux-2.6.22-mac80211/net/mac80211/Makefile
--- linux-2.6.22/net/mac80211/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/Makefile	2007-08-23 20:59:59.000000000 +0200
@@ -13,6 +13,7 @@
 	ieee80211_iface.o \
 	ieee80211_rate.o \
 	michael.o \
+	regdomain.o \
 	tkip.o \
 	aes_ccm.o \
 	wme.o \
diff -ruN linux-2.6.22/net/mac80211/debugfs_netdev.c linux-2.6.22-mac80211/net/mac80211/debugfs_netdev.c
--- linux-2.6.22/net/mac80211/debugfs_netdev.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/debugfs_netdev.c	2007-08-23 20:59:59.000000000 +0200
@@ -87,6 +87,267 @@
 		IEEE80211_IF_FMT_##format(name, field)			\
 		__IEEE80211_IF_FILE(name)
 
+#define DEBUGFS_QOS_FILE(name, f)					\
+static ssize_t qos_ ##name## _write(struct file *file,			\
+				    const char __user *userbuf,		\
+				    size_t count, loff_t *ppos)		\
+{									\
+	struct ieee80211_sub_if_data *sdata = file->private_data;	\
+									\
+	f(sdata->dev, &sdata->u.sta, &sdata->u.sta.tspec);		\
+									\
+	return count;							\
+}									\
+									\
+static const struct file_operations qos_ ##name## _ops = {		\
+	.write = qos_ ##name## _write,					\
+	.open = mac80211_open_file_generic,				\
+};
+
+#define DEBUGFS_QOS_ADD(name)						\
+	sdata->debugfs.sta.qos.name = debugfs_create_file(#name, 0444, qosd,\
+		sdata, &qos_ ##name## _ops);
+
+#define DEBUGFS_QOS_DEL(name)						\
+	do {								\
+		debugfs_remove(sdata->debugfs.sta.qos.name);		\
+		sdata->debugfs.sta.qos.name = NULL;			\
+	} while (0)
+
+DEBUGFS_QOS_FILE(addts_11e, ieee80211_send_addts);
+DEBUGFS_QOS_FILE(addts_wmm, wmm_send_addts);
+DEBUGFS_QOS_FILE(delts_11e, ieee80211_send_delts);
+DEBUGFS_QOS_FILE(delts_wmm, wmm_send_delts);
+
+static ssize_t qos_if_dls_mac(const struct ieee80211_sub_if_data *sdata,
+			      char *buf, int buflen)
+{
+	return scnprintf(buf, buflen, MAC_FMT "\n",
+			 MAC_ARG(sdata->u.sta.dls_mac));
+}
+
+static ssize_t qos_dls_mac_read(struct file *file,
+				char __user *userbuf,
+				size_t count, loff_t *ppos)
+{
+	return ieee80211_if_read(file->private_data,
+				 userbuf, count, ppos,
+				 qos_if_dls_mac);
+}
+
+static ssize_t qos_dls_mac_write(struct file *file, const char __user *userbuf,
+				 size_t count, loff_t *ppos)
+{
+	struct ieee80211_sub_if_data *sdata = file->private_data;
+	char buf[20];
+	size_t size;
+	u8 m[ETH_ALEN];
+
+	size = min(sizeof(buf) - 1, count);
+	buf[size] = '\0';
+	if (copy_from_user(buf, userbuf, size))
+		return -EFAULT;
+
+	if (sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+		   &((u8*)(m))[0], &((u8*)(m))[1], &((u8*)(m))[2],
+		   &((u8*)(m))[3], &((u8*)(m))[4], &((u8*)(m))[5]) != ETH_ALEN){
+		printk(KERN_ERR "%s: sscanf input error\n", sdata->dev->name);
+		return -EINVAL;
+	}
+	memcpy(sdata->u.sta.dls_mac, m, ETH_ALEN);
+	return count;
+}
+
+static const struct file_operations qos_dls_mac_ops = {
+	.read = qos_dls_mac_read,
+	.write = qos_dls_mac_write,
+	.open = mac80211_open_file_generic,
+};
+
+static ssize_t qos_if_dls_op(const struct ieee80211_sub_if_data *sdata,
+			     char *buf, int buflen)
+{
+	return scnprintf(buf, buflen,
+			 "DLS Operation: Setup = 1; Teardown = 2\n");
+}
+
+static ssize_t qos_dls_op_read(struct file *file, char __user *userbuf,
+			       size_t count, loff_t *ppos)
+{
+	return ieee80211_if_read(file->private_data,
+				 userbuf, count, ppos,
+				 qos_if_dls_op);
+}
+
+static ssize_t qos_dls_op_write(struct file *file, const char __user *userbuf,
+				 size_t count, loff_t *ppos)
+{
+	struct ieee80211_sub_if_data *sdata = file->private_data;
+	char buf[20];
+	size_t size;
+	unsigned int opt;
+
+	size = min(sizeof(buf) - 1, count);
+	buf[size] = '\0';
+	if (copy_from_user(buf, userbuf, size))
+		return -EFAULT;
+
+	if (sscanf(buf, "%u", &opt) != 1) {
+		printk(KERN_ERR "%s: sscanf input error\n", sdata->dev->name);
+		return -EINVAL;
+	}
+	switch (opt) {
+	case 1:
+		ieee80211_send_dls_req(sdata->dev, &sdata->u.sta,
+				       sdata->u.sta.dls_mac, 0);
+		break;
+	case 2:
+		ieee80211_send_dls_teardown(sdata->dev, &sdata->u.sta,
+					    sdata->u.sta.dls_mac,
+					    WLAN_REASON_QSTA_NOT_USE);
+		break;
+	default:
+		printk(KERN_ERR "Unknown DLS Operation: %d\n", opt);
+		break;
+	}
+	return count;
+}
+
+static const struct file_operations qos_dls_op_ops = {
+	.read = qos_dls_op_read,
+	.write = qos_dls_op_write,
+	.open = mac80211_open_file_generic,
+};
+
+#define DEBUGFS_TSINFO_FILE(_name, min_val, max_val)			\
+static ssize_t tsinfo_ ##_name## _read(struct file *file,		\
+				       char __user *userbuf,		\
+				       size_t count, loff_t *ppos)	\
+{									\
+	char buf[20];							\
+	struct ieee80211_sub_if_data *sdata = file->private_data;	\
+	int res = scnprintf(buf, count, "%u\n",				\
+		IEEE80211_TSINFO_## _name (sdata->u.sta.tspec.ts_info));\
+	return simple_read_from_buffer(userbuf, count, ppos, buf, res);	\
+}									\
+									\
+static ssize_t tsinfo_ ##_name## _write(struct file *file,		\
+				        const char __user *userbuf,	\
+				        size_t count, loff_t *ppos)	\
+{									\
+	char buf[20];							\
+	size_t size;							\
+	int val;							\
+	struct ieee80211_sub_if_data *sdata = file->private_data;	\
+									\
+	size = min(sizeof(buf) - 1, count);				\
+	buf[size] = '\0';						\
+	if (copy_from_user(buf, userbuf, size))				\
+		return -EFAULT;						\
+									\
+	val = simple_strtoul(buf, NULL, 0);				\
+	if ((val < min_val) || (val > max_val)) {			\
+		printk(KERN_ERR "%s: set value (%u) out of range "	\
+		       "[%u, %u]\n",sdata->dev->name,val,min_val,max_val);\
+		return -EINVAL;						\
+	}								\
+	IEEE80211_SET_TSINFO_ ##_name (sdata->u.sta.tspec.ts_info, val);\
+	return count;							\
+}									\
+									\
+static const struct file_operations tsinfo_ ##_name## _ops = {		\
+	.read = tsinfo_ ##_name## _read,				\
+	.write = tsinfo_ ##_name## _write,				\
+	.open = mac80211_open_file_generic,				\
+};
+
+#define DEBUGFS_TSINFO_ADD_TSID						\
+	sdata->debugfs.sta.tsinfo.tsid =				\
+		debugfs_create_file("tsid", 0444, tsinfod,		\
+				    sdata, &tsinfo_TSID_ops);
+
+#define DEBUGFS_TSINFO_ADD_DIR						\
+	sdata->debugfs.sta.tsinfo.direction =				\
+		debugfs_create_file("direction", 0444, tsinfod,		\
+				    sdata, &tsinfo_DIR_ops);
+
+#define DEBUGFS_TSINFO_ADD_UP						\
+	sdata->debugfs.sta.tsinfo.up =					\
+		debugfs_create_file("up", 0444, tsinfod,		\
+				    sdata, &tsinfo_UP_ops);
+
+#define DEBUGFS_TSINFO_DEL(name)					\
+	do {								\
+		debugfs_remove(sdata->debugfs.sta.tsinfo.name);		\
+		sdata->debugfs.sta.tsinfo.name = NULL;			\
+	} while (0)
+
+DEBUGFS_TSINFO_FILE(TSID, 8, 15);
+DEBUGFS_TSINFO_FILE(DIR, 0, 3);
+DEBUGFS_TSINFO_FILE(UP, 0, 7);
+
+#define DEBUGFS_TSPEC_FILE(name, format_string, endian_f1, endian_f2)	\
+static ssize_t tspec_ ##name## _read(struct file *file,			\
+				      char __user *userbuf,		\
+				      size_t count, loff_t *ppos)	\
+{									\
+	char buf[20];							\
+	struct ieee80211_sub_if_data *sdata = file->private_data;	\
+	int res = scnprintf(buf, count, format_string "\n",		\
+			    endian_f1(sdata->u.sta.tspec.name));	\
+	return simple_read_from_buffer(userbuf, count, ppos, buf, res);	\
+}									\
+									\
+static ssize_t tspec_ ##name## _write(struct file *file,		\
+				       const char __user *userbuf,	\
+				       size_t count, loff_t *ppos)	\
+{									\
+	char buf[20];							\
+	size_t size;							\
+	struct ieee80211_sub_if_data *sdata = file->private_data;	\
+									\
+	size = min(sizeof(buf) - 1, count);				\
+	buf[size] = '\0';						\
+	if (copy_from_user(buf, userbuf, size))				\
+		return -EFAULT;						\
+									\
+	sdata->u.sta.tspec.name = endian_f2(simple_strtoul(buf, NULL, 0));\
+	return count;							\
+}									\
+									\
+static const struct file_operations tspec_ ##name## _ops = {		\
+	.read = tspec_ ##name## _read,					\
+	.write = tspec_ ##name## _write,				\
+	.open = mac80211_open_file_generic,				\
+};
+
+#define DEBUGFS_TSPEC_ADD(name)						\
+	sdata->debugfs.sta.tspec.name = debugfs_create_file(#name,	\
+		0444, tspecd, sdata, &tspec_ ##name## _ops);
+
+#define DEBUGFS_TSPEC_DEL(name)						\
+	do {								\
+		debugfs_remove(sdata->debugfs.sta.tspec.name);		\
+		sdata->debugfs.sta.tspec.name = NULL;			\
+	} while (0)
+
+DEBUGFS_TSPEC_FILE(nominal_msdu_size, "%hu", le16_to_cpu, cpu_to_le16);
+DEBUGFS_TSPEC_FILE(max_msdu_size, "%hu", le16_to_cpu, cpu_to_le16);
+DEBUGFS_TSPEC_FILE(min_service_interval, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(max_service_interval, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(inactivity_interval, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(suspension_interval, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(service_start_time, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(min_data_rate, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(mean_data_rate, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(peak_data_rate, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(burst_size, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(delay_bound, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(min_phy_rate, "%u", le32_to_cpu, cpu_to_le32);
+DEBUGFS_TSPEC_FILE(surplus_band_allow, "%hu", le16_to_cpu, cpu_to_le16);
+DEBUGFS_TSPEC_FILE(medium_time, "%hu", le16_to_cpu, cpu_to_le16);
+
+
 /* common attributes */
 IEEE80211_IF_FILE(channel_use, channel_use, DEC);
 IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
@@ -118,7 +379,7 @@
 			 sdata->u.sta.authenticated ? "AUTH\n" : "",
 			 sdata->u.sta.associated ? "ASSOC\n" : "",
 			 sdata->u.sta.probereq_poll ? "PROBEREQ POLL\n" : "",
-			 sdata->u.sta.use_protection ? "CTS prot\n" : "");
+			 sdata->use_protection ? "CTS prot\n" : "");
 }
 __IEEE80211_IF_FILE(flags);
 
@@ -184,6 +445,10 @@
 
 static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 {
+	struct dentry *qosd;
+	struct dentry *tsinfod;
+	struct dentry *tspecd;
+
 	DEBUGFS_ADD(channel_use, sta);
 	DEBUGFS_ADD(drop_unencrypted, sta);
 	DEBUGFS_ADD(eapol, sta);
@@ -202,6 +467,42 @@
 	DEBUGFS_ADD(auth_alg, sta);
 	DEBUGFS_ADD(auth_transaction, sta);
 	DEBUGFS_ADD(flags, sta);
+
+	qosd = debugfs_create_dir("qos", sdata->debugfsdir);
+	sdata->debugfs.sta.qos_dir = qosd;
+
+	DEBUGFS_QOS_ADD(addts_11e);
+	DEBUGFS_QOS_ADD(addts_wmm);
+	DEBUGFS_QOS_ADD(delts_11e);
+	DEBUGFS_QOS_ADD(delts_wmm);
+	DEBUGFS_QOS_ADD(dls_mac);
+	DEBUGFS_QOS_ADD(dls_op);
+
+	tsinfod = debugfs_create_dir("ts_info", qosd);
+	sdata->debugfs.sta.tsinfo_dir = tsinfod;
+
+	DEBUGFS_TSINFO_ADD_TSID;
+	DEBUGFS_TSINFO_ADD_DIR;
+	DEBUGFS_TSINFO_ADD_UP;
+
+	tspecd = debugfs_create_dir("tspec", qosd);
+	sdata->debugfs.sta.tspec_dir = tspecd;
+
+	DEBUGFS_TSPEC_ADD(nominal_msdu_size);
+	DEBUGFS_TSPEC_ADD(max_msdu_size);
+	DEBUGFS_TSPEC_ADD(min_service_interval);
+	DEBUGFS_TSPEC_ADD(max_service_interval);
+	DEBUGFS_TSPEC_ADD(inactivity_interval);
+	DEBUGFS_TSPEC_ADD(suspension_interval);
+	DEBUGFS_TSPEC_ADD(service_start_time);
+	DEBUGFS_TSPEC_ADD(min_data_rate);
+	DEBUGFS_TSPEC_ADD(mean_data_rate);
+	DEBUGFS_TSPEC_ADD(peak_data_rate);
+	DEBUGFS_TSPEC_ADD(burst_size);
+	DEBUGFS_TSPEC_ADD(delay_bound);
+	DEBUGFS_TSPEC_ADD(min_phy_rate);
+	DEBUGFS_TSPEC_ADD(surplus_band_allow);
+	DEBUGFS_TSPEC_ADD(medium_time);
 }
 
 static void add_ap_files(struct ieee80211_sub_if_data *sdata)
@@ -271,9 +572,11 @@
 	}
 }
 
-#define DEBUGFS_DEL(name, type)\
-	debugfs_remove(sdata->debugfs.type.name);\
-	sdata->debugfs.type.name = NULL;
+#define DEBUGFS_DEL(name, type)					\
+	do {							\
+		debugfs_remove(sdata->debugfs.type.name);	\
+		sdata->debugfs.type.name = NULL;		\
+	} while (0)
 
 static void del_sta_files(struct ieee80211_sub_if_data *sdata)
 {
@@ -295,6 +598,40 @@
 	DEBUGFS_DEL(auth_alg, sta);
 	DEBUGFS_DEL(auth_transaction, sta);
 	DEBUGFS_DEL(flags, sta);
+
+	DEBUGFS_TSINFO_DEL(tsid);
+	DEBUGFS_TSINFO_DEL(direction);
+	DEBUGFS_TSINFO_DEL(up);
+
+	DEBUGFS_TSPEC_DEL(nominal_msdu_size);
+	DEBUGFS_TSPEC_DEL(max_msdu_size);
+	DEBUGFS_TSPEC_DEL(min_service_interval);
+	DEBUGFS_TSPEC_DEL(max_service_interval);
+	DEBUGFS_TSPEC_DEL(inactivity_interval);
+	DEBUGFS_TSPEC_DEL(suspension_interval);
+	DEBUGFS_TSPEC_DEL(service_start_time);
+	DEBUGFS_TSPEC_DEL(min_data_rate);
+	DEBUGFS_TSPEC_DEL(mean_data_rate);
+	DEBUGFS_TSPEC_DEL(peak_data_rate);
+	DEBUGFS_TSPEC_DEL(burst_size);
+	DEBUGFS_TSPEC_DEL(delay_bound);
+	DEBUGFS_TSPEC_DEL(min_phy_rate);
+	DEBUGFS_TSPEC_DEL(surplus_band_allow);
+	DEBUGFS_TSPEC_DEL(medium_time);
+
+	DEBUGFS_QOS_DEL(addts_11e);
+	DEBUGFS_QOS_DEL(addts_wmm);
+	DEBUGFS_QOS_DEL(delts_11e);
+	DEBUGFS_QOS_DEL(delts_wmm);
+	DEBUGFS_QOS_DEL(dls_mac);
+	DEBUGFS_QOS_DEL(dls_op);
+
+	debugfs_remove(sdata->debugfs.sta.tspec_dir);
+	sdata->debugfs.sta.tspec_dir = NULL;
+	debugfs_remove(sdata->debugfs.sta.tsinfo_dir);
+	sdata->debugfs.sta.tsinfo_dir = NULL;
+	debugfs_remove(sdata->debugfs.sta.qos_dir);
+	sdata->debugfs.sta.qos_dir = NULL;
 }
 
 static void del_ap_files(struct ieee80211_sub_if_data *sdata)
@@ -397,6 +734,8 @@
 			 void *ndev)
 {
 	struct net_device *dev = ndev;
+	struct dentry *dir;
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	char buf[10+IFNAMSIZ];
 
 	if (state != NETDEV_CHANGENAME)
@@ -408,10 +747,13 @@
 	if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid)
 		return 0;
 
-	/* TODO
 	sprintf(buf, "netdev:%s", dev->name);
-	debugfs_rename(IEEE80211_DEV_TO_SUB_IF(dev)->debugfsdir, buf);
-	*/
+	dir = sdata->debugfsdir;
+#if 0
+	if (!debugfs_rename(dir->d_parent, dir, dir->d_parent, buf))
+		printk(KERN_ERR "mac80211: debugfs: failed to rename debugfs "
+		       "dir to %s\n", buf);
+#endif
 
 	return 0;
 }
diff -ruN linux-2.6.22/net/mac80211/debugfs_sta.c linux-2.6.22-mac80211/net/mac80211/debugfs_sta.c
--- linux-2.6.22/net/mac80211/debugfs_sta.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/debugfs_sta.c	2007-08-23 20:59:59.000000000 +0200
@@ -55,6 +55,13 @@
 	.open = mac80211_open_file_generic,				\
 }
 
+#define STA_OPS_WR(name)							\
+static const struct file_operations sta_ ##name## _ops = {		\
+	.read = sta_##name##_read,					\
+	.write = sta_##name##_write,				\
+	.open = mac80211_open_file_generic,				\
+}
+
 #define STA_FILE(name, field, format)					\
 		STA_READ_##format(name, field)				\
 		STA_OPS(name)
@@ -87,7 +94,7 @@
 {
 	char buf[100];
 	struct sta_info *sta = file->private_data;
-	int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s",
+	int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s",
 		sta->flags & WLAN_STA_AUTH ? "AUTH\n" : "",
 		sta->flags & WLAN_STA_ASSOC ? "ASSOC\n" : "",
 		sta->flags & WLAN_STA_PS ? "PS\n" : "",
@@ -96,6 +103,7 @@
 		sta->flags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "",
 		sta->flags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "",
 		sta->flags & WLAN_STA_WME ? "WME\n" : "",
+		sta->flags & WLAN_STA_HT ? "HT\n" : "",
 		sta->flags & WLAN_STA_WDS ? "WDS\n" : "");
 	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
 }
@@ -157,7 +165,7 @@
 	struct sta_info *sta = file->private_data;
 	for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
 		p += scnprintf(p, sizeof(buf)+buf-p, "%x ",
-			       sta->last_seq_ctrl[i]);
+			       le16_to_cpu(sta->last_seq_ctrl[i]));
 	p += scnprintf(p, sizeof(buf)+buf-p, "\n");
 	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
 }
@@ -193,6 +201,106 @@
 STA_OPS(wme_tx_queue);
 #endif
 
+static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+        char buf[768], *p = buf;
+        int i;
+        struct sta_info *sta = file->private_data;
+        p += scnprintf(p, sizeof(buf)+buf-p, "Aggregation  state for this STA is:\n");
+        p += scnprintf(p, sizeof(buf)+buf-p, " STA next dialog_token is %d \n TIDs info is: \n TID :",
+                (sta->ht_ba_mlme.dialog_token_allocator + 1));
+        for (i=0;i < STA_TID_NUM;i++)
+                p += scnprintf(p, sizeof(buf)+buf-p, "%5d",i);
+
+        p += scnprintf(p, sizeof(buf)+buf-p, "\n RX  :");
+        for (i=0;i < STA_TID_NUM;i++)
+                p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+                        sta->ht_ba_mlme.tid_agg_info_rx[i].state);
+
+        p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
+        for (i=0;i < STA_TID_NUM;i++)
+                p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+                        sta->ht_ba_mlme.tid_agg_info_rx[i].dialog_token);
+
+        p += scnprintf(p, sizeof(buf)+buf-p, "\n TX  :");
+        for (i=0;i < STA_TID_NUM;i++)
+                p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+                        sta->ht_ba_mlme.tid_agg_info_tx[i].state);
+
+        p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
+        for (i=0;i < STA_TID_NUM;i++)
+                p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+                        sta->ht_ba_mlme.tid_agg_info_tx[i].dialog_token);
+
+        p += scnprintf(p, sizeof(buf)+buf-p, "\n SSEQ:");
+        for (i=0;i < STA_TID_NUM;i++)
+                p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+                        sta->ht_ba_mlme.tid_agg_info_tx[i].start_seq_num);
+
+        p += scnprintf(p, sizeof(buf)+buf-p, "\n");
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+}
+
+static ssize_t sta_agg_status_write(struct file *file,const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct sta_info *sta = file->private_data;
+	struct net_device *dev = sta->dev;
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_hw *hw = &local->hw;
+	u8 *da = sta->addr;
+	static int tid_static_tx[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+	static int tid_static_rx[16]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
+	char *endp;
+	char buf[32];
+	int buf_size,rs;
+	unsigned int tid_num;
+	char state[4];
+
+	memset(buf, 0x00, sizeof(buf));
+	buf_size = min(count, (sizeof(buf)-1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+
+	tid_num = simple_strtoul(buf, &endp, 0);
+	if (endp == buf)
+		return -EINVAL;
+
+	if ((tid_num >= 100) && (tid_num <= 115)) { /* toggle Rx aggregation command */
+		tid_num = tid_num - 100;
+		if(tid_static_rx[tid_num] == 1) {
+			strcpy(state,"off ");
+			ieee80211_sta_stop_rx_BA_session(dev,da,tid_num,0,WLAN_REASON_QSTA_REQUIRE_SETUP);
+			sta->ht_ba_mlme.tid_agg_info_rx[tid_num].buf_size = 0xFF;
+			tid_static_rx[tid_num] = 0;
+		} else {
+			strcpy(state,"on ");
+			sta->ht_ba_mlme.tid_agg_info_rx[tid_num].buf_size = 0x00;
+			tid_static_rx[tid_num] = 1;
+		}
+		printk(KERN_ERR "sta_agg_status_write tried switching tid=%u %s\n",tid_num,state);
+	}
+	else if ((tid_num >= 0) && (tid_num <= 15)) { /* toggle Tx aggregation command */
+		if(tid_static_tx[tid_num] == 0) {
+			strcpy(state,"on ");
+			rs =  ieee80211_start_BA_session(hw, da, tid_num);
+			if (rs==0)
+				tid_static_tx[tid_num] = 1;
+		} else {
+			strcpy(state,"off");
+			rs =  ieee80211_stop_BA_session(hw, da, tid_num);
+	/*		if (rs==0) hack to enable toggeling*/
+				tid_static_tx[tid_num] = 0;
+		}
+		printk(KERN_ERR "sta_agg_status_write tried switching tid=%u %s, return=%d\n",tid_num,state,rs);
+	}
+
+	return count;
+}
+STA_OPS_WR(agg_status);
+
+
 #define DEBUGFS_ADD(name) \
 	sta->debugfs.name = debugfs_create_file(#name, 0444, \
 		sta->debugfs.dir, sta, &sta_ ##name## _ops);
@@ -226,6 +334,7 @@
 	DEBUGFS_ADD(wme_rx_queue);
 	DEBUGFS_ADD(wme_tx_queue);
 #endif
+	DEBUGFS_ADD(agg_status);
 }
 
 void ieee80211_sta_debugfs_remove(struct sta_info *sta)
@@ -240,6 +349,7 @@
 	DEBUGFS_DEL(wme_rx_queue);
 	DEBUGFS_DEL(wme_tx_queue);
 #endif
+	DEBUGFS_DEL(agg_status);
 
 	debugfs_remove(sta->debugfs.dir);
 	sta->debugfs.dir = NULL;
diff -ruN linux-2.6.22/net/mac80211/hostapd_ioctl.h linux-2.6.22-mac80211/net/mac80211/hostapd_ioctl.h
--- linux-2.6.22/net/mac80211/hostapd_ioctl.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/hostapd_ioctl.h	2007-08-23 20:59:59.000000000 +0200
@@ -26,24 +26,16 @@
  * mess shall be deleted completely. */
 enum {
 	PRISM2_PARAM_IEEE_802_1X = 23,
-	PRISM2_PARAM_ANTSEL_TX = 24,
-	PRISM2_PARAM_ANTSEL_RX = 25,
 
 	/* Instant802 additions */
 	PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES = 1001,
-	PRISM2_PARAM_DROP_UNENCRYPTED = 1002,
 	PRISM2_PARAM_PREAMBLE = 1003,
 	PRISM2_PARAM_SHORT_SLOT_TIME = 1006,
 	PRISM2_PARAM_NEXT_MODE = 1008,
-	PRISM2_PARAM_CLEAR_KEYS = 1009,
 	PRISM2_PARAM_RADIO_ENABLED = 1010,
 	PRISM2_PARAM_ANTENNA_MODE = 1013,
 	PRISM2_PARAM_STAT_TIME = 1016,
 	PRISM2_PARAM_STA_ANTENNA_SEL = 1017,
-	PRISM2_PARAM_FORCE_UNICAST_RATE = 1018,
-	PRISM2_PARAM_RATE_CTRL_NUM_UP = 1019,
-	PRISM2_PARAM_RATE_CTRL_NUM_DOWN = 1020,
-	PRISM2_PARAM_MAX_RATECTRL_RATE = 1021,
 	PRISM2_PARAM_TX_POWER_REDUCTION = 1022,
 	PRISM2_PARAM_KEY_TX_RX_THRESHOLD = 1024,
 	PRISM2_PARAM_DEFAULT_WEP_ONLY = 1026,
diff -ruN linux-2.6.22/net/mac80211/ieee80211.c linux-2.6.22-mac80211/net/mac80211/ieee80211.c
--- linux-2.6.22/net/mac80211/ieee80211.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/ieee80211.c	2007-08-23 20:59:59.000000000 +0200
@@ -24,6 +24,7 @@
 #include <linux/compiler.h>
 #include <linux/bitmap.h>
 #include <net/cfg80211.h>
+#include <asm/unaligned.h>
 
 #include "ieee80211_common.h"
 #include "ieee80211_i.h"
@@ -56,6 +57,17 @@
 	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e };
 
 
+/*
+ * For seeing transmitted packets on monitor interfaces
+ * we have a radiotap header too.
+ */
+struct ieee80211_tx_status_rtap_hdr {
+	struct ieee80211_radiotap_header hdr;
+	__le16 tx_flags;
+	u8 data_retries;
+} __attribute__ ((packed));
+
+
 static inline void ieee80211_include_sequence(struct ieee80211_sub_if_data *sdata,
 					      struct ieee80211_hdr *hdr)
 {
@@ -246,13 +258,11 @@
 }
 
 
-static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len)
+static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
+				unsigned int type)
 {
 	u16 fc;
 
-	if (len < 24)
-		return NULL;
-
 	fc = le16_to_cpu(hdr->frame_control);
 
 	switch (fc & IEEE80211_FCTL_FTYPE) {
@@ -273,8 +283,18 @@
 	case IEEE80211_FTYPE_CTL:
 		if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)
 			return hdr->addr1;
+		else if ((fc & IEEE80211_FCTL_STYPE) ==
+			 IEEE80211_STYPE_BACK_REQ) {
+			switch (type) {
+				case IEEE80211_IF_TYPE_STA:
+					return hdr->addr2;
+				case IEEE80211_IF_TYPE_AP:
+					return hdr->addr1;
+			}
+		}
 		else
 			return NULL;
+		break;
 	}
 
 	return NULL;
@@ -296,7 +316,7 @@
 		 * bit position 1 so the result has the value 0 or 2.
 		 */
 		hdrlen += (fc & IEEE80211_STYPE_QOS_DATA)
-				>> (ilog2(IEEE80211_STYPE_QOS_DATA)-1);
+				>> (7 /* QoS data pos */-1);
 		break;
 	case IEEE80211_FTYPE_CTL:
 		/*
@@ -430,7 +450,7 @@
 	if (!tx->u.tx.rate)
 		return TXRX_DROP;
 	if (tx->u.tx.mode->mode == MODE_IEEE80211G &&
-	    tx->local->cts_protect_erp_frames && tx->fragmented &&
+	    tx->sdata->use_protection && tx->fragmented &&
 	    extra.nonerp) {
 		tx->u.tx.last_frag_rate = tx->u.tx.rate;
 		tx->u.tx.probe_last_frag = extra.probe ? 1 : 0;
@@ -450,6 +470,21 @@
 		tx->u.tx.control->tx_rate = tx->u.tx.rate->val2;
 	}
 
+	/* only data unicast frame */
+	if ((tx->u.tx.rate) && tx->skb && tx->sdata && tx->u.tx.unicast &&
+	    (tx->sdata->type == IEEE80211_IF_TYPE_STA ||
+	     tx->sdata->type == IEEE80211_IF_TYPE_IBSS )&& !extra.mgmt_data) {
+		struct ieee80211_hdr *hdr;
+		u16 fc;
+
+		hdr = (struct ieee80211_hdr *) tx->skb->data;
+		fc = le16_to_cpu(hdr->frame_control);
+
+		if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
+			tx->sdata->u.sta.last_rate = tx->u.tx.rate->rate *
+								     100000;
+	}
+
 	return TXRX_CONTINUE;
 }
 
@@ -528,7 +563,7 @@
 		/* reserve enough extra head and tail room for possible
 		 * encryption */
 		frag = frags[i] =
-			dev_alloc_skb(tx->local->hw.extra_tx_headroom +
+			dev_alloc_skb(tx->local->tx_headroom +
 				      frag_threshold +
 				      IEEE80211_ENCRYPT_HEADROOM +
 				      IEEE80211_ENCRYPT_TAILROOM);
@@ -537,8 +572,8 @@
 		/* Make sure that all fragments use the same priority so
 		 * that they end up using the same TX queue */
 		frag->priority = first->priority;
-		skb_reserve(frag, tx->local->hw.extra_tx_headroom +
-			IEEE80211_ENCRYPT_HEADROOM);
+		skb_reserve(frag, tx->local->tx_headroom +
+				  IEEE80211_ENCRYPT_HEADROOM);
 		fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
 		memcpy(fhdr, first->data, hdrlen);
 		if (i == num_fragm - 2)
@@ -856,8 +891,7 @@
 	 * for the frame. */
 	if (mode->mode == MODE_IEEE80211G &&
 	    (tx->u.tx.rate->flags & IEEE80211_RATE_ERP) &&
-	    tx->u.tx.unicast &&
-	    tx->local->cts_protect_erp_frames &&
+	    tx->u.tx.unicast && tx->sdata->use_protection &&
 	    !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
 		control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
 
@@ -1118,7 +1152,138 @@
 }
 
 
-static void inline
+/*
+ * deal with packet injection down monitor interface
+ * with Radiotap Header -- only called for monitor mode interface
+ */
+
+static ieee80211_txrx_result
+__ieee80211_parse_tx_radiotap(
+	struct ieee80211_txrx_data *tx,
+	struct sk_buff *skb, struct ieee80211_tx_control *control)
+{
+	/*
+	 * this is the moment to interpret and discard the radiotap header that
+	 * must be at the start of the packet injected in Monitor mode
+	 *
+	 * Need to take some care with endian-ness since radiotap
+	 * args are little-endian
+	 */
+
+	struct ieee80211_radiotap_iterator iterator;
+	struct ieee80211_radiotap_header *rthdr =
+		(struct ieee80211_radiotap_header *) skb->data;
+	struct ieee80211_hw_mode *mode = tx->local->hw.conf.mode;
+	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
+
+	/*
+	 * default control situation for all injected packets
+	 * FIXME: this does not suit all usage cases, expand to allow control
+	 */
+
+	control->retry_limit = 1; /* no retry */
+	control->key_idx = -1; /* no encryption key */
+	control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
+			    IEEE80211_TXCTL_USE_CTS_PROTECT);
+	control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT |
+			  IEEE80211_TXCTL_NO_ACK;
+	control->antenna_sel_tx = 0; /* default to default antenna */
+
+	/*
+	 * for every radiotap entry that is present
+	 * (ieee80211_radiotap_iterator_next returns -ENOENT when no more
+	 * entries present, or -EINVAL on error)
+	 */
+
+	while (!ret) {
+		int i, target_rate;
+
+		ret = ieee80211_radiotap_iterator_next(&iterator);
+
+		if (ret)
+			continue;
+
+		/* see if this argument is something we can use */
+		switch (iterator.this_arg_index) {
+		/*
+		 * You must take care when dereferencing iterator.this_arg
+		 * for multibyte types... the pointer is not aligned.  Use
+		 * get_unaligned((type *)iterator.this_arg) to dereference
+		 * iterator.this_arg for type "type" safely on all arches.
+		*/
+		case IEEE80211_RADIOTAP_RATE:
+			/*
+			 * radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
+			 * ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
+			 */
+			target_rate = (*iterator.this_arg) * 5;
+			for (i = 0; i < mode->num_rates; i++) {
+				struct ieee80211_rate *r = &mode->rates[i];
+
+				if (r->rate > target_rate)
+					continue;
+
+				control->rate = r;
+
+				if (r->flags & IEEE80211_RATE_PREAMBLE2)
+					control->tx_rate = r->val2;
+				else
+					control->tx_rate = r->val;
+
+				/* end on exact match */
+				if (r->rate == target_rate)
+					i = mode->num_rates;
+			}
+			break;
+
+		case IEEE80211_RADIOTAP_ANTENNA:
+			/*
+			 * radiotap uses 0 for 1st ant, mac80211 is 1 for
+			 * 1st ant
+			 */
+			control->antenna_sel_tx = (*iterator.this_arg) + 1;
+			break;
+
+		case IEEE80211_RADIOTAP_DBM_TX_POWER:
+			control->power_level = *iterator.this_arg;
+			break;
+
+		case IEEE80211_RADIOTAP_FLAGS:
+			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
+				/*
+				 * this indicates that the skb we have been
+				 * handed has the 32-bit FCS CRC at the end...
+				 * we should react to that by snipping it off
+				 * because it will be recomputed and added
+				 * on transmission
+				 */
+				if (skb->len < (iterator.max_length + FCS_LEN))
+					return TXRX_DROP;
+
+				skb_trim(skb, skb->len - FCS_LEN);
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
+		return TXRX_DROP;
+
+	/*
+	 * remove the radiotap header
+	 * iterator->max_length was sanity-checked against
+	 * skb->len by iterator init
+	 */
+	skb_pull(skb, iterator.max_length);
+
+	return TXRX_CONTINUE;
+}
+
+
+static ieee80211_txrx_result inline
 __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
 		       struct sk_buff *skb,
 		       struct net_device *dev,
@@ -1126,6 +1291,9 @@
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	struct ieee80211_sub_if_data *sdata;
+	ieee80211_txrx_result res = TXRX_CONTINUE;
+
 	int hdrlen;
 
 	memset(tx, 0, sizeof(*tx));
@@ -1135,7 +1303,32 @@
 	tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	tx->sta = sta_info_get(local, hdr->addr1);
 	tx->fc = le16_to_cpu(hdr->frame_control);
+
+	/*
+	 * set defaults for things that can be set by
+	 * injected radiotap headers
+	 */
 	control->power_level = local->hw.conf.power_level;
+	control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
+	if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta)
+		control->antenna_sel_tx = tx->sta->antenna_sel_tx;
+
+	/* process and remove the injection radiotap header */
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	if (unlikely(sdata->type == IEEE80211_IF_TYPE_MNTR)) {
+		if (__ieee80211_parse_tx_radiotap(tx, skb, control) ==
+								TXRX_DROP) {
+			return TXRX_DROP;
+		}
+		/*
+		 * we removed the radiotap header after this point,
+		 * we filled control with what we could use
+		 * set to the actual ieee header now
+		 */
+		hdr = (struct ieee80211_hdr *) skb->data;
+		res = TXRX_QUEUED; /* indication it was monitor packet */
+	}
+
 	tx->u.tx.control = control;
 	tx->u.tx.unicast = !is_multicast_ether_addr(hdr->addr1);
 	if (is_multicast_ether_addr(hdr->addr1))
@@ -1152,9 +1345,6 @@
 		control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
 		tx->sta->clear_dst_mask = 0;
 	}
-	control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
-	if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta)
-		control->antenna_sel_tx = tx->sta->antenna_sel_tx;
 	hdrlen = ieee80211_get_hdrlen(tx->fc);
 	if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) {
 		u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)];
@@ -1162,6 +1352,7 @@
 	}
 	control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT;
 
+	return res;
 }
 
 static int inline is_ieee80211_device(struct net_device *dev,
@@ -1274,7 +1465,7 @@
 	struct sta_info *sta;
 	ieee80211_tx_handler *handler;
 	struct ieee80211_txrx_data tx;
-	ieee80211_txrx_result res = TXRX_DROP;
+	ieee80211_txrx_result res = TXRX_DROP, res_prepare;
 	int ret, i;
 
 	WARN_ON(__ieee80211_queue_pending(local, control->queue));
@@ -1284,15 +1475,26 @@
 		return 0;
 	}
 
-	__ieee80211_tx_prepare(&tx, skb, dev, control);
+	res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
+
+	if (res_prepare == TXRX_DROP) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
 	sta = tx.sta;
 	tx.u.tx.mgmt_interface = mgmt;
 	tx.u.tx.mode = local->hw.conf.mode;
 
-	for (handler = local->tx_handlers; *handler != NULL; handler++) {
-		res = (*handler)(&tx);
-		if (res != TXRX_CONTINUE)
-			break;
+	if (res_prepare == TXRX_QUEUED) { /* if it was an injected packet */
+		res = TXRX_CONTINUE;
+	} else {
+		for (handler = local->tx_handlers; *handler != NULL;
+		     handler++) {
+			res = (*handler)(&tx);
+			if (res != TXRX_CONTINUE)
+				break;
+		}
 	}
 
 	skb = tx.skb; /* handlers are allowed to change skb */
@@ -1467,11 +1669,11 @@
 	}
 	osdata = IEEE80211_DEV_TO_SUB_IF(odev);
 
-	headroom = osdata->local->hw.extra_tx_headroom +
-		IEEE80211_ENCRYPT_HEADROOM;
+	headroom = osdata->local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM;
 	if (skb_headroom(skb) < headroom) {
 		if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
 			dev_kfree_skb(skb);
+			dev_put(odev);
 			return 0;
 		}
 	}
@@ -1484,6 +1686,9 @@
 		control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
 	if (pkt_data->requeue)
 		control.flags |= IEEE80211_TXCTL_REQUEUE;
+	if (pkt_data->ht_queue)
+		control.flags |= IEEE80211_TXCTL_HT_MPDU_AGG;
+
 	control.queue = pkt_data->queue;
 
 	ret = ieee80211_tx(odev, skb, &control,
@@ -1494,6 +1699,56 @@
 }
 
 
+int ieee80211_monitor_start_xmit(struct sk_buff *skb,
+				 struct net_device *dev)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_tx_packet_data *pkt_data;
+	struct ieee80211_radiotap_header *prthdr =
+		(struct ieee80211_radiotap_header *)skb->data;
+	u16 len;
+
+	/*
+	 * there must be a radiotap header at the
+	 * start in this case
+	 */
+	if (unlikely(prthdr->it_version)) {
+		/* only version 0 is supported */
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	skb->dev = local->mdev;
+
+	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+	memset(pkt_data, 0, sizeof(*pkt_data));
+	pkt_data->ifindex = dev->ifindex;
+	pkt_data->mgmt_iface = 0;
+	pkt_data->do_not_encrypt = 1;
+
+	/* above needed because we set skb device to master */
+
+	/*
+	 * fix up the pointers accounting for the radiotap
+	 * header still being in there.  We are being given
+	 * a precooked IEEE80211 header so no need for
+	 * normal processing
+	 */
+	len = le16_to_cpu(get_unaligned(&prthdr->it_len));
+	skb_set_mac_header(skb, len);
+	skb_set_network_header(skb, len + sizeof(struct ieee80211_hdr));
+	skb_set_transport_header(skb, len + sizeof(struct ieee80211_hdr));
+
+	/*
+	 * pass the radiotap header up to
+	 * the next stage intact
+	 */
+	dev_queue_xmit(skb);
+
+	return NETDEV_TX_OK;
+}
+
+
 /**
  * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
  * subinterfaces (wlan#, WDS, and VLAN interfaces)
@@ -1509,8 +1764,8 @@
  * encapsulated packet will then be passed to master interface, wlan#.11, for
  * transmission (through low-level driver).
  */
-static int ieee80211_subif_start_xmit(struct sk_buff *skb,
-				      struct net_device *dev)
+int ieee80211_subif_start_xmit(struct sk_buff *skb,
+			       struct net_device *dev)
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_tx_packet_data *pkt_data;
@@ -1557,11 +1812,18 @@
 		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 */
@@ -1619,7 +1881,7 @@
 	 * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
 	 * alloc_skb() (net/core/skbuff.c)
 	 */
-	head_need = hdrlen + encaps_len + local->hw.extra_tx_headroom;
+	head_need = hdrlen + encaps_len + local->tx_headroom;
 	head_need -= skb_headroom(skb);
 
 	/* We are going to modify skb data, so make a copy of it if happens to
@@ -1658,7 +1920,7 @@
 
 	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
 	memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
-	pkt_data->ifindex = sdata->dev->ifindex;
+	pkt_data->ifindex = dev->ifindex;
 	pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
 	pkt_data->do_not_encrypt = no_encrypt;
 
@@ -1706,9 +1968,9 @@
 		return 0;
 	}
 
-	if (skb_headroom(skb) < sdata->local->hw.extra_tx_headroom) {
-		if (pskb_expand_head(skb,
-		    sdata->local->hw.extra_tx_headroom, 0, GFP_ATOMIC)) {
+	if (skb_headroom(skb) < sdata->local->tx_headroom) {
+		if (pskb_expand_head(skb, sdata->local->tx_headroom,
+				     0, GFP_ATOMIC)) {
 			dev_kfree_skb(skb);
 			return 0;
 		}
@@ -1847,12 +2109,12 @@
 	bh_len = ap->beacon_head_len;
 	bt_len = ap->beacon_tail_len;
 
-	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+	skb = dev_alloc_skb(local->tx_headroom +
 		bh_len + bt_len + 256 /* maximum TIM len */);
 	if (!skb)
 		return NULL;
 
-	skb_reserve(skb, local->hw.extra_tx_headroom);
+	skb_reserve(skb, local->tx_headroom);
 	memcpy(skb_put(skb, bh_len), b_head, bh_len);
 
 	ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
@@ -2122,7 +2384,9 @@
 
 	local->hw.conf.channel = chan->chan;
 	local->hw.conf.channel_val = chan->val;
-	local->hw.conf.power_level = chan->power_level;
+	local->hw.conf.power_level =
+		(local->user_txpow < chan->power_level) ?
+			local->user_txpow : chan->power_level;
 	local->hw.conf.freq = chan->freq;
 	local->hw.conf.phymode = mode->mode;
 	local->hw.conf.antenna_max = chan->antenna_max;
@@ -2281,6 +2545,7 @@
 	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) {
@@ -2376,8 +2641,7 @@
 	struct ieee80211_if_init_conf conf;
 
 	if (local->open_count && local->open_count == local->monitors &&
-	    !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER) &&
-	    local->ops->add_interface) {
+	    !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
 		conf.if_id = -1;
 		conf.type = IEEE80211_IF_TYPE_MNTR;
 		conf.mac_addr = NULL;
@@ -2420,21 +2684,14 @@
 	}
 	ieee80211_start_soft_monitor(local);
 
-	if (local->ops->add_interface) {
-		conf.if_id = dev->ifindex;
-		conf.type = sdata->type;
-		conf.mac_addr = dev->dev_addr;
-		res = local->ops->add_interface(local_to_hw(local), &conf);
-		if (res) {
-			if (sdata->type == IEEE80211_IF_TYPE_MNTR)
-				ieee80211_start_hard_monitor(local);
-			return res;
-		}
-	} else {
-		if (sdata->type != IEEE80211_IF_TYPE_STA)
-			return -EOPNOTSUPP;
-		if (local->open_count > 0)
-			return -ENOBUFS;
+	conf.if_id = dev->ifindex;
+	conf.type = sdata->type;
+	conf.mac_addr = dev->dev_addr;
+	res = local->ops->add_interface(local_to_hw(local), &conf);
+	if (res) {
+		if (sdata->type == IEEE80211_IF_TYPE_MNTR)
+			ieee80211_start_hard_monitor(local);
+		return res;
 	}
 
 	if (local->open_count == 0) {
@@ -2548,6 +2805,140 @@
 }
 
 
+inline static unsigned int calc_pad_len(unsigned int len)
+{
+	return ((4 - len) & 0x3);
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_data_agg(struct ieee80211_txrx_data *rx)
+{
+	struct net_device *dev = rx->dev;
+	struct ieee80211_local *local = rx->local;
+	u16 fc, hdrlen, ethertype;
+	u8 *payload;
+	struct sk_buff *skb = rx->skb, *skb2, *frame;
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	const struct ethhdr* eth;
+	int remaining;
+
+	fc = rx->fc;
+	if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
+		return TXRX_CONTINUE;
+
+	if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+		return TXRX_DROP;
+
+	if (!rx->u.rx.is_agg_frame)
+		return TXRX_CONTINUE;
+
+	hdrlen = ieee80211_get_hdrlen(fc);
+
+	payload = skb->data + hdrlen;
+
+	if (unlikely((skb->len - hdrlen) < 8)) {
+		if (net_ratelimit())
+			printk(KERN_DEBUG "%s: RX too short data frame "
+			       "payload\n", dev->name);
+		return TXRX_DROP;
+	}
+
+	ethertype = (payload[6] << 8) | payload[7];
+
+	if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
+		    ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+		   compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
+		/* remove RFC1042 or Bridge-Tunnel encapsulation and
+		* replace EtherType */
+		eth = (struct ethhdr*) (skb->data + hdrlen + 6);
+		remaining = skb->len - (hdrlen + 6);
+	} else {
+		eth = (struct ethhdr*) (skb->data + hdrlen);
+		remaining = skb->len - hdrlen;
+	}
+
+	while ((u8*)eth < skb->data + skb->len) {
+		u8 padding;
+		unsigned int subframe_len = sizeof(struct ethhdr) +
+						   ntohs(eth->h_proto);
+
+		padding = calc_pad_len(subframe_len);
+		/* the last MSDU has no padding */
+		if (subframe_len > remaining)
+			return TXRX_DROP;
+
+		frame = dev_alloc_skb(local->hw.extra_tx_headroom +
+				      subframe_len);
+
+		if (frame == NULL)
+			return TXRX_DROP;
+
+		memcpy(skb_put(frame, subframe_len), (u8*)eth, subframe_len);
+		skb_set_mac_header(frame, 0);
+		skb2 = NULL;
+
+		sdata->stats.rx_packets++;
+		sdata->stats.rx_bytes += frame->len;
+
+		if (local->bridge_packets &&
+		    (sdata->type == IEEE80211_IF_TYPE_AP ||
+		     sdata->type == IEEE80211_IF_TYPE_VLAN) &&
+		     rx->u.rx.ra_match) {
+			if (is_multicast_ether_addr(frame->data)) {
+				/* send multicast frames both to higher layers
+				* in local net stack and back to the wireless
+				* media */
+				skb2 = skb_copy(frame, GFP_ATOMIC);
+				if (!skb2)
+					printk(KERN_DEBUG "%s: failed to clone"
+					       " multicast frame\n", dev->name);
+			} else {
+				struct sta_info *dsta;
+
+				dsta = sta_info_get(local, frame->data);
+				if (dsta && !dsta->dev)
+					printk(KERN_DEBUG "Station with null "
+					       "dev structure!\n");
+				else if (dsta && dsta->dev == dev) {
+					/* Destination station is associated
+					* to this AP, so send the frame
+					* directly to it and do not pass
+					* the frame to local net stack.
+					*/
+					skb2 = frame;
+					frame = NULL;
+				}
+				if (dsta)
+					sta_info_put(dsta);
+			}
+		}
+		if (frame) {
+			/* deliver to local stack */
+			frame->protocol = eth_type_trans(frame, dev);
+			frame->priority = skb->priority;
+			frame->dev = dev;
+			netif_rx(frame);
+		}
+
+		if (skb2) {
+			/* send to wireless media */
+			skb2->protocol = __constant_htons(ETH_P_802_3);
+			skb_set_network_header(skb2, 0);
+			skb_set_mac_header(skb2, 0);
+			skb2->priority = skb->priority;
+			skb2->dev = dev;
+			dev_queue_xmit(skb2);
+		}
+
+		eth = (struct ethhdr*)((u8*)eth + subframe_len + padding);
+
+		remaining -= (subframe_len + padding);
+	}
+
+	dev_kfree_skb(skb);
+	return TXRX_QUEUED;
+}
+
 static ieee80211_txrx_result
 ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
 {
@@ -2941,34 +3332,6 @@
 }
 EXPORT_SYMBOL(ieee80211_radar_status);
 
-int ieee80211_set_aid_for_sta(struct ieee80211_hw *hw, u8 *peer_address,
-			      u16 aid)
-{
-	struct sk_buff *skb;
-	struct ieee80211_msg_set_aid_for_sta *msg;
-	struct ieee80211_local *local = hw_to_local(hw);
-
-	/* unlikely because if this event only happens for APs,
-	 * which require an open ap device. */
-	if (unlikely(!local->apdev))
-		return 0;
-
-	skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
-			    sizeof(struct ieee80211_msg_set_aid_for_sta));
-
-	if (!skb)
-		return -ENOMEM;
-	skb_reserve(skb, sizeof(struct ieee80211_frame_info));
-
-	msg = (struct ieee80211_msg_set_aid_for_sta *)
-		skb_put(skb, sizeof(struct ieee80211_msg_set_aid_for_sta));
-	memcpy(msg->sta_address, peer_address, ETH_ALEN);
-	msg->aid = aid;
-
-	ieee80211_rx_mgmt(local, skb, NULL, ieee80211_msg_set_aid_for_sta);
-	return 0;
-}
-EXPORT_SYMBOL(ieee80211_set_aid_for_sta);
 
 static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
 {
@@ -3455,7 +3818,7 @@
 	 * BSSID to avoid keeping the current IBSS network alive in cases where
 	 * other STAs are using different BSSID. */
 	if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) {
-		u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len);
+		u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,IEEE80211_IF_TYPE_IBSS);
 		if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
 			sta->last_rx = jiffies;
 	} else
@@ -3473,12 +3836,20 @@
 
 	sta->rx_fragments++;
 	sta->rx_bytes += rx->skb->len;
-	sta->last_rssi = (sta->last_rssi * 15 +
-			  rx->u.rx.status->ssi) / 16;
-	sta->last_signal = (sta->last_signal * 15 +
-			    rx->u.rx.status->signal) / 16;
-	sta->last_noise = (sta->last_noise * 15 +
-			   rx->u.rx.status->noise) / 16;
+
+	/* Low pass filter:  15/16 current avg + new.
+	 * Accumulated values here are 16x values sent from driver. */
+	sta->accum_rssi = sta->accum_rssi - (sta->accum_rssi >> 4) +
+			  rx->u.rx.status->ssi;
+	sta->accum_signal = sta->accum_signal - (sta->accum_signal >> 4) +
+			  rx->u.rx.status->signal;
+	sta->accum_noise = sta->accum_noise - (sta->accum_noise >> 4) +
+			  rx->u.rx.status->noise;
+
+	/* Quantize the averages (divide by 16) */
+	sta->last_rssi = sta->accum_rssi >> 4;
+	sta->last_signal = sta->accum_signal >> 4;
+	sta->last_noise = sta->accum_noise >> 4;
 
 	if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
 		/* Change STA power saving mode only in the end of a frame
@@ -3618,11 +3989,52 @@
 	return TXRX_CONTINUE;
 }
 
+static ieee80211_txrx_result
+ieee80211_rx_h_ctrl(struct ieee80211_txrx_data *rx)
+{
+	struct ieee80211_local *local = rx->local;
+	struct ieee80211_hw *hw = &local->hw;
+	struct sk_buff *skb = rx->skb;
+	struct ieee80211_bar *bar = (struct ieee80211_bar *) skb->data;
+	struct tid_ht_agg_info_rx *tid_rx_info;
+	u16 start_seq_num;
+	u16 tid;
+
+	if (likely((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL))
+		return TXRX_CONTINUE;
+
+	if ((rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BACK_REQ) {
+		if (!rx->sta)
+			return TXRX_CONTINUE;
+		tid = le16_to_cpu(bar->control) >> 12;
+		tid_rx_info = &(rx->sta->ht_ba_mlme.tid_agg_info_rx[tid]);
+		if (tid_rx_info->state != HT_AGG_STATE_OPERATIONAL)
+			return TXRX_CONTINUE;
+
+		start_seq_num = le16_to_cpu(bar->start_seq_num) >> 4;
+
+		/* reset session timer */
+		if (tid_rx_info->timeout) {
+			unsigned long expires = jiffies +
+									(tid_rx_info->timeout / 1000) * HZ;
+			mod_timer(&tid_rx_info->session_timer,expires);
+		}
+
+		/* manage reordering buffer according to requested sequence number */
+		ieee80211_sta_manage_reorder_buf(hw,rx,tid_rx_info,NULL,
+											start_seq_num,1);
+
+		return TXRX_DROP;
+	}
+
+	return TXRX_CONTINUE;
+}
 
 static ieee80211_txrx_result
 ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
 {
 	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
 
 	if (!rx->u.rx.ra_match)
 		return TXRX_DROP;
@@ -3632,6 +4044,12 @@
 	     sdata->type == IEEE80211_IF_TYPE_IBSS) &&
 	    !rx->local->user_space_mlme) {
 		ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
+	} else if ((sdata->type == IEEE80211_IF_TYPE_AP) &&
+			   ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) &&
+			   ((rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ACTION) &&
+			   (mgmt->u.action.category == WLAN_CATEGORY_BACK)){
+		/* 11n is not implemented yet in hostapd,so process in mac80211 */
+		ieee80211_rx_mgmt_action(rx->dev,NULL,mgmt,rx->skb->len);
 	} else {
 		/* Management frames are sent to hostapd for processing */
 		if (!rx->local->apdev)
@@ -3860,10 +4278,13 @@
 	} else {
 		struct ieee80211_sub_if_data *prev = NULL;
 		struct sk_buff *skb_new;
-		u8 *bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len);
+		u8 *bssid;
 
 		read_lock(&local->sub_if_lock);
 		list_for_each_entry(sdata, &local->sub_if_list, list) {
+			bssid = ieee80211_get_bssid(hdr,
+						    skb->len - radiotap_len,
+						    sdata->type);
 			rx.u.rx.ra_match = 1;
 			switch (sdata->type) {
 			case IEEE80211_IF_TYPE_STA:
@@ -4023,6 +4444,64 @@
 
 
 static ieee80211_txrx_result
+ieee80211_rx_h_reorder_ampdu(struct ieee80211_txrx_data *rx)
+{
+	struct ieee80211_local *local = rx->local;
+	struct ieee80211_hw *hw = &local->hw;
+	struct sk_buff *skb = rx->skb;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	struct tid_ht_agg_info_rx *tid_rx_info;
+	u16 sc;
+	u16 mpdu_seq_num;
+
+	if (!rx->sta)
+		return TXRX_CONTINUE;
+
+	tid_rx_info = &(rx->sta->ht_ba_mlme.tid_agg_info_rx[rx->u.rx.queue]);
+
+	/* filter the QoS data rx stream according to STA/TID
+	 * and check if this STA/TID is on aggregation */
+	if ((tid_rx_info->state != HT_AGG_STATE_OPERATIONAL)
+		|| ((rx->fc & 0x00CC) != (IEEE80211_STYPE_QOS_DATA |
+									IEEE80211_FTYPE_DATA)))
+		return TXRX_CONTINUE;
+
+	/* check if this frame has already been queued and ordered */
+	if(rx->u.rx.status->ordered == TXRX_QUEUED)
+		return TXRX_CONTINUE;
+
+	/* check if this frame has already been queued and ordered but
+	 * should be deleted */
+	if(rx->u.rx.status->ordered == TXRX_DROP) {
+		if(skb)
+			dev_kfree_skb(skb);
+		return TXRX_DROP;
+	}
+
+	/* new un-ordered ampdu frame - process it */
+
+	/* reset session timer */
+	if (tid_rx_info->timeout) {
+		unsigned long expires = jiffies + (tid_rx_info->timeout / 1000) * HZ;
+		mod_timer(&tid_rx_info->session_timer,expires);
+	}
+
+	/* if this mpdu is fragmented - terminate rx aggregation session */
+	sc = le16_to_cpu(hdr->seq_ctrl);
+	if (sc & IEEE80211_SCTL_FRAG) {
+		ieee80211_sta_stop_rx_BA_session(rx->dev,rx->sta->addr,rx->u.rx.queue,
+										 0,WLAN_REASON_QSTA_REQUIRE_SETUP);
+		return TXRX_CONTINUE;
+	}
+
+	/* according to mpdu sequence number deal with reordering buffer */
+	mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
+	return ieee80211_sta_manage_reorder_buf(hw,rx,tid_rx_info,skb,
+											mpdu_seq_num,0);
+}
+
+
+static ieee80211_txrx_result
 ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx)
 {
 	struct ieee80211_local *local = rx->local;
@@ -4035,7 +4514,7 @@
 
 	/* Estimate total channel use caused by this frame */
 
-	if (unlikely(mode->num_rates < 0))
+	if (unlikely(mode->num_rates < 0) || (rx->u.rx.status->ordered != 0))
 		return TXRX_CONTINUE;
 
 	rate = &mode->rates[0];
@@ -4284,6 +4763,9 @@
 	struct ieee80211_local *local = hw_to_local(hw);
 	u16 frag, type;
 	u32 msg_type;
+	struct ieee80211_tx_status_rtap_hdr *rthdr;
+	struct ieee80211_sub_if_data *sdata;
+	int monitors;
 
 	if (!status) {
 		printk(KERN_ERR
@@ -4395,27 +4877,99 @@
 			local->dot11FailedCount++;
 	}
 
-	if (!(status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS)
-	    || unlikely(!local->apdev)) {
+	msg_type = (status->flags & IEEE80211_TX_STATUS_ACK) ?
+		ieee80211_msg_tx_callback_ack : ieee80211_msg_tx_callback_fail;
+
+	/* this was a transmitted frame, but now we want to reuse it */
+	skb_orphan(skb);
+
+	if ((status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS) &&
+	    local->apdev) {
+		if (local->monitors) {
+			skb2 = skb_clone(skb, GFP_ATOMIC);
+		} else {
+			skb2 = skb;
+			skb = NULL;
+		}
+
+		if (skb2)
+			/* Send frame to hostapd */
+			ieee80211_rx_mgmt(local, skb2, NULL, msg_type);
+
+		if (!skb)
+			return;
+	}
+
+	if (!local->monitors) {
 		dev_kfree_skb(skb);
 		return;
 	}
 
-	msg_type = (status->flags & IEEE80211_TX_STATUS_ACK) ?
-		ieee80211_msg_tx_callback_ack : ieee80211_msg_tx_callback_fail;
+	/* send frame to monitor interfaces now */
 
-	/* skb was the original skb used for TX. Clone it and give the clone
-	 * to netif_rx(). Free original skb. */
-	skb2 = skb_copy(skb, GFP_ATOMIC);
-	if (!skb2) {
+	if (skb_headroom(skb) < sizeof(*rthdr)) {
+		printk(KERN_ERR "ieee80211_tx_status: headroom too small\n");
 		dev_kfree_skb(skb);
 		return;
 	}
-	dev_kfree_skb(skb);
-	skb = skb2;
 
-	/* Send frame to hostapd */
-	ieee80211_rx_mgmt(local, skb, NULL, msg_type);
+	rthdr = (struct ieee80211_tx_status_rtap_hdr*)
+				skb_push(skb, sizeof(*rthdr));
+
+	memset(rthdr, 0, sizeof(*rthdr));
+	rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
+	rthdr->hdr.it_present =
+		cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
+			    (1 << IEEE80211_RADIOTAP_DATA_RETRIES));
+
+	if (!(status->flags & IEEE80211_TX_STATUS_ACK) &&
+	    !is_multicast_ether_addr(hdr->addr1))
+		rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL);
+
+	if ((status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) &&
+	    (status->control.flags & IEEE80211_TXCTL_USE_CTS_PROTECT))
+		rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS);
+	else if (status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS)
+		rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS);
+
+	rthdr->data_retries = status->retry_count;
+
+	read_lock(&local->sub_if_lock);
+	monitors = local->monitors;
+	list_for_each_entry(sdata, &local->sub_if_list, list) {
+		/*
+		 * Using the monitors counter is possibly racy, but
+		 * if the value is wrong we simply either clone the skb
+		 * once too much or forget sending it to one monitor iface
+		 * The latter case isn't nice but fixing the race is much
+		 * more complicated.
+		 */
+		if (!monitors || !skb)
+			goto out;
+
+		if (sdata->type == IEEE80211_IF_TYPE_MNTR) {
+			if (!netif_running(sdata->dev))
+				continue;
+			monitors--;
+			if (monitors)
+				skb2 = skb_clone(skb, GFP_KERNEL);
+			else
+				skb2 = NULL;
+			skb->dev = sdata->dev;
+			/* XXX: is this sufficient for BPF? */
+			skb_set_mac_header(skb, 0);
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+			skb->pkt_type = PACKET_OTHERHOST;
+			skb->protocol = htons(ETH_P_802_2);
+			memset(skb->cb, 0, sizeof(skb->cb));
+			netif_rx(skb);
+			skb = skb2;
+		}
+	}
+ out:
+	read_unlock(&local->sub_if_lock);
+	if (skb)
+		dev_kfree_skb(skb);
 }
 EXPORT_SYMBOL(ieee80211_tx_status);
 
@@ -4428,6 +4982,7 @@
 {
 	ieee80211_rx_h_parse_qos,
 	ieee80211_rx_h_load_stats,
+	ieee80211_rx_h_reorder_ampdu,
 	NULL
 };
 
@@ -4452,7 +5007,9 @@
 	ieee80211_rx_h_remove_qos_control,
 	ieee80211_rx_h_802_1x_pae,
 	ieee80211_rx_h_drop_unencrypted,
+	ieee80211_rx_h_data_agg,
 	ieee80211_rx_h_data,
+	ieee80211_rx_h_ctrl,
 	ieee80211_rx_h_mgmt,
 	NULL
 };
@@ -4619,6 +5176,9 @@
 			 ((sizeof(struct ieee80211_local) +
 			   NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
 
+	BUG_ON(!ops->tx);
+	BUG_ON(!ops->config);
+	BUG_ON(!ops->add_interface);
 	local->ops = ops;
 
 	/* for now, mdev needs sub_if_data :/ */
@@ -4647,8 +5207,7 @@
 	local->short_retry_limit = 7;
 	local->long_retry_limit = 4;
 	local->hw.conf.radio_enabled = 1;
-	local->rate_ctrl_num_up = RATE_CONTROL_NUM_UP;
-	local->rate_ctrl_num_down = RATE_CONTROL_NUM_DOWN;
+	local->hw.conf.power_management_enable = 0;
 
 	local->enabled_modes = (unsigned int) -1;
 
@@ -4671,7 +5230,7 @@
 	mdev->type = ARPHRD_IEEE80211;
 	mdev->hard_header_parse = header_parse_80211;
 
-	sdata->type = IEEE80211_IF_TYPE_AP;
+	sdata->type = 0xFFFFFFFF;
 	sdata->dev = mdev;
 	sdata->local = local;
 	sdata->u.ap.force_unicast_rateidx = -1;
@@ -4712,6 +5271,14 @@
 		goto fail_workqueue;
 	}
 
+	/*
+	 * The hardware needs headroom for sending the frame,
+	 * and we need some headroom for passing the frame to monitor
+	 * interfaces, but never both at the same time.
+	 */
+	local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom,
+				   sizeof(struct ieee80211_tx_status_rtap_hdr));
+
 	debugfs_hw_add(local);
 
 	local->hw.conf.beacon_int = 1000;
@@ -4725,6 +5292,7 @@
 	if (local->hw.max_rssi < 0 || local->hw.max_noise < 0)
 		local->wstats_flags |= IW_QUAL_DBM;
 
+	local->user_txpow = IEEE80211_MAX_TXPOWER;
 	result = sta_info_start(local);
 	if (result < 0)
 		goto fail_sta_info;
@@ -4743,10 +5311,13 @@
 
 	ieee80211_debugfs_add_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
 
-	result = ieee80211_init_rate_ctrl_alg(local, NULL);
+	result = ieee80211_init_rate_ctrl_alg(local,
+				hw->preferred_rate_control);
 	if (result < 0) {
-		printk(KERN_DEBUG "%s: Failed to initialize rate control "
-		       "algorithm\n", local->mdev->name);
+		printk(KERN_DEBUG "%s: Failed to initialize %s rate control "
+			"algorithm\n", local->mdev->name,
+			hw->preferred_rate_control ?
+			hw->preferred_rate_control : "default");
 		goto fail_rate;
 	}
 
@@ -4818,7 +5389,7 @@
 	}
 
 	if (!(hw->flags & IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED))
-		ieee80211_init_client(local->mdev);
+		ieee80211_set_default_regdomain(mode);
 
 	return 0;
 }
@@ -4947,6 +5518,141 @@
 }
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
+int ieee80211_start_BA_session(struct ieee80211_hw *hw, u8 *da, u16 tid)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct sta_info *sta;
+	struct net_device *dev;
+	struct ieee80211_sub_if_data *sdata;
+	u16 start_seq_num=0;
+	int rc = -EAGAIN;
+
+	if (tid >= STA_TID_NUM)
+		return -EINVAL;
+	else
+		printk (KERN_DEBUG "Open a BA session requested on DA " MAC_FMT " tid %d\n",MAC_ARG(da),tid);
+
+	sta = sta_info_get(local, da);
+	if (!sta)
+		return -ENOENT;
+
+	/* check if the TID is not in aggregation flow already */
+	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+	if (sta->ht_ba_mlme.tid_agg_info_tx[tid].state == HT_AGG_STATE_IDLE)
+		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_START_BA;
+	else {
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		printk (KERN_DEBUG "BA request denied - aggregation is not idle on tid %d\n",tid);
+		goto start_BA_exit;
+	}
+	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+
+	/* create a new queue for this aggregation */
+	rc = ieee80211_ht_agg_queue_add(local,sta,tid);
+
+	/* case no queue is available to aggregation don't switch to aggregation */
+	if(rc) {
+		printk (KERN_DEBUG "BA request denied - no queue available for tid %d\n",tid);
+	} else { /* try to move HW to aggregation */
+		rc = -EAGAIN;
+		dev = sta->dev;
+		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+		start_seq_num = sdata->sequence >> 4; /* this default value can be changed by driver */
+		if (local->ops->ht_tx_agg_start)
+			rc = local->ops->ht_tx_agg_start(hw,da,tid,&start_seq_num);
+		if(rc)
+			/* remove the queue for this aggregation */
+			ieee80211_ht_agg_queue_remove(local,sta,tid);
+	}
+
+	/* case we denied aggregation go back to legacy */
+	if(rc) {
+		spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_IDLE;
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		printk (KERN_DEBUG "BA request denied - HW or queue unavailable for tid %d\n",tid);
+		goto start_BA_exit;
+	}
+
+	/* send an addBA request */
+	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+	sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_ADDBA_REQUESTED;
+	sta->ht_ba_mlme.dialog_token_allocator++;
+	sta->ht_ba_mlme.tid_agg_info_tx[tid].dialog_token = sta->ht_ba_mlme.dialog_token_allocator;
+	sta->ht_ba_mlme.tid_agg_info_tx[tid].start_seq_num = start_seq_num;
+	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+
+	ieee80211_send_addba_request(sta->dev, da, tid,
+								 sta->ht_ba_mlme.tid_agg_info_tx[tid].dialog_token,
+								 sta->ht_ba_mlme.tid_agg_info_tx[tid].start_seq_num,
+								 0x40,5000);
+
+	/* activate the timer for the recipient's addBA response */
+	sta->ht_ba_mlme.tid_agg_info_tx[tid].addba_resp_timer.expires = jiffies + ADDBA_RESP_INTERVAL;
+	add_timer(&sta->ht_ba_mlme.tid_agg_info_tx[tid].addba_resp_timer);
+	printk(KERN_DEBUG "activated addBA response timer on tid %d\n",tid);
+
+	rc = 0;
+start_BA_exit:
+	sta_info_put(sta);
+	return rc;
+}
+EXPORT_SYMBOL(ieee80211_start_BA_session);
+
+int ieee80211_stop_BA_session(struct ieee80211_hw *hw, u8 *da, u16 tid)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct sta_info *sta;
+	int rc = -EAGAIN;
+
+	if (tid >= STA_TID_NUM)
+		return -EINVAL;
+	else
+		printk (KERN_DEBUG "Stop a BA session requested on DA " MAC_FMT " tid %d\n",MAC_ARG(da),tid);
+
+	sta = sta_info_get(local, da);
+	if (!sta)
+		return -ENOENT;
+
+	/* check if the TID is in aggregation */
+	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+	if (sta->ht_ba_mlme.tid_agg_info_tx[tid].state == HT_AGG_STATE_OPERATIONAL)
+		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_INITIATOR_REQ_STOP_BA;
+	else {
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		printk (KERN_DEBUG "request denied - aggregation is not opertional yet on tid %d\n",tid);
+		goto stop_BA_exit;
+	}
+	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+
+	if (local->ops->ht_tx_agg_stop)
+		rc = local->ops->ht_tx_agg_stop(hw,da,tid,1 /*WLAN_BACK_INITIATOR*/);
+
+	/* case HW denied going back to legacy */
+	if(rc) {
+		spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_OPERATIONAL;
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		printk (KERN_ERR "HW problem - can not stop aggergation for tid %d\n",tid);
+		goto stop_BA_exit;
+	}
+
+	/* remove the queue for this aggregation */
+	ieee80211_ht_agg_queue_remove(local,sta,tid);
+
+	/* send a delBA */
+	ieee80211_send_delba(sta->dev, da, tid, 1 /*WLAN_BACK_INITIATOR*/, WLAN_REASON_QSTA_NOT_USE);
+	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+	sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_IDLE;
+	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+
+	rc = 0;
+stop_BA_exit:
+	sta_info_put(sta);
+	return rc;
+}
+EXPORT_SYMBOL(ieee80211_stop_BA_session);
+
 struct net_device_stats *ieee80211_dev_stats(struct net_device *dev)
 {
 	struct ieee80211_sub_if_data *sdata;
@@ -4969,6 +5675,7 @@
 	}
 
 	ieee80211_debugfs_netdev_init();
+	ieee80211_regdomain_init();
 
 	return 0;
 }
diff -ruN linux-2.6.22/net/mac80211/ieee80211_common.h linux-2.6.22-mac80211/net/mac80211/ieee80211_common.h
--- linux-2.6.22/net/mac80211/ieee80211_common.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/ieee80211_common.h	2007-08-23 20:59:59.000000000 +0200
@@ -47,21 +47,16 @@
 	ieee80211_msg_normal = 0,
 	ieee80211_msg_tx_callback_ack = 1,
 	ieee80211_msg_tx_callback_fail = 2,
-	ieee80211_msg_passive_scan = 3,
+	/* hole at 3, was ieee80211_msg_passive_scan but unused */
 	ieee80211_msg_wep_frame_unknown_key = 4,
 	ieee80211_msg_michael_mic_failure = 5,
 	/* hole at 6, was monitor but never sent to userspace */
 	ieee80211_msg_sta_not_assoc = 7,
-	ieee80211_msg_set_aid_for_sta = 8 /* used by Intersil MVC driver */,
+	/* 8 was ieee80211_msg_set_aid_for_sta */
 	ieee80211_msg_key_threshold_notification = 9,
 	ieee80211_msg_radar = 11,
 };
 
-struct ieee80211_msg_set_aid_for_sta {
-	char	sta_address[ETH_ALEN];
-	u16	aid;
-};
-
 struct ieee80211_msg_key_notification {
 	int tx_rx_count;
 	char ifname[IFNAMSIZ];
diff -ruN linux-2.6.22/net/mac80211/ieee80211_i.h linux-2.6.22-mac80211/net/mac80211/ieee80211_i.h
--- linux-2.6.22/net/mac80211/ieee80211_i.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/ieee80211_i.h	2007-08-23 20:59:59.000000000 +0200
@@ -59,6 +59,10 @@
  * 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;
@@ -90,6 +94,8 @@
 	size_t rsn_ie_len;
 	u8 *wmm_ie;
 	size_t wmm_ie_len;
+	u8 *ht_ie;
+	size_t ht_ie_len;
 #define IEEE80211_MAX_SUPP_RATES 32
 	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
 	size_t supp_rates_len;
@@ -99,6 +105,12 @@
 	int probe_resp;
 	unsigned long last_update;
 
+	/* during assocation, we save an ERP value from a probe response so
+	 * that we can feed ERP info to the driver when handling the
+	 * association completes. these fields probably won't be up-to-date
+	 * otherwise, you probably don't want to use them. */
+	int has_erp_value;
+	u8 erp_value;
 };
 
 
@@ -141,10 +153,12 @@
 			int sent_ps_buffered;
 			int queue;
 			int load;
+			u16 qos_control;
 			unsigned int in_scan:1;
 			/* frame is destined to interface currently processed
 			 * (including multicast frames) */
 			unsigned int ra_match:1;
+			unsigned int is_agg_frame:1;
 		} rx;
 	} u;
 };
@@ -158,6 +172,7 @@
 	unsigned int requeue:1;
 	unsigned int mgmt_iface:1;
 	unsigned int queue:4;
+	unsigned int ht_queue:1;
 };
 
 struct ieee80211_tx_stored_packet {
@@ -171,6 +186,19 @@
 	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);
 
@@ -211,10 +239,12 @@
 	enum {
 		IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
 		IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
-		IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
+		IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED,
+		IEEE80211_CHANNEL_SWITCH
 	} 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;
@@ -222,6 +252,7 @@
 	u16 ap_capab, capab;
 	u8 *extra_ie; /* to be added to the end of AssocReq */
 	size_t extra_ie_len;
+	u8 nick[IW_ESSID_MAX_SIZE];
 
 	/* The last AssocReq/Resp IEs */
 	u8 *assocreq_ies, *assocresp_ies;
@@ -235,10 +266,10 @@
 	unsigned int authenticated:1;
 	unsigned int associated:1;
 	unsigned int probereq_poll:1;
-	unsigned int use_protection:1;
 	unsigned int create_ibss:1;
 	unsigned int mixed_cell:1;
 	unsigned int wmm_enabled:1;
+	unsigned int ht_enabled:1;
 	unsigned int auto_ssid_sel:1;
 	unsigned int auto_bssid_sel:1;
 	unsigned int auto_channel_sel:1;
@@ -262,7 +293,22 @@
 	struct sk_buff *probe_resp; /* ProbeResp template for IBSS */
 	u32 supp_rates_bits;
 
+	u32 last_rate; /* last tx data rate value. management and multi cast frame
+			* wont be used. */
+
 	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];
+#ifdef CONFIG_MAC80211_DEBUGFS
+	struct ieee80211_elem_tspec tspec;
+	u8 dls_mac[ETH_ALEN];
+#endif
+	struct ieee80211_channel *switch_channel;
 };
 
 
@@ -278,6 +324,7 @@
 	int mc_count;
 	unsigned int allmulti:1;
 	unsigned int promisc:1;
+	unsigned int use_protection:1; /* CTS protect ERP frames */
 
 	struct net_device_stats stats;
 	int drop_unencrypted;
@@ -330,6 +377,39 @@
 			struct dentry *auth_alg;
 			struct dentry *auth_transaction;
 			struct dentry *flags;
+			struct dentry *qos_dir;
+			struct {
+				struct dentry *addts_11e;
+				struct dentry *addts_wmm;
+				struct dentry *delts_11e;
+				struct dentry *delts_wmm;
+				struct dentry *dls_mac;
+				struct dentry *dls_op;
+			} qos;
+			struct dentry *tsinfo_dir;
+			struct {
+				struct dentry *tsid;
+				struct dentry *direction;
+				struct dentry *up;
+			} tsinfo;
+			struct dentry *tspec_dir;
+			struct {
+				struct dentry *nominal_msdu_size;
+				struct dentry *max_msdu_size;
+				struct dentry *min_service_interval;
+				struct dentry *max_service_interval;
+				struct dentry *inactivity_interval;
+				struct dentry *suspension_interval;
+				struct dentry *service_start_time;
+				struct dentry *min_data_rate;
+				struct dentry *mean_data_rate;
+				struct dentry *peak_data_rate;
+				struct dentry *burst_size;
+				struct dentry *delay_bound;
+				struct dentry *min_phy_rate;
+				struct dentry *surplus_band_allow;
+				struct dentry *medium_time;
+			} tspec;
 		} sta;
 		struct {
 			struct dentry *channel_use;
@@ -392,6 +472,7 @@
 	int monitors;
 	struct iw_statistics wstats;
 	u8 wstats_flags;
+	int tx_headroom; /* required headroom for hardware/radiotap */
 
 	enum {
 		IEEE80211_DEV_UNINITIALIZED = 0,
@@ -416,8 +497,8 @@
 	struct sta_info *sta_hash[STA_HASH_SIZE];
 	struct timer_list sta_cleanup;
 
-	unsigned long state[NUM_TX_DATA_QUEUES];
-	struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES];
+	unsigned long state[NUM_TX_DATA_QUEUES_11N];
+	struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES_11N];
 	struct tasklet_struct tx_pending_tasklet;
 
 	int mc_count;	/* total count of multicast entries in all interfaces */
@@ -437,7 +518,6 @@
 	int *basic_rates[NUM_IEEE80211_MODES];
 
 	int rts_threshold;
-	int cts_protect_erp_frames;
 	int fragmentation_threshold;
 	int short_retry_limit; /* dot11ShortRetryLimit */
 	int long_retry_limit; /* dot11LongRetryLimit */
@@ -471,6 +551,8 @@
 	struct ieee80211_hw_mode *oper_hw_mode, *scan_hw_mode;
 	u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
 	size_t scan_ssid_len;
+#define IEEE80211_MAX_TXPOWER 50
+	u8 user_txpow;
 	struct list_head sta_bss_list;
 	struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE];
 	spinlock_t sta_bss_lock;
@@ -513,8 +595,6 @@
 		STA_ANTENNA_SEL_SW_CTRL_DEBUG = 2
 	} sta_antenna_sel;
 
-	int rate_ctrl_num_up, rate_ctrl_num_down;
-
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
 	/* TX/RX handler statistics */
 	unsigned int tx_handlers_drop;
@@ -629,6 +709,11 @@
 #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)
 {
@@ -644,6 +729,7 @@
 enum ieee80211_link_state_t {
 	IEEE80211_LINK_STATE_XOFF = 0,
 	IEEE80211_LINK_STATE_PENDING,
+	IEEE80211_LINK_STATE_AGGREGATED,
 };
 
 struct sta_attribute {
@@ -719,6 +805,8 @@
 			     struct ieee80211_hw_mode *mode);
 void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx);
 int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
+int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
+int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
 void ieee80211_if_setup(struct net_device *dev);
 void ieee80211_if_mgmt_setup(struct net_device *dev);
 int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
@@ -753,11 +841,11 @@
 /* ieee80211_ioctl.c */
 int ieee80211_set_compression(struct ieee80211_local *local,
 			      struct net_device *dev, struct sta_info *sta);
-int ieee80211_init_client(struct net_device *dev);
 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 +866,44 @@
 					 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_addba_request(struct net_device *dev, u8 *da, u16 tid,
+					u8 dialog_token, u16 start_seq_num,
+					u16 agg_size,u16 timeout);
+void ieee80211_send_delba(struct net_device *dev, u8 *da, u16 tid,
+				u16 initiator, u16 reason_code);
+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, u16 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);
+void ieee80211_sta_stop_rx_BA_session(struct net_device *dev,u8 *da,
+				      u16 tid, u16 initiator, u16 reason);
+ieee80211_txrx_result ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+	struct ieee80211_txrx_data *rx, struct tid_ht_agg_info_rx *tid_rx_info,
+	struct sk_buff *skb, u16 mpdu_seq_num, int bar_req);
+
+
+void ieee80211_rx_mgmt_action(struct net_device *dev,
+			struct ieee80211_if_sta *ifsta,
+                        struct ieee80211_mgmt *mgmt,
+                        size_t len);
 
 /* ieee80211_iface.c */
 int ieee80211_if_add(struct net_device *dev, const char *name,
@@ -792,6 +918,10 @@
 int ieee80211_if_add_mgmt(struct ieee80211_local *local);
 void ieee80211_if_del_mgmt(struct ieee80211_local *local);
 
+/* regdomain.c */
+void ieee80211_regdomain_init(void);
+void ieee80211_set_default_regdomain(struct ieee80211_hw_mode *mode);
+
 /* for wiphy privid */
 extern void *mac80211_wiphy_privid;
 
diff -ruN linux-2.6.22/net/mac80211/ieee80211_iface.c linux-2.6.22-mac80211/net/mac80211/ieee80211_iface.c
--- linux-2.6.22/net/mac80211/ieee80211_iface.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/ieee80211_iface.c	2007-08-23 20:59:59.000000000 +0200
@@ -157,6 +157,8 @@
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	int oldtype = sdata->type;
 
+	dev->hard_start_xmit = ieee80211_subif_start_xmit;
+
 	sdata->type = type;
 	switch (type) {
 	case IEEE80211_IF_TYPE_WDS:
@@ -182,20 +184,38 @@
 			    (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;
 		ifsta->create_ibss = 1;
 		ifsta->wmm_enabled = 1;
+		ifsta->ht_enabled = 1;
 		ifsta->auto_channel_sel = 1;
 		ifsta->auto_bssid_sel = 1;
 
+		/* Initialize non-AP QSTA QoS Params */
+		ifsta->dot11EDCAAveragingPeriod = 5;
+		ifsta->MPDUExchangeTime = 0;
+#ifdef CONFIG_MAC80211_DEBUGFS
+		ifsta->tspec.nominal_msdu_size = cpu_to_le16(200),
+		ifsta->tspec.inactivity_interval = cpu_to_le32(40),
+		ifsta->tspec.mean_data_rate = cpu_to_le32(40000),
+		ifsta->tspec.min_phy_rate = cpu_to_le32(6000000),
+		ifsta->tspec.surplus_band_allow = cpu_to_le16(8192),
+		ifsta->tspec.medium_time = cpu_to_le16(30),
+#endif
+
 		msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev);
 		sdata->bss = &msdata->u.ap;
 		break;
 	}
 	case IEEE80211_IF_TYPE_MNTR:
 		dev->type = ARPHRD_IEEE80211_RADIOTAP;
+		dev->hard_start_xmit = ieee80211_monitor_start_xmit;
 		break;
 	default:
 		printk(KERN_WARNING "%s: %s: Unknown interface type 0x%x",
diff -ruN linux-2.6.22/net/mac80211/ieee80211_ioctl.c linux-2.6.22-mac80211/net/mac80211/ieee80211_ioctl.c
--- linux-2.6.22/net/mac80211/ieee80211_ioctl.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/ieee80211_ioctl.c	2007-08-23 20:59:59.000000000 +0200
@@ -27,20 +27,6 @@
 #include "aes_ccm.h"
 #include "debugfs_key.h"
 
-static int ieee80211_regdom = 0x10; /* FCC */
-module_param(ieee80211_regdom, int, 0444);
-MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain; 64=MKK");
-
-/*
- * If firmware is upgraded by the vendor, additional channels can be used based
- * on the new Japanese regulatory rules. This is indicated by setting
- * ieee80211_japan_5ghz module parameter to one when loading the 80211 kernel
- * module.
- */
-static int ieee80211_japan_5ghz /* = 0 */;
-module_param(ieee80211_japan_5ghz, int, 0444);
-MODULE_PARM_DESC(ieee80211_japan_5ghz, "Vendor-updated firmware for 5 GHz");
-
 static void ieee80211_set_hw_encryption(struct net_device *dev,
 					struct sta_info *sta, u8 addr[ETH_ALEN],
 					struct ieee80211_key *key)
@@ -345,6 +331,8 @@
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct iw_range *range = (struct iw_range *) extra;
+	struct ieee80211_hw_mode *mode = NULL;
+	int c = 0;
 
 	data->length = sizeof(struct iw_range);
 	memset(range, 0, sizeof(struct iw_range));
@@ -378,130 +366,34 @@
 	range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
 			  IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
 
-	IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
-	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY);
-	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
-	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
-
-	return 0;
-}
-
-
-struct ieee80211_channel_range {
-	short start_freq;
-	short end_freq;
-	unsigned char power_level;
-	unsigned char antenna_max;
-};
-
-static const struct ieee80211_channel_range ieee80211_fcc_channels[] = {
-	{ 2412, 2462, 27, 6 } /* IEEE 802.11b/g, channels 1..11 */,
-	{ 5180, 5240, 17, 6 } /* IEEE 802.11a, channels 36..48 */,
-	{ 5260, 5320, 23, 6 } /* IEEE 802.11a, channels 52..64 */,
-	{ 5745, 5825, 30, 6 } /* IEEE 802.11a, channels 149..165, outdoor */,
-	{ 0 }
-};
-
-static const struct ieee80211_channel_range ieee80211_mkk_channels[] = {
-	{ 2412, 2472, 20, 6 } /* IEEE 802.11b/g, channels 1..13 */,
-	{ 5170, 5240, 20, 6 } /* IEEE 802.11a, channels 34..48 */,
-	{ 5260, 5320, 20, 6 } /* IEEE 802.11a, channels 52..64 */,
-	{ 0 }
-};
-
-
-static const struct ieee80211_channel_range *channel_range =
-	ieee80211_fcc_channels;
-
-
-static void ieee80211_unmask_channel(struct net_device *dev, int mode,
-				     struct ieee80211_channel *chan)
-{
-	int i;
-
-	chan->flag = 0;
-
-	if (ieee80211_regdom == 64 &&
-	    (mode == MODE_ATHEROS_TURBO || mode == MODE_ATHEROS_TURBOG)) {
-		/* Do not allow Turbo modes in Japan. */
-		return;
-	}
-
-	for (i = 0; channel_range[i].start_freq; i++) {
-		const struct ieee80211_channel_range *r = &channel_range[i];
-		if (r->start_freq <= chan->freq && r->end_freq >= chan->freq) {
-			if (ieee80211_regdom == 64 && !ieee80211_japan_5ghz &&
-			    chan->freq >= 5260 && chan->freq <= 5320) {
-				/*
-				 * Skip new channels in Japan since the
-				 * firmware was not marked having been upgraded
-				 * by the vendor.
-				 */
-				continue;
-			}
+	list_for_each_entry(mode, &local->modes_list, list) {
+		int i = 0;
 
-			if (ieee80211_regdom == 0x10 &&
-			    (chan->freq == 5190 || chan->freq == 5210 ||
-			     chan->freq == 5230)) {
-				    /* Skip MKK channels when in FCC domain. */
-				    continue;
-			}
+		if (!(local->enabled_modes & (1 << mode->mode)) ||
+		    (local->hw_modes & local->enabled_modes &
+		     (1 << MODE_IEEE80211G) && mode->mode == MODE_IEEE80211B))
+			continue;
 
-			chan->flag |= IEEE80211_CHAN_W_SCAN |
-				IEEE80211_CHAN_W_ACTIVE_SCAN |
-				IEEE80211_CHAN_W_IBSS;
-			chan->power_level = r->power_level;
-			chan->antenna_max = r->antenna_max;
-
-			if (ieee80211_regdom == 64 &&
-			    (chan->freq == 5170 || chan->freq == 5190 ||
-			     chan->freq == 5210 || chan->freq == 5230)) {
-				/*
-				 * New regulatory rules in Japan have backwards
-				 * compatibility with old channels in 5.15-5.25
-				 * GHz band, but the station is not allowed to
-				 * use active scan on these old channels.
-				 */
-				chan->flag &= ~IEEE80211_CHAN_W_ACTIVE_SCAN;
-			}
+		while (i < mode->num_channels && c < IW_MAX_FREQUENCIES) {
+			struct ieee80211_channel *chan = &mode->channels[i];
 
-			if (ieee80211_regdom == 64 &&
-			    (chan->freq == 5260 || chan->freq == 5280 ||
-			     chan->freq == 5300 || chan->freq == 5320)) {
-				/*
-				 * IBSS is not allowed on 5.25-5.35 GHz band
-				 * due to radar detection requirements.
-				 */
-				chan->flag &= ~IEEE80211_CHAN_W_IBSS;
+			if (chan->flag & IEEE80211_CHAN_W_SCAN) {
+				range->freq[c].i = chan->chan;
+				range->freq[c].m = chan->freq * 100000;
+				range->freq[c].e = 1;
+				c++;
 			}
-
-			break;
-		}
-	}
-}
-
-
-static int ieee80211_unmask_channels(struct net_device *dev)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_hw_mode *mode;
-	int c;
-
-	list_for_each_entry(mode, &local->modes_list, list) {
-		for (c = 0; c < mode->num_channels; c++) {
-			ieee80211_unmask_channel(dev, mode->mode,
-						 &mode->channels[c]);
+			i++;
 		}
 	}
-	return 0;
-}
+	range->num_channels = c;
+	range->num_frequency = c;
 
+	IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
+	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY);
+	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
+	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
 
-int ieee80211_init_client(struct net_device *dev)
-{
-	if (ieee80211_regdom == 0x40)
-		channel_range = ieee80211_mkk_channels;
-	ieee80211_unmask_channels(dev);
 	return 0;
 }
 
@@ -805,17 +697,24 @@
 	if (!netif_running(dev))
 		return -ENETDOWN;
 
-	if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
-		if (sdata->type == IEEE80211_IF_TYPE_STA ||
-		    sdata->type == IEEE80211_IF_TYPE_IBSS) {
+	switch (sdata->type) {
+	case IEEE80211_IF_TYPE_STA:
+	case IEEE80211_IF_TYPE_IBSS:
+		if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
 			ssid = sdata->u.sta.ssid;
 			ssid_len = sdata->u.sta.ssid_len;
-		} else if (sdata->type == IEEE80211_IF_TYPE_AP) {
+		}
+		break;
+	case IEEE80211_IF_TYPE_AP:
+		if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
 			ssid = sdata->u.ap.ssid;
 			ssid_len = sdata->u.ap.ssid_len;
-		} else
-			return -EINVAL;
+		}
+		break;
+	default:
+		return -EOPNOTSUPP;
 	}
+
 	return ieee80211_sta_req_scan(dev, ssid, ssid_len);
 }
 
@@ -838,6 +737,67 @@
 }
 
 
+static int ieee80211_ioctl_siwrate(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *rate, char *extra)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_hw_mode *mode;
+	int i;
+	u32 target_rate = rate->value / 100000;
+	struct ieee80211_sub_if_data *sdata;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	if (!sdata->bss)
+		return -ENODEV;
+	mode = local->oper_hw_mode;
+	/* target_rate = -1, rate->fixed = 0 means auto only, so use all rates
+	 * target_rate = X, rate->fixed = 1 means only rate X
+	 * target_rate = X, rate->fixed = 0 means all rates <= X */
+	sdata->bss->max_ratectrl_rateidx = -1;
+	sdata->bss->force_unicast_rateidx = -1;
+	if (rate->value < 0)
+		return 0;
+	for (i=0; i< mode->num_rates; i++) {
+		struct ieee80211_rate *rates = &mode->rates[i];
+		int this_rate = rates->rate;
+
+		if (mode->mode == MODE_ATHEROS_TURBO ||
+		    mode->mode == MODE_ATHEROS_TURBOG)
+			this_rate *= 2;
+		if (target_rate == this_rate) {
+			sdata->bss->max_ratectrl_rateidx = i;
+			if (rate->fixed)
+				sdata->bss->force_unicast_rateidx = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+static int ieee80211_ioctl_giwrate(struct net_device *dev,
+				  struct iw_request_info *info,
+				  struct iw_param *rate, char *extra)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct sta_info *sta;
+	struct ieee80211_sub_if_data *sdata;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	if (sdata->type == IEEE80211_IF_TYPE_STA)
+		sta = sta_info_get(local, sdata->u.sta.bssid);
+	else
+		return -EOPNOTSUPP;
+	if (!sta)
+		return -ENODEV;
+	if (sta->txrate < local->oper_hw_mode->num_rates)
+		rate->value = local->oper_hw_mode->rates[sta->txrate].rate * 100000;
+	else
+		rate->value = 0;
+	sta_info_put(sta);
+	return 0;
+}
+
 static int ieee80211_ioctl_siwrts(struct net_device *dev,
 				  struct iw_request_info *info,
 				  struct iw_param *rts, char *extra)
@@ -915,6 +875,125 @@
 	return 0;
 }
 
+static int ieee80211_ioctl_giwtxpow(struct net_device *dev,
+				    struct iw_request_info *info,
+				    union iwreq_data *wrqu,
+				    char *extra)
+{
+        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+        struct ieee80211_conf *conf = &local->hw.conf;
+
+	wrqu->txpower.flags = IW_TXPOW_DBM;
+	wrqu->txpower.fixed = 1;
+	wrqu->txpower.disabled = (conf->radio_enabled) ? 0 : 1;
+	wrqu->txpower.value = conf->power_level;
+	return 0;
+}
+
+static int ieee80211_ioctl_siwtxpow(struct net_device *dev,
+				    struct iw_request_info *info,
+				    union iwreq_data *wrqu,
+				    char *extra)
+{
+	int rc = 0;
+        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+
+
+	if (wrqu->txpower.flags != IW_TXPOW_DBM)
+		rc = -EINVAL;
+	else if (!wrqu->txpower.disabled) {
+		if (wrqu->txpower.value < 0)
+			return -EINVAL;
+		local->user_txpow = wrqu->txpower.value;
+	}
+
+
+	ieee80211_ioctl_set_radio_enabled(dev, !wrqu->txpower.disabled);
+	return rc;
+}
+
+static int ieee80211_ioctl_siwpower(struct net_device *dev,
+				    struct iw_request_info *info,
+				    union iwreq_data *wrqu,
+				    char *extra)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_conf *conf = &local->hw.conf;
+
+	if (wrqu->power.disabled) {
+		conf->power_management_enable = 0;
+		if (ieee80211_hw_config(local))
+			return -EINVAL;
+		return 0;
+	}
+
+	if (wrqu->power.flags & IW_POWER_TYPE)
+		return -EINVAL;
+
+	switch (wrqu->power.flags & IW_POWER_MODE) {
+	case IW_POWER_ON:       /* If not specified */
+	case IW_POWER_MODE:     /* If set all mask */
+	case IW_POWER_ALL_R:    /* If explicitely state all */
+		break;
+	default:                /* Otherwise we don't support it */
+		return -EINVAL;
+	}
+
+	conf->power_management_enable = 1;
+
+	if (ieee80211_hw_config(local))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ieee80211_ioctl_giwpower(struct net_device *dev,
+				    struct iw_request_info *info,
+				    union iwreq_data *wrqu,
+				    char *extra)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_conf *conf = &local->hw.conf;
+
+	if (!conf->power_management_enable)
+		wrqu->power.disabled = 1;
+	else
+		wrqu->power.disabled = 0;
+	return 0;
+}
+
+static int ieee80211_ioctl_siwnick(struct net_device *dev,
+				   struct iw_request_info *info,
+				   union iwreq_data *wrqu, char *extra)
+{
+	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_if_sta *ifsta;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	ifsta = &sdata->u.sta;
+	if (wrqu->data.length >= IW_ESSID_MAX_SIZE)
+		return -E2BIG;
+
+	memset(ifsta->nick, 0, sizeof(ifsta->nick));
+	memcpy(ifsta->nick, extra, wrqu->data.length);
+	return 0;
+}
+
+static int ieee80211_ioctl_giwnick(struct net_device *dev,
+				   struct iw_request_info *info,
+				   union iwreq_data *wrqu, char *extra)
+{
+	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_if_sta *ifsta;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	ifsta = &sdata->u.sta;
+
+	wrqu->data.length = strlen(ifsta->nick) + 1;
+	memcpy(extra, ifsta->nick, wrqu->data.length);
+	wrqu->data.flags = 1;   /* active */
+	return 0;
+}
 
 static int ieee80211_ioctl_siwretry(struct net_device *dev,
 				    struct iw_request_info *info,
@@ -970,118 +1049,6 @@
 	return 0;
 }
 
-static int ieee80211_ioctl_clear_keys(struct net_device *dev)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_key_conf key;
-	int i;
-	u8 addr[ETH_ALEN];
-	struct ieee80211_key_conf *keyconf;
-	struct ieee80211_sub_if_data *sdata;
-	struct sta_info *sta;
-
-	memset(addr, 0xff, ETH_ALEN);
-	read_lock(&local->sub_if_lock);
-	list_for_each_entry(sdata, &local->sub_if_list, list) {
-		for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
-			keyconf = NULL;
-			if (sdata->keys[i] &&
-			    !sdata->keys[i]->force_sw_encrypt &&
-			    local->ops->set_key &&
-			    (keyconf = ieee80211_key_data2conf(local,
-							       sdata->keys[i])))
-				local->ops->set_key(local_to_hw(local),
-						   DISABLE_KEY, addr,
-						   keyconf, 0);
-			kfree(keyconf);
-			ieee80211_key_free(sdata->keys[i]);
-			sdata->keys[i] = NULL;
-		}
-		sdata->default_key = NULL;
-	}
-	read_unlock(&local->sub_if_lock);
-
-	spin_lock_bh(&local->sta_lock);
-	list_for_each_entry(sta, &local->sta_list, list) {
-		keyconf = NULL;
-		if (sta->key && !sta->key->force_sw_encrypt &&
-		    local->ops->set_key &&
-		    (keyconf = ieee80211_key_data2conf(local, sta->key)))
-			local->ops->set_key(local_to_hw(local), DISABLE_KEY,
-					   sta->addr, keyconf, sta->aid);
-		kfree(keyconf);
-		ieee80211_key_free(sta->key);
-		sta->key = NULL;
-	}
-	spin_unlock_bh(&local->sta_lock);
-
-	memset(&key, 0, sizeof(key));
-	if (local->ops->set_key &&
-		    local->ops->set_key(local_to_hw(local), REMOVE_ALL_KEYS,
-				       NULL, &key, 0))
-		printk(KERN_DEBUG "%s: failed to remove hwaccel keys\n",
-		       dev->name);
-
-	return 0;
-}
-
-
-static int
-ieee80211_ioctl_force_unicast_rate(struct net_device *dev,
-				   struct ieee80211_sub_if_data *sdata,
-				   int rate)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_hw_mode *mode;
-	int i;
-
-	if (sdata->type != IEEE80211_IF_TYPE_AP)
-		return -ENOENT;
-
-	if (rate == 0) {
-		sdata->u.ap.force_unicast_rateidx = -1;
-		return 0;
-	}
-
-	mode = local->oper_hw_mode;
-	for (i = 0; i < mode->num_rates; i++) {
-		if (mode->rates[i].rate == rate) {
-			sdata->u.ap.force_unicast_rateidx = i;
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-
-static int
-ieee80211_ioctl_max_ratectrl_rate(struct net_device *dev,
-				  struct ieee80211_sub_if_data *sdata,
-				  int rate)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_hw_mode *mode;
-	int i;
-
-	if (sdata->type != IEEE80211_IF_TYPE_AP)
-		return -ENOENT;
-
-	if (rate == 0) {
-		sdata->u.ap.max_ratectrl_rateidx = -1;
-		return 0;
-	}
-
-	mode = local->oper_hw_mode;
-	for (i = 0; i < mode->num_rates; i++) {
-		if (mode->rates[i].rate == rate) {
-			sdata->u.ap.max_ratectrl_rateidx = i;
-			return 0;
-		}
-	}
-	return -EINVAL;
-}
-
-
 static void ieee80211_key_enable_hwaccel(struct ieee80211_local *local,
 					 struct ieee80211_key *key)
 {
@@ -1205,24 +1172,11 @@
 			sdata->ieee802_1x = value;
 		break;
 
-	case PRISM2_PARAM_ANTSEL_TX:
-		local->hw.conf.antenna_sel_tx = value;
-		if (ieee80211_hw_config(local))
-			ret = -EINVAL;
-		break;
-
-	case PRISM2_PARAM_ANTSEL_RX:
-		local->hw.conf.antenna_sel_rx = value;
-		if (ieee80211_hw_config(local))
-			ret = -EINVAL;
-		break;
-
 	case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES:
-		local->cts_protect_erp_frames = value;
-		break;
-
-	case PRISM2_PARAM_DROP_UNENCRYPTED:
-		sdata->drop_unencrypted = value;
+		if (sdata->type != IEEE80211_IF_TYPE_AP)
+			ret = -ENOENT;
+		else
+			sdata->use_protection = value;
 		break;
 
 	case PRISM2_PARAM_PREAMBLE:
@@ -1251,10 +1205,6 @@
 		local->next_mode = value;
 		break;
 
-	case PRISM2_PARAM_CLEAR_KEYS:
-		ret = ieee80211_ioctl_clear_keys(dev);
-		break;
-
 	case PRISM2_PARAM_RADIO_ENABLED:
 		ret = ieee80211_ioctl_set_radio_enabled(dev, value);
 		break;
@@ -1269,22 +1219,6 @@
 		local->sta_antenna_sel = value;
 		break;
 
-	case PRISM2_PARAM_FORCE_UNICAST_RATE:
-		ret = ieee80211_ioctl_force_unicast_rate(dev, sdata, value);
-		break;
-
-	case PRISM2_PARAM_MAX_RATECTRL_RATE:
-		ret = ieee80211_ioctl_max_ratectrl_rate(dev, sdata, value);
-		break;
-
-	case PRISM2_PARAM_RATE_CTRL_NUM_UP:
-		local->rate_ctrl_num_up = value;
-		break;
-
-	case PRISM2_PARAM_RATE_CTRL_NUM_DOWN:
-		local->rate_ctrl_num_down = value;
-		break;
-
 	case PRISM2_PARAM_TX_POWER_REDUCTION:
 		if (value < 0)
 			ret = -EINVAL;
@@ -1364,20 +1298,8 @@
 		*param = sdata->ieee802_1x;
 		break;
 
-	case PRISM2_PARAM_ANTSEL_TX:
-		*param = local->hw.conf.antenna_sel_tx;
-		break;
-
-	case PRISM2_PARAM_ANTSEL_RX:
-		*param = local->hw.conf.antenna_sel_rx;
-		break;
-
 	case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES:
-		*param = local->cts_protect_erp_frames;
-		break;
-
-	case PRISM2_PARAM_DROP_UNENCRYPTED:
-		*param = sdata->drop_unencrypted;
+		*param = sdata->use_protection;
 		break;
 
 	case PRISM2_PARAM_PREAMBLE:
@@ -1403,14 +1325,6 @@
 		*param = local->sta_antenna_sel;
 		break;
 
-	case PRISM2_PARAM_RATE_CTRL_NUM_UP:
-		*param = local->rate_ctrl_num_up;
-		break;
-
-	case PRISM2_PARAM_RATE_CTRL_NUM_DOWN:
-		*param = local->rate_ctrl_num_down;
-		break;
-
 	case PRISM2_PARAM_TX_POWER_REDUCTION:
 		*param = local->hw.conf.tx_power_reduction;
 		break;
@@ -1774,24 +1688,24 @@
 	(iw_handler) ieee80211_ioctl_giwscan,		/* SIOCGIWSCAN */
 	(iw_handler) ieee80211_ioctl_siwessid,		/* SIOCSIWESSID */
 	(iw_handler) ieee80211_ioctl_giwessid,		/* SIOCGIWESSID */
-	(iw_handler) NULL,				/* SIOCSIWNICKN */
-	(iw_handler) NULL,				/* SIOCGIWNICKN */
+	(iw_handler) ieee80211_ioctl_siwnick,		/* SIOCSIWNICKN */
+	(iw_handler) ieee80211_ioctl_giwnick,		/* SIOCGIWNICKN */
 	(iw_handler) NULL,				/* -- hole -- */
 	(iw_handler) NULL,				/* -- hole -- */
-	(iw_handler) NULL,				/* SIOCSIWRATE */
-	(iw_handler) NULL,				/* SIOCGIWRATE */
+	(iw_handler) ieee80211_ioctl_siwrate,		/* SIOCSIWRATE */
+	(iw_handler) ieee80211_ioctl_giwrate,		/* SIOCGIWRATE */
 	(iw_handler) ieee80211_ioctl_siwrts,		/* SIOCSIWRTS */
 	(iw_handler) ieee80211_ioctl_giwrts,		/* SIOCGIWRTS */
 	(iw_handler) ieee80211_ioctl_siwfrag,		/* SIOCSIWFRAG */
 	(iw_handler) ieee80211_ioctl_giwfrag,		/* SIOCGIWFRAG */
-	(iw_handler) NULL,				/* SIOCSIWTXPOW */
-	(iw_handler) NULL,				/* SIOCGIWTXPOW */
+	(iw_handler) ieee80211_ioctl_siwtxpow,		/* SIOCSIWTXPOW */
+	(iw_handler) ieee80211_ioctl_giwtxpow,		/* SIOCGIWTXPOW */
 	(iw_handler) ieee80211_ioctl_siwretry,		/* SIOCSIWRETRY */
 	(iw_handler) ieee80211_ioctl_giwretry,		/* SIOCGIWRETRY */
 	(iw_handler) ieee80211_ioctl_siwencode,		/* SIOCSIWENCODE */
 	(iw_handler) ieee80211_ioctl_giwencode,		/* SIOCGIWENCODE */
-	(iw_handler) NULL,				/* SIOCSIWPOWER */
-	(iw_handler) NULL,				/* SIOCGIWPOWER */
+	(iw_handler) ieee80211_ioctl_siwpower,		/* SIOCSIWPOWER */
+	(iw_handler) ieee80211_ioctl_giwpower,		/* SIOCGIWPOWER */
 	(iw_handler) NULL,				/* -- hole -- */
 	(iw_handler) NULL,				/* -- hole -- */
 	(iw_handler) ieee80211_ioctl_siwgenie,		/* SIOCSIWGENIE */
diff -ruN linux-2.6.22/net/mac80211/ieee80211_rate.c linux-2.6.22-mac80211/net/mac80211/ieee80211_rate.c
--- linux-2.6.22/net/mac80211/ieee80211_rate.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/ieee80211_rate.c	2007-08-23 20:59:59.000000000 +0200
@@ -24,11 +24,10 @@
 {
 	struct rate_control_alg *alg;
 
-	alg = kmalloc(sizeof(*alg), GFP_KERNEL);
+	alg = kzalloc(sizeof(*alg), GFP_KERNEL);
 	if (alg == NULL) {
 		return -ENOMEM;
 	}
-	memset(alg, 0, sizeof(*alg));
 	alg->ops = ops;
 
 	mutex_lock(&rate_ctrl_mutex);
diff -ruN linux-2.6.22/net/mac80211/ieee80211_sta.c linux-2.6.22-mac80211/net/mac80211/ieee80211_sta.c
--- linux-2.6.22/net/mac80211/ieee80211_sta.c	2007-08-20 19:24:28.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/ieee80211_sta.c	2007-08-23 20:59:59.000000000 +0200
@@ -33,6 +32,7 @@
 #include "ieee80211_i.h"
 #include "ieee80211_rate.h"
 #include "hostapd_ioctl.h"
+#include "wme.h"
 
 #define IEEE80211_AUTH_TIMEOUT (HZ / 5)
 #define IEEE80211_AUTH_MAX_TRIES 3
@@ -59,6 +59,15 @@
 
 #define ERP_INFO_USE_PROTECTION BIT(1)
 
+/* mgmt header + 1 byte action code */
+#define IEEE80211_MIN_ACTION_SIZE (24 + 1)
+#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
+#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
+#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
+#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000
+#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800
+
+
 static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst,
 				     u8 *ssid, size_t ssid_len);
 static struct ieee80211_sta_bss *
@@ -76,34 +85,43 @@
 
 /* Parsed Information Elements */
 struct ieee802_11_elems {
+	/* pointers to IEs */
 	u8 *ssid;
-	u8 ssid_len;
 	u8 *supp_rates;
-	u8 supp_rates_len;
 	u8 *fh_params;
-	u8 fh_params_len;
 	u8 *ds_params;
-	u8 ds_params_len;
 	u8 *cf_params;
-	u8 cf_params_len;
 	u8 *tim;
-	u8 tim_len;
 	u8 *ibss_params;
-	u8 ibss_params_len;
 	u8 *challenge;
-	u8 challenge_len;
 	u8 *wpa;
-	u8 wpa_len;
 	u8 *rsn;
-	u8 rsn_len;
 	u8 *erp_info;
-	u8 erp_info_len;
+	u8 *ht_cap_param;
+	u8 ht_cap_param_len;
+	u8 *ht_extra_param;
+	u8 ht_extra_param_len;
 	u8 *ext_supp_rates;
-	u8 ext_supp_rates_len;
 	u8 *wmm_info;
-	u8 wmm_info_len;
 	u8 *wmm_param;
+	u8 *tspec;
+
+	/* length of them, respectively */
+	u8 ssid_len;
+	u8 supp_rates_len;
+	u8 fh_params_len;
+	u8 ds_params_len;
+	u8 cf_params_len;
+	u8 tim_len;
+	u8 ibss_params_len;
+	u8 challenge_len;
+	u8 wpa_len;
+	u8 rsn_len;
+	u8 erp_info_len;
+	u8 ext_supp_rates_len;
+	u8 wmm_info_len;
 	u8 wmm_param_len;
+	u8 tspec_len;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -172,17 +190,34 @@
 			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 +234,22 @@
 			elems->ext_supp_rates = pos;
 			elems->ext_supp_rates_len = elen;
 			break;
+		case WLAN_EID_HT_CAPABILITY:
+			elems->ht_cap_param = pos;
+			elems->ht_cap_param_len = elen;
+			break;
+		case WLAN_EID_HT_EXTRA_INFO:
+			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 "
@@ -232,7 +283,6 @@
 	return cw - 1;
 }
 
-
 static void ieee80211_sta_wmm_params(struct net_device *dev,
 				     struct ieee80211_if_sta *ifsta,
 				     u8 *wmm_param, size_t wmm_param_len)
@@ -311,6 +361,25 @@
 }
 
 
+static void ieee80211_handle_erp_ie(struct net_device *dev, u8 erp_value)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+	int use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
+
+	if (use_protection != sdata->use_protection) {
+		if (net_ratelimit()) {
+			printk(KERN_DEBUG "%s: CTS protection %s (BSSID="
+			       MAC_FMT ")\n",
+			       dev->name,
+			       use_protection ? "enabled" : "disabled",
+			       MAC_ARG(ifsta->bssid));
+		}
+		sdata->use_protection = use_protection;
+	}
+}
+
+
 static void ieee80211_sta_send_associnfo(struct net_device *dev,
 					 struct ieee80211_if_sta *ifsta)
 {
@@ -366,6 +435,7 @@
 				     struct ieee80211_if_sta *ifsta, int assoc)
 {
 	union iwreq_data wrqu;
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 	if (ifsta->associated == assoc)
 		return;
@@ -374,9 +444,18 @@
 
 	if (assoc) {
 		struct ieee80211_sub_if_data *sdata;
+		struct ieee80211_sta_bss *bss;
 		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 		if (sdata->type != IEEE80211_IF_TYPE_STA)
 			return;
+
+		bss = ieee80211_rx_bss_get(dev, ifsta->bssid);
+		if (bss) {
+			if (bss->has_erp_value)
+				ieee80211_handle_erp_ie(dev, bss->erp_value);
+			ieee80211_rx_bss_put(dev, bss);
+		}
+
 		netif_carrier_on(dev);
 		ifsta->prev_bssid_set = 1;
 		memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
@@ -384,6 +463,7 @@
 		ieee80211_sta_send_associnfo(dev, ifsta);
 	} else {
 		netif_carrier_off(dev);
+		sdata->use_protection = 0;
 		memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
 	}
 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
@@ -494,6 +574,7 @@
 	u16 capab;
 	struct ieee80211_sta_bss *bss;
 	int wmm = 0;
+	int ht_enabled = 0;
 
 	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
 			    sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
@@ -517,6 +598,8 @@
 			capab |= WLAN_CAPABILITY_PRIVACY;
 		if (bss->wmm_ie) {
 			wmm = 1;
+
+			ht_enabled = 1;
 		}
 		ieee80211_rx_bss_put(dev, bss);
 	}
@@ -592,6 +675,16 @@
 		*pos++ = 0;
 	}
 
+	/* if low level driver supports 11n, fill in 11n IE */
+	if (ht_enabled && ifsta->ht_enabled && local->ops->get_ht_capab) {
+		pos = skb_put(skb, sizeof(struct ieee80211_ht_capability)+2);
+		*pos++ = WLAN_EID_HT_CAPABILITY;
+		*pos++ = sizeof(struct ieee80211_ht_capability);
+		memset(pos, 0, sizeof(struct ieee80211_ht_capability));
+		local->ops->get_ht_capab(local_to_hw(local),
+			(struct ieee80211_ht_capability *)pos);
+	}
+
 	kfree(ifsta->assocreq_ies);
 	ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
 	ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_ATOMIC);
@@ -660,6 +753,402 @@
 }
 
 
+static 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);
+	u16 medium_time = le16_to_cpu(tp->medium_time);
+	u8 index = ieee80211_ts_index(direction);
+
+	if (ifsta->ts_data[tsid][index].status == TS_STATUS_UNUSED) {
+		printk(KERN_DEBUG "%s: Trying 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));
+
+	IEEE80211_SET_TSINFO_TSID(tp->ts_info, tsid);
+	IEEE80211_SET_TSINFO_DIR(tp->ts_info, direction);
+	IEEE80211_SET_TSINFO_POLICY(tp->ts_info, WLAN_TSINFO_EDCA);
+	IEEE80211_SET_TSINFO_APSD(tp->ts_info, WLAN_TSINFO_PSB_LEGACY);
+	IEEE80211_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);
+	u16 medium_time = le16_to_cpu(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));
+
+	IEEE80211_SET_TSINFO_TSID(tspec->ts_info, tsid);
+	IEEE80211_SET_TSINFO_DIR(tspec->ts_info, direction);
+	IEEE80211_SET_TSINFO_POLICY(tspec->ts_info, WLAN_TSINFO_EDCA);
+	IEEE80211_SET_TSINFO_APSD(tspec->ts_info, WLAN_TSINFO_PSB_LEGACY);
+	IEEE80211_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, u16 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 = cpu_to_le16(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)
 {
@@ -1174,6 +1663,18 @@
 		return;
 	}
 
+	/* it probably doesn't, but if the frame includes an ERP value then
+	 * update our stored copy */
+	if (elems.erp_info && elems.erp_info_len >= 1) {
+		struct ieee80211_sta_bss *bss
+			= ieee80211_rx_bss_get(dev, ifsta->bssid);
+		if (bss) {
+			bss->erp_value = elems.erp_info[0];
+			bss->has_erp_value = 1;
+			ieee80211_rx_bss_put(dev, bss);
+		}
+	}
+
 	printk(KERN_DEBUG "%s: associated\n", dev->name);
 	ifsta->aid = aid;
 	ifsta->ap_capab = capab_info;
@@ -1198,9 +1699,16 @@
 		}
 		bss = ieee80211_rx_bss_get(dev, ifsta->bssid);
 		if (bss) {
+			/* Init signal values from beacon or probe response. */
 			sta->last_rssi = bss->rssi;
 			sta->last_signal = bss->signal;
 			sta->last_noise = bss->noise;
+
+			/* Init averaging filter accumulators to 16x values. */
+			sta->accum_rssi = bss->rssi << 4;
+			sta->accum_signal = bss->signal << 4;
+			sta->accum_noise = bss->noise << 4;
+
 			ieee80211_rx_bss_put(dev, bss);
 		}
 	}
@@ -1229,6 +1737,20 @@
 	}
 	sta->supp_rates = rates;
 
+	if (elems.ht_extra_param && elems.ht_cap_param && elems.wmm_param &&
+	    ifsta->ht_enabled && local->ops->conf_ht){
+		int rc;
+
+		rc = local->ops->conf_ht(local_to_hw(local),
+					 (struct ieee80211_ht_capability *)
+					 elems.ht_cap_param,
+					 (struct ieee80211_ht_additional_info *)
+					 elems.ht_extra_param);
+		if (!rc)
+			sta->flags |= WLAN_STA_HT;
+	}
+
+
 	rate_control_rate_init(sta, local);
 
 	if (elems.wmm_param && ifsta->wmm_enabled) {
@@ -1243,6 +1765,258 @@
 	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,
@@ -1282,10 +2056,9 @@
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_sta_bss *bss;
 
-	bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
+	bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
 	if (!bss)
 		return NULL;
-	memset(bss, 0, sizeof(*bss));
 	atomic_inc(&bss->users);
 	atomic_inc(&bss->users);
 	memcpy(bss->bssid, bssid, ETH_ALEN);
@@ -1324,6 +2097,7 @@
 	kfree(bss->wpa_ie);
 	kfree(bss->rsn_ie);
 	kfree(bss->wmm_ie);
+	kfree(bss->ht_ie);
 	kfree(bss);
 }
 
@@ -1496,6 +2270,12 @@
 		return;
 	}
 
+	/* save the ERP value so that it is available at association time */
+	if (elems.erp_info && elems.erp_info_len >= 1) {
+		bss->erp_value = elems.erp_info[0];
+		bss->has_erp_value = 1;
+	}
+
 	bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int);
 	bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
 	if (elems.ssid && elems.ssid_len <= IEEE80211_MAX_SSID_LEN) {
@@ -1570,6 +2350,23 @@
 		bss->wmm_ie_len = 0;
 	}
 
+	if (elems.ht_cap_param &&
+	    (!bss->ht_ie || bss->ht_ie_len != elems.ht_cap_param_len ||
+	     memcmp(bss->ht_ie, elems.ht_cap_param, elems.ht_cap_param_len))) {
+		if (bss->ht_ie)
+			kfree(bss->ht_ie);
+		bss->ht_ie = kmalloc(elems.ht_cap_param_len + 2, GFP_ATOMIC);
+		if (bss->ht_ie) {
+			memcpy(bss->ht_ie, elems.ht_cap_param - 2,
+			       elems.ht_cap_param_len + 2);
+			bss->ht_ie_len = elems.ht_cap_param_len + 2;
+		} else
+			bss->ht_ie_len = 0;
+	} else if (!elems.ht_cap_param && bss->ht_ie) {
+		kfree(bss->ht_ie);
+		bss->ht_ie = NULL;
+		bss->ht_ie_len = 0;
+	}
 
 	bss->hw_mode = rx_status->phymode;
 	bss->channel = channel;
@@ -1611,10 +2408,8 @@
 				     size_t len,
 				     struct ieee80211_rx_status *rx_status)
 {
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_if_sta *ifsta;
-	int use_protection;
 	size_t baselen;
 	struct ieee802_11_elems elems;
 
@@ -1634,27 +2429,12 @@
 	if (baselen > len)
 		return;
 
-	if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen,
-				   &elems) == ParseFailed)
-		return;
-
-	use_protection = 0;
-	if (elems.erp_info && elems.erp_info_len >= 1) {
-		use_protection =
-			(elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0;
-	}
-
-	if (use_protection != !!ifsta->use_protection) {
-		if (net_ratelimit()) {
-			printk(KERN_DEBUG "%s: CTS protection %s (BSSID="
-			       MAC_FMT ")\n",
-			       dev->name,
-			       use_protection ? "enabled" : "disabled",
-			       MAC_ARG(ifsta->bssid));
-		}
-		ifsta->use_protection = use_protection ? 1 : 0;
-		local->cts_protect_erp_frames = use_protection;
-	}
+	if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen,
+				   &elems) == ParseFailed)
+		return;
+
+	if (elems.erp_info && elems.erp_info_len >= 1)
+		ieee80211_handle_erp_ie(dev, elems.erp_info[0]);
 
 	if (elems.wmm_param && ifsta->wmm_enabled) {
 		ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param,
@@ -1732,6 +2512,806 @@
 	ieee80211_sta_tx(dev, skb, 0);
 }
 
+void ieee80211_send_addba_resp(struct net_device *dev, u8 *da, u16 tid,
+					u8 dialog_token, u16 status, u16 policy,
+					u16 buf_size,u16 timeout)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct sk_buff *skb;
+	struct ieee80211_mgmt *mgmt;
+	u16 capab=0;
+
+	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
+	if (!skb) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer "
+		       "for addba resp frame\n", dev->name);
+		return;
+	}
+
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+	memset(mgmt, 0, 24);
+	memcpy(mgmt->da, da, ETH_ALEN);
+	memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+	if(sdata->type == IEEE80211_IF_TYPE_AP)
+		memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
+	else
+		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.addba_resp));
+	mgmt->u.action.category = WLAN_CATEGORY_BACK;
+	mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
+	mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
+
+	capab |= (u16)(policy << 1);		/* bit 1 aggregation policy (1 - immediate 0 - delayed )*/
+	capab |= (u16)(tid << 2); 			/* bit 5:2 TID number */
+	capab |= (u16)(buf_size << 6);		/* bit 15:6 max size of aggergation */
+
+	mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
+	mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
+	mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
+
+	ieee80211_sta_tx(dev, skb, 0);
+
+	return;
+}
+
+static void sta_process_csa(struct ieee80211_local *local,
+			    struct ieee80211_if_sta *ifsta,
+			    struct ieee80211_mgmt *mgmt,
+			    size_t len)
+{
+	u8 *pos = (u8 *)&mgmt->u.action.u.chan_switch;
+	struct ieee80211_hw_mode *mode = local->oper_hw_mode;
+	int i;
+	unsigned short count;
+	short chan_num;
+	u8 csa_mode;
+
+	if (len < 24 + 7) {
+		printk(KERN_DEBUG "csa frame len:%zd too short\n", len);
+		return;
+	}
+
+	++pos; /* skip the action code */
+
+	if (*pos++ != WLAN_EID_CHANNEL_SWITCH ||
+	    *pos++ != 3) {
+		printk(KERN_DEBUG "not a csa info element\n");
+		return;
+	}
+
+	csa_mode = *pos++;
+	chan_num = *pos++;
+
+	ifsta->switch_channel = NULL;
+
+	for (i = 0; i < mode->num_channels; i++)
+		if (mode->channels[i].chan == chan_num &&
+		    mode->channels[i].flag & IEEE80211_CHAN_W_SCAN)
+			ifsta->switch_channel = &mode->channels[i];
+
+	if (!ifsta->switch_channel) {
+		printk(KERN_DEBUG "can't find switch channel: %d\n",
+			chan_num);
+		return;
+	}
+
+	if (csa_mode == 1)
+		ieee80211_stop_queues(&local->hw);
+
+	ifsta->state = IEEE80211_CHANNEL_SWITCH;
+	count = *pos;
+	mod_timer(&ifsta->timer,
+		jiffies + msecs_to_jiffies(count * local->hw.conf.beacon_int));
+}
+
+static void ieee80211_sta_process_addba_request(struct net_device *dev,
+						struct ieee80211_mgmt *mgmt,
+						size_t len)
+ {
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ 	struct ieee80211_hw *hw = &local->hw;
+ 	struct sta_info *sta;
+ 	struct ieee80211_ht_capability ht_capab;
+ 	u16 capab,tid,timeout,ba_policy,buf_size,start_seq_num;
+ 	u8 dialog_token;
+ 	int rc = -EAGAIN;
+
+ 	sta = sta_info_get(local,mgmt->sa);
+ 	if (!sta) {
+ 		return;
+ 	}
+
+ 	/* extract session parameters from addba request frame */
+ 	dialog_token = mgmt->u.action.u.addba_req.dialog_token;
+ 	capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+ 	ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
+ 	tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+ 	buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
+ 	timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
+ 	start_seq_num = le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
+
+  	/* TODO - debug version only, remove when not needed */
+ 	if (sta->ht_ba_mlme.tid_agg_info_rx[tid].buf_size == 0xFF) {
+ 		ieee80211_send_addba_resp(sta->dev,sta->addr,tid,dialog_token,
+ 								  WLAN_STATUS_REQUEST_DECLINED,1,buf_size,
+ 								  timeout);
+ 		printk (KERN_ERR "Rx is switched to off by user, "
+ 							"refuse addba request\n");
+ 		goto err_exit;
+ 	}
+ 	memset(&ht_capab, 0, sizeof(struct ieee80211_ht_capability));
+	if (local->ops->get_ht_capab) {
+		local->ops->get_ht_capab(hw,&ht_capab);
+	}
+
+ 	/* sanity check for incoming parameters:
+ 	 * check if driver can support the BA policy
+ 	 * and if buffer size does not exceeds max value */
+ 	if (((ba_policy != 1)
+		&& (!(ht_capab.capabilities_info & __constant_cpu_to_le16(0x0400))))
+ 		|| (buf_size > 0x40)) {
+ 		ieee80211_send_addba_resp(sta->dev,sta->addr,tid,dialog_token,
+ 								  WLAN_STATUS_INVALID_QOS_PARAM,1,buf_size,
+ 								  timeout);
+ 		printk (KERN_DEBUG "addBA request with bad params from " MAC_FMT
+ 							" arrived on tid %u \n",MAC_ARG(mgmt->sa),tid);
+ 		printk (KERN_DEBUG "BA policy = %d buffer size = %d\n",ba_policy,buf_size);
+ 		goto err_exit;
+ 	}
+
+	if(buf_size == 0)
+		buf_size = 0x40;
+
+ 	/* examine state machine */
+ 	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+ 	if (sta->ht_ba_mlme.tid_agg_info_rx[tid].state == HT_AGG_STATE_IDLE) {
+ 		sta->ht_ba_mlme.tid_agg_info_rx[tid].state = HT_AGG_STATE_START_BA;
+ 		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+ 		printk (KERN_DEBUG "addBA request from " MAC_FMT
+ 							" arrived on tid %u \n",MAC_ARG(mgmt->sa),tid);
+ 	} else {
+ 		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+		printk (KERN_DEBUG "unexpected addBA request from " MAC_FMT
+							" arrived on tid %u \n",MAC_ARG(mgmt->sa),tid);
+ 		goto err_send;
+ 	}
+
+ 	/* prepare reordering buffer */
+	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf =
+					kmalloc(buf_size * sizeof(struct sk_buf*), GFP_ATOMIC);
+	memset(sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf,0,
+			buf_size * sizeof(struct ieee80211_txre_data*));
+	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+	if (!sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf) {
+		printk (KERN_ERR "can not allocate reordering buffer to tid %u\n",tid);
+ 		goto err_reset;
+	}
+
+ 	/* configuring the hw to start Rx aggregation */
+ 	if (local->ops->ht_rx_agg_start)
+ 		rc = local->ops->ht_rx_agg_start(hw,sta->addr,tid,start_seq_num);
+
+ 	if(unlikely(rc)) {
+ 		printk (KERN_ERR "HW problem - can not start "
+ 							"Rx aggergation for tid %d\n",tid);
+		goto err_free;
+	}
+	/* change state and send addba resp */
+	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].state = HT_AGG_STATE_OPERATIONAL;
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].dialog_token = dialog_token;
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].start_seq_num = start_seq_num;
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].head_seq_num = start_seq_num;
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].buf_size = buf_size;
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].timeout = timeout;
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].stored_mpdu_num = 0;
+	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+	ieee80211_send_addba_resp(sta->dev,sta->addr,tid,dialog_token,
+						 	  WLAN_STATUS_SUCCESS,1,buf_size,timeout);
+
+	sta_info_put(sta);
+	return;
+
+err_free:
+	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+	if (sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf)
+		kfree(sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf);
+	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+err_reset:
+	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].state = HT_AGG_STATE_IDLE;
+	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+err_send:
+	ieee80211_send_addba_resp(sta->dev,sta->addr,tid,dialog_token,
+							  WLAN_STATUS_REQUEST_DECLINED,1,buf_size,
+ 							  timeout);
+err_exit:
+ 	sta_info_put(sta);
+ }
+
+
+void ieee80211_send_addba_request(struct net_device *dev, u8 *da, u16 tid,
+					u8 dialog_token, u16 start_seq_num,
+					u16 agg_size,u16 timeout)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+	struct sk_buff *skb;
+	struct ieee80211_mgmt *mgmt;
+	u16 capab=0;
+
+	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 +
+				sizeof(mgmt->u.action.u.addba_req));
+
+
+	if (!skb) {
+		printk(KERN_ERR "%s: failed to allocate buffer "
+				"for addba request frame\n", dev->name);
+		return;
+	}
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+	mgmt = (struct ieee80211_mgmt *) skb_put(skb,24);
+	memset(mgmt, 0, 24);
+	memcpy(mgmt->da, da, ETH_ALEN);
+	memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+	if(sdata->type == IEEE80211_IF_TYPE_AP)
+		memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
+	else
+		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.addba_req));
+
+	mgmt->u.action.category = WLAN_CATEGORY_BACK;
+	mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
+
+	mgmt->u.action.u.addba_req.dialog_token = dialog_token;
+	capab |= (u16)(1 << 1);				/* bit 1 aggregation policy (1 - immediate 0 - delayed )*/
+	capab |= (u16)(tid << 2); 			/* bit 5:2 TID number */
+	capab |= (u16)(agg_size << 6);		/* bit 15:6 max size of aggergation */
+
+	mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
+
+	mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout);
+	mgmt->u.action.u.addba_req.start_seq_num = cpu_to_le16(start_seq_num << 4);
+
+	ieee80211_sta_tx(dev, skb, 0);
+}
+
+
+static void ieee80211_sta_process_addba_resp(struct net_device *dev,
+						struct ieee80211_mgmt *mgmt,
+						size_t len)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_hw *hw = &local->hw;
+	struct sta_info *sta;
+	u16 capab=0;
+	u16 tid=0;
+
+	sta = sta_info_get(local,mgmt->sa);
+	if (!sta)
+		return;
+
+	capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
+	tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+
+	if ((sta->ht_ba_mlme.tid_agg_info_tx[tid].state != HT_AGG_STATE_ADDBA_REQUESTED) ||
+		(mgmt->u.action.u.addba_resp.dialog_token !=
+      		sta->ht_ba_mlme.tid_agg_info_tx[tid].dialog_token)) {
+		printk (KERN_DEBUG "unexpected addBA response on tid %d\n",tid);
+		sta_info_put(sta);
+		return;
+	} else {
+		del_timer_sync(&sta->ht_ba_mlme.tid_agg_info_tx[tid].addba_resp_timer);
+		printk (KERN_DEBUG "deleted the timer on tid %d \n",tid);
+	}
+
+	if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) == WLAN_STATUS_SUCCESS) {
+		spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_OPERATIONAL;
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		printk (KERN_DEBUG "recipient accepted aggregation on tid %d \n",tid);
+	} else {  /* recipient denied the addba request */
+		spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_RECIPIENT_REQ_STOP_BA;
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+
+		printk (KERN_DEBUG "recipient rejected aggregation on tid %d \n",tid);
+
+		/* configuring the hw to stop aggregation */
+		if (local->ops->ht_tx_agg_stop) {
+			int rc = local->ops->ht_tx_agg_stop(hw,sta->addr,tid,0 /* WLAN_BACK_RECIPIENT */);
+			if(rc)
+				printk (KERN_ERR "HW problem - can not stop aggergation "
+      					"for tid %d\n",tid);
+			/* remove the queue for this aggregation */
+      			ieee80211_ht_agg_queue_remove(local, sta, tid);
+		}
+		spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_IDLE;
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+	}
+	sta_info_put(sta);
+}
+
+void ieee80211_send_delba(struct net_device *dev, u8 *da, u16 tid,
+				u16 initiator, u16 reason_code)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+	struct sk_buff *skb;
+	struct ieee80211_mgmt *mgmt;
+	u16 params=0;
+
+	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 +
+							sizeof(mgmt->u.action.u.delba));
+
+	if (!skb) {
+		printk(KERN_ERR "%s: failed to allocate buffer "
+					"for delba frame\n", dev->name);
+		return;
+	}
+
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+	mgmt = (struct ieee80211_mgmt *) skb_put(skb,24);
+	memset(mgmt, 0, 24);
+	memcpy(mgmt->da, da, ETH_ALEN);
+	memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+	if(sdata->type == IEEE80211_IF_TYPE_AP)
+		memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
+	else
+		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.delba));
+
+	mgmt->u.action.category = WLAN_CATEGORY_BACK;
+	mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
+	params |= (u16)(initiator << 11); 		/* bit 11 initiator */
+	params |= (u16)(tid << 12); 			/* bit 15:12 TID number */
+
+	mgmt->u.action.u.delba.params = cpu_to_le16(params);
+	mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
+
+	ieee80211_sta_tx(dev, skb, 0);
+}
+
+void ieee80211_sta_stop_rx_BA_session(struct net_device *dev,u8 *da,
+				 u16 tid, u16 initiator, u16 reason)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_hw *hw = &local->hw;
+	struct sta_info *sta;
+	struct ieee80211_rx_status status;
+	int i;
+
+	sta = sta_info_get(local,da);
+	if (!sta)
+		return;
+
+	/* check if the TID is in opertional state */
+	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+ 	if (sta->ht_ba_mlme.tid_agg_info_rx[tid].state
+ 									!= HT_AGG_STATE_OPERATIONAL) {
+ 		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+ 		printk (KERN_ERR "rx BA session requested to stop on an "
+ 												"unactive tid %d\n",tid);
+ 		sta_info_put(sta);
+ 		return;
+ 	} else {
+	 	if(initiator == 1)
+			sta->ht_ba_mlme.tid_agg_info_rx[tid].state =
+								HT_AGG_STATE_INITIATOR_REQ_STOP_BA;
+		else
+			sta->ht_ba_mlme.tid_agg_info_rx[tid].state =
+								HT_AGG_STATE_RECIPIENT_REQ_STOP_BA;
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_rx);
+ 	}
+
+ 	/* stop HW Rx aggregation */
+	if (local->ops->ht_rx_agg_stop) {
+			int rc = local->ops->ht_rx_agg_stop(hw,da,tid,initiator);
+			if (rc)
+				printk (KERN_ERR "HW problem - can not stop rx aggergation "
+									 "for tid %d\n",tid);
+	}
+
+	/* shutdown timer has not expired */
+	if (initiator != 2)
+		del_timer_sync(&sta->ht_ba_mlme.tid_agg_info_rx[tid].session_timer);
+
+	/* check if this is a self generated aggregation halt */
+	if (initiator == 0 || initiator == 2)
+		ieee80211_send_delba(dev,da,tid,0/*WLAN_BACK_RECIPIENT*/,reason);
+
+	/* free the reordering buffer */
+	for (i=0; i < sta->ht_ba_mlme.tid_agg_info_rx[tid].buf_size; i++) {
+		if(sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf[i]) {
+			/* release the reordered frames to stack,
+			 * but drop them there */
+			memcpy(&status,
+					sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf[i]->cb,
+					sizeof(status));
+			status.ordered = TXRX_DROP;
+			__ieee80211_rx(hw,sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf[i],&status);
+			sta->ht_ba_mlme.tid_agg_info_rx[tid].stored_mpdu_num--;
+			sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf[i] = NULL;
+		}
+	}
+	kfree(sta->ht_ba_mlme.tid_agg_info_rx[tid].reordering_buf);
+
+	sta->ht_ba_mlme.tid_agg_info_rx[tid].state = HT_AGG_STATE_IDLE;
+	sta_info_put(sta);
+}
+
+static void ieee80211_sta_process_delba(struct net_device *dev, struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_hw *hw = &local->hw;
+	struct sta_info *sta;
+	u16 tid,params;
+	u16 initiator;
+
+	sta = sta_info_get(local,mgmt->sa);
+	if (!sta)
+		return;
+	params = le16_to_cpu(mgmt->u.action.u.delba.params);
+	tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
+	initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11;
+
+	printk (KERN_DEBUG "received delBA from recipient "MAC_FMT" on tid %d, reason=%d\n",
+		MAC_ARG(mgmt->sa),tid,mgmt->u.action.u.delba.reason_code);
+
+	if(initiator == 0 /*WLAN_BACK_RECIPIENT*/) {
+
+		spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_RECIPIENT_REQ_STOP_BA;
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+
+		del_timer_sync(&sta->ht_ba_mlme.tid_agg_info_tx[tid].addba_resp_timer);
+
+		if (local->ops->ht_tx_agg_stop) {
+			int rc = local->ops->ht_tx_agg_stop(hw,sta->addr,tid,0 /* WLAN_BACK_RECIPIENT */);
+			if (rc)
+				printk (KERN_ERR "HW problem - can not stop"
+									" aggergation for tid %d\n",tid);
+			/* remove the queue for this aggregation */
+			ieee80211_ht_agg_queue_remove(local, sta, tid);
+		}
+
+		spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_IDLE;
+		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+	} else { /*WLAN_BACK_INITIATOR*/
+		ieee80211_sta_stop_rx_BA_session(dev,sta->addr,tid,1,0);
+	}
+
+	sta_info_put(sta);
+}
+
+ /*
+  * After sending add BA request we activated a timer until add BA response
+  * will arrive from the recipient. if this timer expires
+  * sta_addba_resp_timer_expired will be executed
+  */
+ void sta_addba_resp_timer_expired(unsigned long data)
+ {
+ 	/* timer argument is the address of the tid entry in the
+	 * sta_info's timer_to_tid table.  From it we can determine the
+	 * tid as well as the sta_info location */
+	u16 tid = *(int *)data;
+ 	struct sta_info *temp_sta = container_of((void *)data,
+		struct sta_info, timer_to_tid[tid]);
+
+ 	struct ieee80211_local *local = temp_sta->local;
+ 	struct ieee80211_hw *hw = &local->hw;
+ 	struct sta_info *sta;
+
+ 	sta = sta_info_get(local, temp_sta->addr);
+ 	if (!sta)
+ 		return;
+
+ 	/* check if the TID waits for addBA response */
+ 	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+ 	if (sta->ht_ba_mlme.tid_agg_info_tx[tid].state == HT_AGG_STATE_ADDBA_REQUESTED)
+ 		sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_INITIATOR_REQ_STOP_BA;
+ 	else {
+ 		spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+ 		printk (KERN_DEBUG "timer expired on tid %d but we are not expecting addBA response there",tid);
+ 		goto timer_expired_exit;
+ 	}
+ 	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+ 	printk (KERN_DEBUG "addBA response timer expired on tid %d\n",tid);
+
+ 	if (local->ops->ht_tx_agg_stop) {
+ 		int rc = local->ops->ht_tx_agg_stop(hw,sta->addr,tid,1 /*WLAN_BACK_INITIATOR */);
+ 		if(rc)
+ 			printk (KERN_ERR "HW problem - can not stop aggergation for tid %d\n",tid);
+ 		/* remove the queue for this aggregation */
+		ieee80211_ht_agg_queue_remove(local, sta, tid);
+ 	}
+ 	/* send a delBA */
+ 	ieee80211_send_delba(sta->dev, sta->addr, tid, 1 /* WLAN_BACK_INITIATOR */, WLAN_REASON_QSTA_NOT_USE);
+ 	spin_lock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+ 	sta->ht_ba_mlme.tid_agg_info_tx[tid].state = HT_AGG_STATE_IDLE;
+ 	spin_unlock_bh(&sta->ht_ba_mlme.agg_data_lock_tx);
+
+ timer_expired_exit:
+ 	sta_info_put(sta);
+ }
+
+ /*
+  * After receiving BA request we activated a timer after each frame
+  * arrives from the originator. if this timer expires
+  * ieee80211_sta_stop_rx_BA_session will be executed
+  */
+ void sta_rx_agg_session_timer_expired(unsigned long data)
+ {
+ 	/* not an elegant detour, but there is no choice as the timer passes
+ 	* only one argument, and ieee80211_local is needed here */
+ 	int *ptid = (int *)data;
+ 	int *timer_to_id = ptid-*ptid;
+ 	struct sta_info *temp_sta = container_of(timer_to_id, struct sta_info,
+						 timer_to_tid[0]);
+ 	struct ieee80211_local *local = temp_sta->local;
+ 	struct sta_info *sta;
+ 	u16 tid = (u16)*ptid;
+
+ 	sta = sta_info_get(local, temp_sta->addr);
+ 	if (!sta)
+ 		return;
+
+	printk(KERN_DEBUG "rx session timer expired on tid %d\n",tid);
+
+	ieee80211_sta_stop_rx_BA_session(sta->dev,sta->addr,tid,2,
+									 WLAN_REASON_QSTA_TIMEOUT);
+
+	sta_info_put(sta);
+ }
+
+static const u16 seq_modulo = 0x1000;
+#define SEQ_MASK 0xfff
+
+static inline void handle_seq_wrap_around(u16 *sq1, u16 *sq2)
+{
+	if(abs((int)*sq2 - (int)*sq1) > seq_modulo / 2) {
+		if(*sq1 < *sq2)
+			*sq1 += seq_modulo;
+		else
+			*sq2 += seq_modulo;
+	}
+}
+
+inline static int seq_less(u16 sq1, u16 sq2)
+{
+	handle_seq_wrap_around(&sq1,&sq2);
+	return (sq1 < sq2);
+}
+
+inline static u16 seq_add(u16 sq1, u16 sq2)
+{
+	return (sq1 + sq2) & SEQ_MASK;
+}
+
+inline static u16 seq_sub(u16 sq1, u16 sq2)
+{
+	if (sq1 >= sq2)
+		return (sq1 - sq2) & SEQ_MASK;
+	else
+		return (sq1 + seq_modulo - sq2) & SEQ_MASK;
+}
+
+ieee80211_txrx_result
+ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, struct ieee80211_txrx_data *rx,
+								 struct tid_ht_agg_info_rx *tid_rx_info,
+								 struct sk_buff *skb, u16 mpdu_seq_num, int bar_req)
+{
+	struct ieee80211_rx_status status;
+	u16 head_seq_num,buf_size;
+	int index;
+
+	buf_size = tid_rx_info->buf_size;
+	head_seq_num = tid_rx_info->head_seq_num;
+
+	/* frame with out of date sequence number */
+	if (seq_less(mpdu_seq_num,head_seq_num))
+		return TXRX_DROP;
+
+	/* if frame sequence number exceeds our buffering window size or
+	 * block Ack Request arrived - release stored frames */
+	if ((!seq_less(mpdu_seq_num,head_seq_num+buf_size)) || (bar_req)) {
+		/* new head to the ordering buffer */
+		if (bar_req)
+			head_seq_num = mpdu_seq_num;
+		else
+			head_seq_num = seq_add(seq_sub(mpdu_seq_num,buf_size),1);
+		/* release stored frames up to new head to stack */
+		while (seq_less(tid_rx_info->head_seq_num,head_seq_num)) {
+			index = seq_sub(tid_rx_info->head_seq_num,
+							tid_rx_info->start_seq_num)
+							% tid_rx_info->buf_size;
+			if (tid_rx_info->reordering_buf[index]) {
+				/* release the reordered frames to stack */
+				memcpy(&status, tid_rx_info->reordering_buf[index]->cb,
+						sizeof(status));
+				status.ordered = TXRX_QUEUED;
+				__ieee80211_rx(hw, tid_rx_info->reordering_buf[index],&status);
+				tid_rx_info->stored_mpdu_num--;
+				tid_rx_info->reordering_buf[index] = NULL;
+			}
+			tid_rx_info->head_seq_num = seq_add(tid_rx_info->head_seq_num,1);
+		}
+		if(bar_req)
+			return TXRX_DROP;
+	}
+
+	/* now the new frame is always in the range of the reordering buffer window */
+	index = seq_sub(mpdu_seq_num,tid_rx_info->start_seq_num)
+				% tid_rx_info->buf_size;
+	/* check if we already stored this frame */
+	if (tid_rx_info->reordering_buf[index])
+		return TXRX_DROP;
+
+	/* if arrived mpdu is in the right order and nothing else stored
+	 * release it immediately */
+	if (mpdu_seq_num == tid_rx_info->head_seq_num &&
+			tid_rx_info->stored_mpdu_num == 0) {
+		tid_rx_info->head_seq_num = seq_add(tid_rx_info->head_seq_num,1);
+		return TXRX_CONTINUE;
+	}
+
+	/* put the frame in the reordering buffer */
+	tid_rx_info->reordering_buf[index] = skb;
+	tid_rx_info->stored_mpdu_num++;
+	/* release the buffer until next missing frame */
+	index = seq_sub(tid_rx_info->head_seq_num,tid_rx_info->start_seq_num)
+						% tid_rx_info->buf_size;
+	while(tid_rx_info->reordering_buf[index]) {
+		/* release the reordered frame back to stack */
+		memcpy(&status, tid_rx_info->reordering_buf[index]->cb, sizeof(status));
+		status.ordered = TXRX_QUEUED;
+		__ieee80211_rx(hw, tid_rx_info->reordering_buf[index],&status);
+		tid_rx_info->stored_mpdu_num--;
+		tid_rx_info->reordering_buf[index] = NULL;
+		tid_rx_info->head_seq_num = seq_add(tid_rx_info->head_seq_num,1);
+		index = seq_sub(tid_rx_info->head_seq_num,tid_rx_info->start_seq_num)
+						% tid_rx_info->buf_size;
+	}
+	return TXRX_QUEUED;
+ }
+
+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_SPECTRUM_MGMT:
+		switch (mgmt->u.action.u.chan_switch.action_code) {
+		case WLAN_ACTION_CSA:
+			sta_process_csa(local, ifsta, mgmt, len);
+			break;
+		default:
+			break;
+		}
+		break;
+	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:
+			if (len < (IEEE80211_MIN_ACTION_SIZE +
+				   sizeof(mgmt->u.action.u.addba_req)))
+				break;
+			ieee80211_sta_process_addba_request(dev,mgmt,len);
+			break;
+		case WLAN_ACTION_ADDBA_RESP:
+			if (len < (IEEE80211_MIN_ACTION_SIZE +
+				   sizeof(mgmt->u.action.u.addba_resp)))
+				break;
+			ieee80211_sta_process_addba_resp(dev,mgmt,len);
+			break;
+		case WLAN_ACTION_DELBA:
+			if (len < (IEEE80211_MIN_ACTION_SIZE +
+				   sizeof(mgmt->u.action.u.delba)))
+				break;
+			ieee80211_sta_process_delba(dev,mgmt,len);
+			break;
+		default:
+			printk(KERN_DEBUG "%s: recieved unsupported BACK \n", dev->name);
+			break;
+		}
+		break;
+
+	default:
+		break;
+	}
+}
 
 void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
 			   struct ieee80211_rx_status *rx_status)
@@ -1761,6 +3341,7 @@
 	case IEEE80211_STYPE_REASSOC_RESP:
 	case IEEE80211_STYPE_DEAUTH:
 	case IEEE80211_STYPE_DISASSOC:
+	case IEEE80211_STYPE_ACTION:
 		skb_queue_tail(&ifsta->skb_queue, skb);
 		queue_work(local->hw.workqueue, &ifsta->work);
 		return;
@@ -1818,6 +3399,9 @@
 	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);
@@ -1904,6 +3488,24 @@
 	ieee80211_sta_req_scan(dev, ifsta->ssid, ifsta->ssid_len);
 }
 
+static void ieee80211_sta_channel_switch(struct net_device *dev,
+					 struct ieee80211_if_sta *ifsta)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+
+	printk(KERN_DEBUG "%s: switch to new channel %d\n",
+		dev->name, ifsta->switch_channel->chan);
+
+	local->oper_channel = ifsta->switch_channel;
+	local->hw.conf.flags |= IEEE80211_CONF_CHANNEL_SWITCH;
+	ieee80211_hw_config(local);
+
+	local->hw.conf.flags &= ~IEEE80211_CONF_CHANNEL_SWITCH;
+	ieee80211_start_queues(&local->hw);
+
+	ifsta->state = IEEE80211_ASSOCIATED;
+	mod_timer(&ifsta->timer, jiffies + IEEE80211_MONITORING_INTERVAL);
+}
 
 void ieee80211_sta_timer(unsigned long data)
 {
@@ -1975,6 +3577,9 @@
 	case IEEE80211_IBSS_JOINED:
 		ieee80211_sta_merge_ibss(dev, ifsta);
 		break;
+	case IEEE80211_CHANNEL_SWITCH:
+		ieee80211_sta_channel_switch(dev, ifsta);
+		break;
 	default:
 		printk(KERN_DEBUG "ieee80211_sta_work: Unknown state %d\n",
 		       ifsta->state);
@@ -1991,6 +3596,43 @@
 }
 
 
+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)
 {
@@ -2121,16 +3760,20 @@
 		ieee80211_sta_set_bssid(dev, selected->bssid);
 		ieee80211_rx_bss_put(dev, selected);
 		ifsta->state = IEEE80211_AUTHENTICATE;
 		ieee80211_sta_reset_auth(dev, ifsta);
 		return 0;
 	} else {
 		if (ifsta->state != IEEE80211_AUTHENTICATE) {
-			ieee80211_sta_start_scan(dev, NULL, 0);
+			if (ifsta->auto_ssid_sel)
+				ieee80211_sta_start_scan(dev, NULL, 0);
+			else
+				ieee80211_sta_start_scan(dev, ifsta->ssid,
+							 ifsta->ssid_len);
 			ifsta->state = IEEE80211_AUTHENTICATE;
 			set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
 		} else
 			ifsta->state = IEEE80211_DISABLED;
 	}
 	return -1;
 }
 
@@ -2435,8 +4076,10 @@
 			interval = IEEE80211_SCAN_INTERVAL_SLOW;
 		}
 
-		ifsta->state = IEEE80211_IBSS_SEARCH;
-		mod_timer(&ifsta->timer, jiffies + interval);
+		if (ifsta->state != IEEE80211_DISABLED) {
+			ifsta->state = IEEE80211_IBSS_SEARCH;
+			mod_timer(&ifsta->timer, jiffies + interval);
+		}
 		return 0;
 	}
 
diff -ruN linux-2.6.22/net/mac80211/rc80211_simple.c linux-2.6.22-mac80211/net/mac80211/rc80211_simple.c
--- linux-2.6.22/net/mac80211/rc80211_simple.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/rc80211_simple.c	2007-08-23 20:59:59.000000000 +0200
@@ -187,9 +187,13 @@
 		}
 #endif
 
-		if (per_failed > local->rate_ctrl_num_down) {
+		/*
+		 * XXX: Make these configurable once we have an
+		 * interface to the rate control algorithms
+		 */
+		if (per_failed > RATE_CONTROL_NUM_DOWN) {
 			rate_control_rate_dec(local, sta);
-		} else if (per_failed < local->rate_ctrl_num_up) {
+		} else if (per_failed < RATE_CONTROL_NUM_UP) {
 			rate_control_rate_inc(local, sta);
 		}
 		srctrl->tx_avg_rate_sum += status->control.rate->rate;
@@ -283,14 +287,16 @@
 	int i;
 	sta->txrate = 0;
 	mode = local->oper_hw_mode;
-	/* TODO: what is a good starting rate for STA? About middle? Maybe not
-	 * the lowest or the highest rate.. Could consider using RSSI from
-	 * previous packets? Need to have IEEE 802.1X auth succeed immediately
-	 * after assoc.. */
+	/* TODO: This routine should consider using RSSI from previous packets
+	 * as we need to have IEEE 802.1X auth succeed immediately after assoc..
+	 * Until that method is implemented, we will use the lowest supported rate
+	 * as a workaround, */
 	for (i = 0; i < mode->num_rates; i++) {
 		if ((sta->supp_rates & BIT(i)) &&
-		    (mode->rates[i].flags & IEEE80211_RATE_SUPPORTED))
+		    (mode->rates[i].flags & IEEE80211_RATE_SUPPORTED)) {
 			sta->txrate = i;
+			break;
+		}
 	}
 }
 
diff -ruN linux-2.6.22/net/mac80211/regdomain.c linux-2.6.22-mac80211/net/mac80211/regdomain.c
--- linux-2.6.22/net/mac80211/regdomain.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-mac80211/net/mac80211/regdomain.c	2007-08-23 20:59:59.000000000 +0200
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * This regulatory domain control implementation is known to be incomplete
+ * and confusing. mac80211 regulatory domain control will be significantly
+ * reworked in the not-too-distant future.
+ *
+ * For now, drivers wishing to control which channels are and aren't available
+ * are advised as follows:
+ *  - set the IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED flag
+ *  - continue to include *ALL* possible channels in the modes registered
+ *    through ieee80211_register_hwmode()
+ *  - for each allowable ieee80211_channel structure registered in the above
+ *    call, set the flag member to some meaningful value such as
+ *    IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN |
+ *    IEEE80211_CHAN_W_IBSS.
+ *  - leave flag as 0 for non-allowable channels
+ *
+ * The usual implementation is for a driver to read a device EEPROM to
+ * determine which regulatory domain it should be operating under, then
+ * looking up the allowable channels in a driver-local table, then performing
+ * the above.
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+
+static int ieee80211_regdom = 0x10; /* FCC */
+module_param(ieee80211_regdom, int, 0444);
+MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain; 64=MKK");
+
+/*
+ * If firmware is upgraded by the vendor, additional channels can be used based
+ * on the new Japanese regulatory rules. This is indicated by setting
+ * ieee80211_japan_5ghz module parameter to one when loading the 80211 kernel
+ * module.
+ */
+static int ieee80211_japan_5ghz /* = 0 */;
+module_param(ieee80211_japan_5ghz, int, 0444);
+MODULE_PARM_DESC(ieee80211_japan_5ghz, "Vendor-updated firmware for 5 GHz");
+
+
+struct ieee80211_channel_range {
+	short start_freq;
+	short end_freq;
+	unsigned char power_level;
+	unsigned char antenna_max;
+};
+
+static const struct ieee80211_channel_range ieee80211_fcc_channels[] = {
+	{ 2412, 2462, 27, 6 } /* IEEE 802.11b/g, channels 1..11 */,
+	{ 5180, 5240, 17, 6 } /* IEEE 802.11a, channels 36..48 */,
+	{ 5260, 5320, 23, 6 } /* IEEE 802.11a, channels 52..64 */,
+	{ 5745, 5825, 30, 6 } /* IEEE 802.11a, channels 149..165, outdoor */,
+	{ 0 }
+};
+
+static const struct ieee80211_channel_range ieee80211_mkk_channels[] = {
+	{ 2412, 2472, 20, 6 } /* IEEE 802.11b/g, channels 1..13 */,
+	{ 5170, 5240, 20, 6 } /* IEEE 802.11a, channels 34..48 */,
+	{ 5260, 5320, 20, 6 } /* IEEE 802.11a, channels 52..64 */,
+	{ 0 }
+};
+
+
+static const struct ieee80211_channel_range *channel_range =
+	ieee80211_fcc_channels;
+
+
+static void ieee80211_unmask_channel(int mode, struct ieee80211_channel *chan)
+{
+	int i;
+
+	chan->flag = 0;
+
+	if (ieee80211_regdom == 64 &&
+	    (mode == MODE_ATHEROS_TURBO || mode == MODE_ATHEROS_TURBOG)) {
+		/* Do not allow Turbo modes in Japan. */
+		return;
+	}
+
+	for (i = 0; channel_range[i].start_freq; i++) {
+		const struct ieee80211_channel_range *r = &channel_range[i];
+		if (r->start_freq <= chan->freq && r->end_freq >= chan->freq) {
+			if (ieee80211_regdom == 64 && !ieee80211_japan_5ghz &&
+			    chan->freq >= 5260 && chan->freq <= 5320) {
+				/*
+				 * Skip new channels in Japan since the
+				 * firmware was not marked having been upgraded
+				 * by the vendor.
+				 */
+				continue;
+			}
+
+			if (ieee80211_regdom == 0x10 &&
+			    (chan->freq == 5190 || chan->freq == 5210 ||
+			     chan->freq == 5230)) {
+				    /* Skip MKK channels when in FCC domain. */
+				    continue;
+			}
+
+			chan->flag |= IEEE80211_CHAN_W_SCAN |
+				IEEE80211_CHAN_W_ACTIVE_SCAN |
+				IEEE80211_CHAN_W_IBSS;
+			chan->power_level = r->power_level;
+			chan->antenna_max = r->antenna_max;
+
+			if (ieee80211_regdom == 64 &&
+			    (chan->freq == 5170 || chan->freq == 5190 ||
+			     chan->freq == 5210 || chan->freq == 5230)) {
+				/*
+				 * New regulatory rules in Japan have backwards
+				 * compatibility with old channels in 5.15-5.25
+				 * GHz band, but the station is not allowed to
+				 * use active scan on these old channels.
+				 */
+				chan->flag &= ~IEEE80211_CHAN_W_ACTIVE_SCAN;
+			}
+
+			if (ieee80211_regdom == 64 &&
+			    (chan->freq == 5260 || chan->freq == 5280 ||
+			     chan->freq == 5300 || chan->freq == 5320)) {
+				/*
+				 * IBSS is not allowed on 5.25-5.35 GHz band
+				 * due to radar detection requirements.
+				 */
+				chan->flag &= ~IEEE80211_CHAN_W_IBSS;
+			}
+
+			break;
+		}
+	}
+}
+
+
+void ieee80211_set_default_regdomain(struct ieee80211_hw_mode *mode)
+{
+	int c;
+	for (c = 0; c < mode->num_channels; c++)
+		ieee80211_unmask_channel(mode->mode, &mode->channels[c]);
+}
+
+
+void ieee80211_regdomain_init(void)
+{
+	if (ieee80211_regdom == 0x40)
+		channel_range = ieee80211_mkk_channels;
+}
+
diff -ruN linux-2.6.22/net/mac80211/sta_info.c linux-2.6.22-mac80211/net/mac80211/sta_info.c
--- linux-2.6.22/net/mac80211/sta_info.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/sta_info.c	2007-08-23 20:59:59.000000000 +0200
@@ -22,6 +22,10 @@
 #include "debugfs_key.h"
 #include "debugfs_sta.h"
 
+/* Need to find a better place for this */
+extern void sta_addba_resp_timer_expired(unsigned long data);
+extern void sta_rx_agg_session_timer_expired(unsigned long data);
+
 /* Caller must hold local->sta_lock */
 static void sta_info_hash_add(struct ieee80211_local *local,
 			      struct sta_info *sta)
@@ -33,7 +37,7 @@
 
 /* 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 +45,19 @@
 	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 +86,28 @@
 }
 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;
@@ -108,6 +138,7 @@
 	struct sta_info *sta = container_of(kref, struct sta_info, kref);
 	struct ieee80211_local *local = sta->local;
 	struct sk_buff *skb;
+	int i;
 
 	/* free sta structure; it has already been removed from
 	 * hash table etc. external structures. Make sure that all
@@ -120,6 +151,12 @@
 	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
 		dev_kfree_skb_any(skb);
 	}
+
+	for (i=0; i< STA_TID_NUM; i++) {
+		del_timer_sync(&sta->ht_ba_mlme.tid_agg_info_tx[i].addba_resp_timer);
+		del_timer_sync(&sta->ht_ba_mlme.tid_agg_info_rx[i].session_timer);
+	}
+
 	rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
 	rate_control_put(sta->rate_ctrl);
 	if (sta->key)
@@ -139,6 +176,7 @@
 			       struct net_device *dev, u8 *addr, gfp_t gfp)
 {
 	struct sta_info *sta;
+	int i;
 
 	sta = kzalloc(sizeof(*sta), gfp);
 	if (!sta)
@@ -158,6 +196,24 @@
 	memcpy(sta->addr, addr, ETH_ALEN);
 	sta->local = local;
 	sta->dev = dev;
+	spin_lock_init(&sta->ht_ba_mlme.agg_data_lock_tx);
+	spin_lock_init(&sta->ht_ba_mlme.agg_data_lock_rx);
+	for (i=0; i< STA_TID_NUM; i++) {
+		sta->timer_to_tid[i] = i;
+		/* tx timers */
+		sta->ht_ba_mlme.tid_agg_info_tx[i].addba_resp_timer.function = sta_addba_resp_timer_expired;
+		sta->ht_ba_mlme.tid_agg_info_tx[i].addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[i];
+		init_timer(&sta->ht_ba_mlme.tid_agg_info_tx[i].addba_resp_timer);
+
+		/* net sched tx queue id: initialize to max (0 is valid one)*/
+       		sta->tx_queue_id[i] = local->hw.queues;
+
+		/* rx timers */
+		sta->ht_ba_mlme.tid_agg_info_rx[i].session_timer.function = sta_rx_agg_session_timer_expired;
+		sta->ht_ba_mlme.tid_agg_info_rx[i].session_timer.data = (unsigned long)&sta->timer_to_tid[i];
+		init_timer(&sta->ht_ba_mlme.tid_agg_info_rx[i].session_timer);
+	}
+
 	skb_queue_head_init(&sta->ps_tx_buf);
 	skb_queue_head_init(&sta->tx_filtered);
 	__sta_info_get(sta);	/* sta used by caller, decremented by
@@ -218,7 +274,7 @@
 	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 -ruN linux-2.6.22/net/mac80211/sta_info.h linux-2.6.22-mac80211/net/mac80211/sta_info.h
--- linux-2.6.22/net/mac80211/sta_info.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/sta_info.h	2007-08-23 20:59:59.000000000 +0200
@@ -27,8 +27,51 @@
 				    */
 #define WLAN_STA_SHORT_PREAMBLE BIT(7)
 #define WLAN_STA_WME BIT(9)
+#define WLAN_STA_HT  BIT(10)
 #define WLAN_STA_WDS BIT(27)
 
+#define STA_TID_NUM 16
+#define ADDBA_RESP_INTERVAL HZ
+
+enum ht_agg_state {
+	HT_AGG_STATE_IDLE = 0,
+	HT_AGG_STATE_START_BA,
+	HT_AGG_STATE_ADDBA_REQUESTED,
+	HT_AGG_STATE_OPERATIONAL,
+	HT_AGG_STATE_INITIATOR_REQ_STOP_BA,
+	HT_AGG_STATE_RECIPIENT_REQ_STOP_BA
+};
+
+struct tid_ht_agg_info_tx {
+	enum ht_agg_state state;
+	u8 dialog_token;
+	u16 start_seq_num;
+	struct timer_list addba_resp_timer; /* check if peer responds to addba request */
+};
+
+struct tid_ht_agg_info_rx {
+	enum ht_agg_state state;
+	u8 dialog_token;
+	u16 start_seq_num;
+	u16 head_seq_num;
+	u16 buf_size;
+	struct sk_buff** reordering_buf;
+	u16 stored_mpdu_num;			   /* number of mpdus in reordering buffer */
+	struct timer_list session_timer;   /* check if peer keeps Tx-ing on the TID */
+	u16 timeout;
+};
+
+struct sta_ht_agg_info {
+	u8 dialog_token_allocator;
+	struct tid_ht_agg_info_tx tid_agg_info_tx[STA_TID_NUM];
+	struct tid_ht_agg_info_rx tid_agg_info_rx[STA_TID_NUM];
+	spinlock_t agg_data_lock_tx;
+	spinlock_t agg_data_lock_rx;
+};
+
+
+
+
 
 struct sta_info {
 	struct kref kref;
@@ -82,9 +125,12 @@
 	unsigned long rx_fragments; /* number of received MPDUs */
 	unsigned long rx_dropped; /* number of dropped MPDUs from this STA */
 
-	int last_rssi; /* RSSI of last received frame from this STA */
-	int last_signal; /* signal of last received frame from this STA */
-	int last_noise; /* noise of last received frame from this STA */
+	int accum_rssi; /* hi-precision running average (rssi * 16) */
+	int accum_signal; /* hi-precision average (signal-quality * 16) */
+	int accum_noise; /* hi-precision running average (noise * 16) */
+	int last_rssi; /* average RSSI of recent frames from this STA */
+	int last_signal; /* average sig-qual of recent frames from this STA */
+	int last_noise; /* average noise of recent frames from this STA */
 	int last_ack_rssi[3]; /* RSSI of last received ACKs from this STA */
 	unsigned long last_ack;
 	int channel_use;
@@ -103,6 +149,12 @@
 #endif
 	int assoc_ap; /* whether this is an AP that we are
 		       * associated with as a client */
+	int dls_sta; /* 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];
@@ -112,6 +164,10 @@
 	int vlan_id;
 
 	u16 listen_interval;
+	struct sta_ht_agg_info ht_ba_mlme;
+	int timer_to_tid[STA_TID_NUM]; /* converts expired addba response timer */
+	int tx_queue_id[STA_TID_NUM];
+
 
 #ifdef CONFIG_MAC80211_DEBUGFS
 	struct sta_info_debugfsdentries {
@@ -122,6 +178,7 @@
 		struct dentry *last_ack_ms;
 		struct dentry *inactive_ms;
 		struct dentry *last_seq_ctrl;
+		struct dentry *agg_status;
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
 		struct dentry *wme_rx_queue;
 		struct dentry *wme_tx_queue;
diff -ruN linux-2.6.22/net/mac80211/wme.c linux-2.6.22-mac80211/net/mac80211/wme.c
--- linux-2.6.22/net/mac80211/wme.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/wme.c	2007-08-23 20:59:59.000000000 +0200
@@ -29,12 +29,18 @@
 {
 	u8 *data = rx->skb->data;
 	int tid;
+	unsigned int is_agg_frame = 0;
 
 	/* does the frame have a qos control field? */
 	if (WLAN_FC_IS_QOS_DATA(rx->fc)) {
 		u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
+
 		/* frame has qos control */
-		tid = qc[0] & QOS_CONTROL_TID_MASK;
+		rx->u.rx.qos_control = le16_to_cpu(*((__le16*)qc));
+		tid = rx->u.rx.qos_control & QOS_CONTROL_TID_MASK;
+		if (rx->u.rx.qos_control &
+		    IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+			is_agg_frame = 1;
 	} else {
 		if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
 			/* Separate TID for management frames */
@@ -43,6 +49,7 @@
 			/* no qos control present */
 			tid = 0; /* 802.1d - Best Effort */
 		}
+		rx->u.rx.qos_control = 0;
 	}
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
 	I802_DEBUG_INC(rx->local->wme_rx_queue[tid]);
@@ -52,6 +59,7 @@
 #endif /* CONFIG_MAC80211_DEBUG_COUNTERS */
 
 	rx->u.rx.queue = tid;
+	rx->u.rx.is_agg_frame = is_agg_frame;
 	/* Set skb->priority to 1d tag if highest order bit of TID is not set.
 	 * For now, set skb->priority to 0 for other cases. */
 	rx->skb->priority = (tid > 7) ? 0 : tid;
@@ -83,13 +91,14 @@
 
 #ifdef CONFIG_NET_SCHED
 /* maximum number of hardware queues we support. */
-#define TC_80211_MAX_QUEUES 8
+#define TC_80211_MAX_QUEUES 16
 
 struct ieee80211_sched_data
 {
 	struct tcf_proto *filter_list;
 	struct Qdisc *queues[TC_80211_MAX_QUEUES];
 	struct sk_buff_head requeued[TC_80211_MAX_QUEUES];
+	unsigned long qdisc_pool;
 };
 
 
@@ -158,11 +167,13 @@
 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 +200,38 @@
 	}
 
 	/* 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;
 
-	/* incase we are a client verify acm is not set for this ac */
-	while (unlikely(local->wmm_acm & BIT(skb->priority))) {
+	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;
+		}
+	}
+
+	/* 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;
 		}
 	}
@@ -228,15 +263,33 @@
 	/* now we know the 1d priority, fill in the QoS header if there is one
 	 */
 	if (WLAN_FC_IS_QOS_DATA(fc)) {
+		struct sta_info *sta;
 		u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2;
-		u8 qos_hdr = skb->priority & QOS_CONTROL_TAG1D_MASK;
+		u8 tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
+		u8 ack_policy = 0;
 		if (local->wifi_wme_noack_test)
-			qos_hdr |= QOS_CONTROL_ACK_POLICY_NOACK <<
+			ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK <<
 					QOS_CONTROL_ACK_POLICY_SHIFT;
 		/* qos header is 2 bytes, second reserved */
-		*p = qos_hdr;
+		*p = ack_policy|tid;
 		p++;
 		*p = 0;
+
+		sta = sta_info_get(local, hdr->addr1);
+		if (sta) {
+			int ht_queue = sta->tx_queue_id[tid];
+			if ((ht_queue < local->hw.queues) &&
+				test_bit(ht_queue, &q->qdisc_pool)) {
+				queue = ht_queue;
+				pkt_data->ht_queue = 1;
+			}
+			printk(KERN_DEBUG "wme:%s ht_queue=%d,queue=%d pool=0x%lX qdisc=%p\n",
+				 __func__,ht_queue,queue,q->qdisc_pool,q);
+
+			sta_info_put(sta);
+		}
+
+
 	}
 
 	if (unlikely(queue >= local->hw.queues)) {
@@ -254,6 +307,7 @@
 			kfree_skb(skb);
 			err = NET_XMIT_DROP;
 	} else {
+
 		pkt_data->queue = (unsigned int) queue;
 		qdisc = q->queues[queue];
 		err = qdisc->enqueue(skb, qdisc);
@@ -305,10 +359,9 @@
 	/* check all the h/w queues in numeric/priority order */
 	for (queue = 0; queue < hw->queues; queue++) {
 		/* see if there is room in this hardware queue */
-		if (test_bit(IEEE80211_LINK_STATE_XOFF,
-			     &local->state[queue]) ||
-		    test_bit(IEEE80211_LINK_STATE_PENDING,
-			     &local->state[queue]))
+		if ((test_bit(IEEE80211_LINK_STATE_XOFF,&local->state[queue])) ||
+		    (test_bit(IEEE80211_LINK_STATE_PENDING,&local->state[queue])) ||
+			 (!test_bit(queue,&q->qdisc_pool)))
 			continue;
 
 		/* there is space - try and get a frame */
@@ -430,6 +483,10 @@
 		}
 	}
 
+	for (i = 0; i < 4; i++)
+		set_bit(i,&q->qdisc_pool);
+
+
 	return err;
 }
 
@@ -676,3 +733,51 @@
 	unregister_qdisc(&wme_qdisc_ops);
 }
 #endif /* CONFIG_NET_SCHED */
+
+int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, struct sta_info *sta, u16 tid)
+{
+#ifdef CONFIG_NET_SCHED
+	int i;
+	struct ieee80211_sched_data *q = qdisc_priv(local->mdev->qdisc_sleeping);
+
+	/* prepare the filter and save it for the SW queue
+	* matching the recieved HW queue
+	* (TODO - policy should be replaced for AP)*/
+
+	/* try to get a Qdisc from a pool */
+	/* FIXME: use test and set command */
+	for (i=7; i < local->hw.queues; i++)
+		if (!test_and_set_bit(i, &q->qdisc_pool)) {
+			sta->tx_queue_id[tid] = i;
+
+			/* IF there are already pending packets
+			 * on this tid first we need to drain them
+			 * on the previous queue
+			 * since HT is strict in order */
+#ifdef CONFIG_MAC80211_DEBUG
+			printk(KERN_ERR "mac80211:wme allocated ht agg queue %d"
+				"for tid=%d and sta " MAC_FMT "pool=0x%lX qdisc=%p\n",
+				i,tid,MAC_ARG(sta->addr),q->qdisc_pool,q);
+#endif /* CONFIG_MAC80211_DEBUG */
+			return 0;
+		}
+
+#endif /* CONFIG_NET_SCHED */
+	return -EAGAIN;
+}
+
+int ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, struct sta_info *sta, u16 tid)
+{
+#ifdef CONFIG_NET_SCHED
+       	int i;
+	struct ieee80211_sched_data *q = qdisc_priv(local->mdev->qdisc_sleeping);
+
+	/* return the qdisc to the pool */
+	i = sta->tx_queue_id[tid];
+	clear_bit(i, &q->qdisc_pool);
+	sta->tx_queue_id[tid] = local->hw.queues;
+
+#endif /* CONFIG_NET_SCHED */
+	return 0;
+}
+
diff -ruN linux-2.6.22/net/mac80211/wme.h linux-2.6.22-mac80211/net/mac80211/wme.h
--- linux-2.6.22/net/mac80211/wme.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/mac80211/wme.h	2007-08-23 20:59:59.000000000 +0200
@@ -30,6 +30,11 @@
 ieee80211_txrx_result
 ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx);
 
+#define AGG_BASE_QUEUE 7  // TODO - remove when switching to dynamic queues
+
+int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, struct sta_info *sta, u16 tid);
+int ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, struct sta_info *sta, u16 tid);
+
 #ifdef CONFIG_NET_SCHED
 void ieee80211_install_qdisc(struct net_device *dev);
 int ieee80211_qdisc_installed(struct net_device *dev);
diff -ruN linux-2.6.22/net/wireless/Makefile linux-2.6.22-mac80211/net/wireless/Makefile
--- linux-2.6.22/net/wireless/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-mac80211/net/wireless/Makefile	2007-08-23 20:59:59.000000000 +0200
@@ -1,4 +1,4 @@
 obj-$(CONFIG_WIRELESS_EXT) += wext.o
 obj-$(CONFIG_CFG80211) += cfg80211.o
 
-cfg80211-y += core.o sysfs.o
+cfg80211-y += core.o sysfs.o radiotap.o
diff -ruN linux-2.6.22/net/wireless/radiotap.c linux-2.6.22-mac80211/net/wireless/radiotap.c
--- linux-2.6.22/net/wireless/radiotap.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-mac80211/net/wireless/radiotap.c	2007-08-23 20:59:59.000000000 +0200
@@ -0,0 +1,257 @@
+/*
+ * Radiotap parser
+ *
+ * Copyright 2007		Andy Green <andy@warmcat.com>
+ */
+
+#include <net/cfg80211.h>
+#include <net/ieee80211_radiotap.h>
+#include <asm/unaligned.h>
+
+/* function prototypes and related defs are in include/net/cfg80211.h */
+
+/**
+ * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
+ * @iterator: radiotap_iterator to initialize
+ * @radiotap_header: radiotap header to parse
+ * @max_length: total length we can parse into (eg, whole packet length)
+ *
+ * Returns: 0 or a negative error code if there is a problem.
+ *
+ * This function initializes an opaque iterator struct which can then
+ * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
+ * argument which is present in the header.  It knows about extended
+ * present headers and handles them.
+ *
+ * How to use:
+ * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
+ * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
+ * checking for a good 0 return code.  Then loop calling
+ * __ieee80211_radiotap_iterator_next()... it returns either 0,
+ * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
+ * The iterator's @this_arg member points to the start of the argument
+ * associated with the current argument index that is present, which can be
+ * found in the iterator's @this_arg_index member.  This arg index corresponds
+ * to the IEEE80211_RADIOTAP_... defines.
+ *
+ * Radiotap header length:
+ * You can find the CPU-endian total radiotap header length in
+ * iterator->max_length after executing ieee80211_radiotap_iterator_init()
+ * successfully.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned.  Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ *
+ * Example code:
+ * See Documentation/networking/radiotap-headers.txt
+ */
+
+int ieee80211_radiotap_iterator_init(
+    struct ieee80211_radiotap_iterator *iterator,
+    struct ieee80211_radiotap_header *radiotap_header,
+    int max_length)
+{
+	/* Linux only supports version 0 radiotap format */
+	if (radiotap_header->it_version)
+		return -EINVAL;
+
+	/* sanity check for allowed length and radiotap length field */
+	if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len)))
+		return -EINVAL;
+
+	iterator->rtheader = radiotap_header;
+	iterator->max_length = le16_to_cpu(get_unaligned(
+						&radiotap_header->it_len));
+	iterator->arg_index = 0;
+	iterator->bitmap_shifter = le32_to_cpu(get_unaligned(
+						&radiotap_header->it_present));
+	iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header);
+	iterator->this_arg = NULL;
+
+	/* find payload start allowing for extended bitmap(s) */
+
+	if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) {
+		while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) &
+				   (1<<IEEE80211_RADIOTAP_EXT)) {
+			iterator->arg += sizeof(u32);
+
+			/*
+			 * check for insanity where the present bitmaps
+			 * keep claiming to extend up to or even beyond the
+			 * stated radiotap header length
+			 */
+
+			if (((ulong)iterator->arg -
+			     (ulong)iterator->rtheader) > iterator->max_length)
+				return -EINVAL;
+		}
+
+		iterator->arg += sizeof(u32);
+
+		/*
+		 * no need to check again for blowing past stated radiotap
+		 * header length, because ieee80211_radiotap_iterator_next
+		 * checks it before it is dereferenced
+		 */
+	}
+
+	/* we are all initialized happily */
+
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
+
+
+/**
+ * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
+ * @iterator: radiotap_iterator to move to next arg (if any)
+ *
+ * Returns: 0 if there is an argument to handle,
+ * -ENOENT if there are no more args or -EINVAL
+ * if there is something else wrong.
+ *
+ * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
+ * in @this_arg_index and sets @this_arg to point to the
+ * payload for the field.  It takes care of alignment handling and extended
+ * present fields.  @this_arg can be changed by the caller (eg,
+ * incremented to move inside a compound argument like
+ * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in
+ * little-endian format whatever the endianess of your CPU.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned.  Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ */
+
+int ieee80211_radiotap_iterator_next(
+    struct ieee80211_radiotap_iterator *iterator)
+{
+
+	/*
+	 * small length lookup table for all radiotap types we heard of
+	 * starting from b0 in the bitmap, so we can walk the payload
+	 * area of the radiotap header
+	 *
+	 * There is a requirement to pad args, so that args
+	 * of a given length must begin at a boundary of that length
+	 * -- but note that compound args are allowed (eg, 2 x u16
+	 * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not
+	 * a reliable indicator of alignment requirement.
+	 *
+	 * upper nybble: content alignment for arg
+	 * lower nybble: content length for arg
+	 */
+
+	static const u8 rt_sizes[] = {
+		[IEEE80211_RADIOTAP_TSFT] = 0x88,
+		[IEEE80211_RADIOTAP_FLAGS] = 0x11,
+		[IEEE80211_RADIOTAP_RATE] = 0x11,
+		[IEEE80211_RADIOTAP_CHANNEL] = 0x24,
+		[IEEE80211_RADIOTAP_FHSS] = 0x22,
+		[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11,
+		[IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11,
+		[IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22,
+		[IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22,
+		[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22,
+		[IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11,
+		[IEEE80211_RADIOTAP_ANTENNA] = 0x11,
+		[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11,
+		[IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11
+		/*
+		 * add more here as they are defined in
+		 * include/net/ieee80211_radiotap.h
+		 */
+	};
+
+	/*
+	 * for every radiotap entry we can at
+	 * least skip (by knowing the length)...
+	 */
+
+	while (iterator->arg_index < sizeof(rt_sizes)) {
+		int hit = 0;
+		int pad;
+
+		if (!(iterator->bitmap_shifter & 1))
+			goto next_entry; /* arg not present */
+
+		/*
+		 * arg is present, account for alignment padding
+		 *  8-bit args can be at any alignment
+		 * 16-bit args must start on 16-bit boundary
+		 * 32-bit args must start on 32-bit boundary
+		 * 64-bit args must start on 64-bit boundary
+		 *
+		 * note that total arg size can differ from alignment of
+		 * elements inside arg, so we use upper nybble of length
+		 * table to base alignment on
+		 *
+		 * also note: these alignments are ** relative to the
+		 * start of the radiotap header **.  There is no guarantee
+		 * that the radiotap header itself is aligned on any
+		 * kind of boundary.
+		 *
+		 * the above is why get_unaligned() is used to dereference
+		 * multibyte elements from the radiotap area
+		 */
+
+		pad = (((ulong)iterator->arg) -
+			((ulong)iterator->rtheader)) &
+			((rt_sizes[iterator->arg_index] >> 4) - 1);
+
+		if (pad)
+			iterator->arg +=
+				(rt_sizes[iterator->arg_index] >> 4) - pad;
+
+		/*
+		 * this is what we will return to user, but we need to
+		 * move on first so next call has something fresh to test
+		 */
+		iterator->this_arg_index = iterator->arg_index;
+		iterator->this_arg = iterator->arg;
+		hit = 1;
+
+		/* internally move on the size of this arg */
+		iterator->arg += rt_sizes[iterator->arg_index] & 0x0f;
+
+		/*
+		 * check for insanity where we are given a bitmap that
+		 * claims to have more arg content than the length of the
+		 * radiotap section.  We will normally end up equalling this
+		 * max_length on the last arg, never exceeding it.
+		 */
+
+		if (((ulong)iterator->arg - (ulong)iterator->rtheader) >
+		    iterator->max_length)
+			return -EINVAL;
+
+	next_entry:
+		iterator->arg_index++;
+		if (unlikely((iterator->arg_index & 31) == 0)) {
+			/* completed current u32 bitmap */
+			if (iterator->bitmap_shifter & 1) {
+				/* b31 was set, there is more */
+				/* move to next u32 bitmap */
+				iterator->bitmap_shifter = le32_to_cpu(
+					get_unaligned(iterator->next_bitmap));
+				iterator->next_bitmap++;
+			} else
+				/* no more bitmaps: end */
+				iterator->arg_index = sizeof(rt_sizes);
+		} else /* just try the next bit */
+			iterator->bitmap_shifter >>= 1;
+
+		/* if we found a valid arg earlier, return it now */
+		if (hit)
+			return 0;
+	}
+
+	/* we don't know how to handle any more args, we're done */
+	return -ENOENT;
+}
+EXPORT_SYMBOL(ieee80211_radiotap_iterator_next);

