static int ar9170_ampdu_action(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               enum ieee80211_ampdu_mlme_action action,
-                              struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                              struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                              u8 buf_size)
 {
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
 
                                  struct ieee80211_vif *vif,
                                  enum ieee80211_ampdu_mlme_action action,
                                  struct ieee80211_sta *sta,
-                                 u16 tid, u16 *ssn)
+                                 u16 tid, u16 *ssn, u8 buf_size)
 {
        struct ath9k_htc_priv *priv = hw->priv;
        struct ath9k_htc_sta *ista;
 
                              struct ieee80211_vif *vif,
                              enum ieee80211_ampdu_mlme_action action,
                              struct ieee80211_sta *sta,
-                             u16 tid, u16 *ssn)
+                             u16 tid, u16 *ssn, u8 buf_size)
 {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
 
                                    struct ieee80211_vif *vif,
                                    enum ieee80211_ampdu_mlme_action action,
                                    struct ieee80211_sta *sta,
-                                   u16 tid, u16 *ssn)
+                                   u16 tid, u16 *ssn, u8 buf_size)
 {
        struct ar9170 *ar = hw->priv;
        struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
 
 int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            enum ieee80211_ampdu_mlme_action action,
-                           struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                           struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                           u8 buf_size)
 {
        struct iwl_priv *priv = hw->priv;
        int ret = -EINVAL;
 
 int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            enum ieee80211_ampdu_mlme_action action,
-                           struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+                           struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                           u8 buf_size);
 int iwlagn_mac_sta_add(struct ieee80211_hw *hw,
                       struct ieee80211_vif *vif,
                       struct ieee80211_sta *sta);
 
 static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       enum ieee80211_ampdu_mlme_action action,
-                                      struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                                      struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                                      u8 buf_size)
 {
        switch (action) {
        case IEEE80211_AMPDU_TX_START:
 
 static int
 mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                   enum ieee80211_ampdu_mlme_action action,
-                  struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                  struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                  u8 buf_size)
 {
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
 
 
 int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                       u8 buf_size)
 {
        int ret = 0;
 
 
 u64 rt2800_get_tsf(struct ieee80211_hw *hw);
 int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                       u8 buf_size);
 int rt2800_get_survey(struct ieee80211_hw *hw, int idx,
                      struct survey_info *survey);
 
 
 static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               enum ieee80211_ampdu_mlme_action action,
-                              struct ieee80211_sta *sta, u16 tid, u16 * ssn)
+                              struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                              u8 buf_size)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
 
  *     ieee80211_ampdu_mlme_action. Starting sequence number (@ssn)
  *     is the first frame we expect to perform the action on. Notice
  *     that TX/RX_STOP can pass NULL for this parameter.
+ *     The @buf_size parameter is only valid when the action is set to
+ *     %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's reorder
+ *     buffer size (number of subframes) for this session -- aggregates
+ *     containing more subframes than this may not be transmitted to the peer.
  *     Returns a negative error code on failure.
  *     The callback can sleep.
  *
        int (*ampdu_action)(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            enum ieee80211_ampdu_mlme_action action,
-                           struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+                           struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                           u8 buf_size);
        int (*get_survey)(struct ieee80211_hw *hw, int idx,
                struct survey_info *survey);
        void (*rfkill_poll)(struct ieee80211_hw *hw);
 
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
        if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP,
-                            &sta->sta, tid, NULL))
+                            &sta->sta, tid, NULL, 0))
                printk(KERN_DEBUG "HW problem - can not stop rx "
                                "aggregation for tid %d\n", tid);
 
        }
 
        ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
-                              &sta->sta, tid, &start_seq_num);
+                              &sta->sta, tid, &start_seq_num, 0);
 #ifdef CONFIG_MAC80211_HT_DEBUG
        printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
 
        ret = drv_ampdu_action(local, sta->sdata,
                               IEEE80211_AMPDU_TX_STOP,
-                              &sta->sta, tid, NULL);
+                              &sta->sta, tid, NULL, 0);
 
        /* HW shall not deny going back to legacy */
        if (WARN_ON(ret)) {
        start_seq_num = sta->tid_seq[tid] >> 4;
 
        ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START,
-                              &sta->sta, tid, &start_seq_num);
+                              &sta->sta, tid, &start_seq_num, 0);
        if (ret) {
 #ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "BA request denied - HW unavailable for"
 
        drv_ampdu_action(local, sta->sdata,
                         IEEE80211_AMPDU_TX_OPERATIONAL,
-                        &sta->sta, tid, NULL);
+                        &sta->sta, tid, NULL,
+                        sta->ampdu_mlme.tid_tx[tid]->buf_size);
 
        /*
         * synchronize with TX path, while splicing the TX path
 {
        struct tid_ampdu_tx *tid_tx;
        u16 capab, tid;
+       u8 buf_size;
 
        capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
        tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+       buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
 
        mutex_lock(&sta->ampdu_mlme.mtx);
 
 
        if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
                        == WLAN_STATUS_SUCCESS) {
+               /*
+                * IEEE 802.11-2007 7.3.1.14:
+                * In an ADDBA Response frame, when the Status Code field
+                * is set to 0, the Buffer Size subfield is set to a value
+                * of at least 1.
+                */
+               if (!buf_size)
+                       goto out;
+
                if (test_and_set_bit(HT_AGG_STATE_RESPONSE_RECEIVED,
                                     &tid_tx->state)) {
                        /* ignore duplicate response */
                        goto out;
                }
 
+               tid_tx->buf_size = buf_size;
+
                if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))
                        ieee80211_agg_tx_operational(local, sta, tid);
 
 
                                   struct ieee80211_sub_if_data *sdata,
                                   enum ieee80211_ampdu_mlme_action action,
                                   struct ieee80211_sta *sta, u16 tid,
-                                  u16 *ssn)
+                                  u16 *ssn, u8 buf_size)
 {
        int ret = -EOPNOTSUPP;
 
        might_sleep();
 
-       trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn);
+       trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size);
 
        if (local->ops->ampdu_action)
                ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action,
-                                              sta, tid, ssn);
+                                              sta, tid, ssn, buf_size);
 
        trace_drv_return_int(local, ret);
 
 
                 struct ieee80211_sub_if_data *sdata,
                 enum ieee80211_ampdu_mlme_action action,
                 struct ieee80211_sta *sta, u16 tid,
-                u16 *ssn),
+                u16 *ssn, u8 buf_size),
 
-       TP_ARGS(local, sdata, action, sta, tid, ssn),
+       TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
                __field(u32, action)
                __field(u16, tid)
                __field(u16, ssn)
+               __field(u8, buf_size)
                VIF_ENTRY
        ),
 
                __entry->action = action;
                __entry->tid = tid;
                __entry->ssn = ssn ? *ssn : 0;
+               __entry->buf_size = buf_size;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d",
-               LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid
+               LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d",
+               LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action,
+               __entry->tid, __entry->buf_size
        )
 );
 
 
  * @state: session state (see above)
  * @stop_initiator: initiator of a session stop
  * @tx_stop: TX DelBA frame when stopping
+ * @buf_size: reorder buffer size at receiver
  *
  * This structure's lifetime is managed by RCU, assignments to
  * the array holding it must hold the aggregation mutex.
        u8 dialog_token;
        u8 stop_initiator;
        bool tx_stop;
+       u8 buf_size;
 };
 
 /**