linux/net/mac80211/agg-rx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * HT handling
   4 *
   5 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
   6 * Copyright 2002-2005, Instant802 Networks, Inc.
   7 * Copyright 2005-2006, Devicescape Software, Inc.
   8 * Copyright 2006-2007  Jiri Benc <jbenc@suse.cz>
   9 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  10 * Copyright 2007-2010, Intel Corporation
  11 * Copyright(c) 2015-2017 Intel Deutschland GmbH
  12 * Copyright (C) 2018        Intel Corporation
  13 */
  14
  15/**
  16 * DOC: RX A-MPDU aggregation
  17 *
  18 * Aggregation on the RX side requires only implementing the
  19 * @ampdu_action callback that is invoked to start/stop any
  20 * block-ack sessions for RX aggregation.
  21 *
  22 * When RX aggregation is started by the peer, the driver is
  23 * notified via @ampdu_action function, with the
  24 * %IEEE80211_AMPDU_RX_START action, and may reject the request
  25 * in which case a negative response is sent to the peer, if it
  26 * accepts it a positive response is sent.
  27 *
  28 * While the session is active, the device/driver are required
  29 * to de-aggregate frames and pass them up one by one to mac80211,
  30 * which will handle the reorder buffer.
  31 *
  32 * When the aggregation session is stopped again by the peer or
  33 * ourselves, the driver's @ampdu_action function will be called
  34 * with the action %IEEE80211_AMPDU_RX_STOP. In this case, the
  35 * call must not fail.
  36 */
  37
  38#include <linux/ieee80211.h>
  39#include <linux/slab.h>
  40#include <linux/export.h>
  41#include <net/mac80211.h>
  42#include "ieee80211_i.h"
  43#include "driver-ops.h"
  44
  45static void ieee80211_free_tid_rx(struct rcu_head *h)
  46{
  47        struct tid_ampdu_rx *tid_rx =
  48                container_of(h, struct tid_ampdu_rx, rcu_head);
  49        int i;
  50
  51        for (i = 0; i < tid_rx->buf_size; i++)
  52                __skb_queue_purge(&tid_rx->reorder_buf[i]);
  53        kfree(tid_rx->reorder_buf);
  54        kfree(tid_rx->reorder_time);
  55        kfree(tid_rx);
  56}
  57
  58void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
  59                                     u16 initiator, u16 reason, bool tx)
  60{
  61        struct ieee80211_local *local = sta->local;
  62        struct tid_ampdu_rx *tid_rx;
  63        struct ieee80211_ampdu_params params = {
  64                .sta = &sta->sta,
  65                .action = IEEE80211_AMPDU_RX_STOP,
  66                .tid = tid,
  67                .amsdu = false,
  68                .timeout = 0,
  69                .ssn = 0,
  70        };
  71
  72        lockdep_assert_held(&sta->ampdu_mlme.mtx);
  73
  74        tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid],
  75                                        lockdep_is_held(&sta->ampdu_mlme.mtx));
  76
  77        if (!test_bit(tid, sta->ampdu_mlme.agg_session_valid))
  78                return;
  79
  80        RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL);
  81        __clear_bit(tid, sta->ampdu_mlme.agg_session_valid);
  82
  83        ht_dbg(sta->sdata,
  84               "Rx BA session stop requested for %pM tid %u %s reason: %d\n",
  85               sta->sta.addr, tid,
  86               initiator == WLAN_BACK_RECIPIENT ? "recipient" : "initiator",
  87               (int)reason);
  88
  89        if (drv_ampdu_action(local, sta->sdata, &params))
  90                sdata_info(sta->sdata,
  91                           "HW problem - can not stop rx aggregation for %pM tid %d\n",
  92                           sta->sta.addr, tid);
  93
  94        /* check if this is a self generated aggregation halt */
  95        if (initiator == WLAN_BACK_RECIPIENT && tx)
  96                ieee80211_send_delba(sta->sdata, sta->sta.addr,
  97                                     tid, WLAN_BACK_RECIPIENT, reason);
  98
  99        /*
 100         * return here in case tid_rx is not assigned - which will happen if
 101         * IEEE80211_HW_SUPPORTS_REORDERING_BUFFER is set.
 102         */
 103        if (!tid_rx)
 104                return;
 105
 106        del_timer_sync(&tid_rx->session_timer);
 107
 108        /* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
 109        spin_lock_bh(&tid_rx->reorder_lock);
 110        tid_rx->removed = true;
 111        spin_unlock_bh(&tid_rx->reorder_lock);
 112        del_timer_sync(&tid_rx->reorder_timer);
 113
 114        call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
 115}
 116
 117void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
 118                                    u16 initiator, u16 reason, bool tx)
 119{
 120        mutex_lock(&sta->ampdu_mlme.mtx);
 121        ___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason, tx);
 122        mutex_unlock(&sta->ampdu_mlme.mtx);
 123}
 124
 125void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
 126                                  const u8 *addr)
 127{
 128        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 129        struct sta_info *sta;
 130        int i;
 131
 132        rcu_read_lock();
 133        sta = sta_info_get_bss(sdata, addr);
 134        if (!sta) {
 135                rcu_read_unlock();
 136                return;
 137        }
 138
 139        for (i = 0; i < IEEE80211_NUM_TIDS; i++)
 140                if (ba_rx_bitmap & BIT(i))
 141                        set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested);
 142
 143        ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
 144        rcu_read_unlock();
 145}
 146EXPORT_SYMBOL(ieee80211_stop_rx_ba_session);
 147
 148/*
 149 * After accepting the AddBA Request we activated a timer,
 150 * resetting it after each frame that arrives from the originator.
 151 */
 152static void sta_rx_agg_session_timer_expired(struct timer_list *t)
 153{
 154        struct tid_ampdu_rx *tid_rx = from_timer(tid_rx, t, session_timer);
 155        struct sta_info *sta = tid_rx->sta;
 156        u8 tid = tid_rx->tid;
 157        unsigned long timeout;
 158
 159        timeout = tid_rx->last_rx + TU_TO_JIFFIES(tid_rx->timeout);
 160        if (time_is_after_jiffies(timeout)) {
 161                mod_timer(&tid_rx->session_timer, timeout);
 162                return;
 163        }
 164
 165        ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n",
 166               sta->sta.addr, tid);
 167
 168        set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired);
 169        ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
 170}
 171
 172static void sta_rx_agg_reorder_timer_expired(struct timer_list *t)
 173{
 174        struct tid_ampdu_rx *tid_rx = from_timer(tid_rx, t, reorder_timer);
 175
 176        rcu_read_lock();
 177        ieee80211_release_reorder_timeout(tid_rx->sta, tid_rx->tid);
 178        rcu_read_unlock();
 179}
 180
 181static void ieee80211_add_addbaext(struct ieee80211_sub_if_data *sdata,
 182                                   struct sk_buff *skb,
 183                                   const struct ieee80211_addba_ext_ie *req)
 184{
 185        struct ieee80211_supported_band *sband;
 186        struct ieee80211_addba_ext_ie *resp;
 187        const struct ieee80211_sta_he_cap *he_cap;
 188        u8 frag_level, cap_frag_level;
 189        u8 *pos;
 190
 191        sband = ieee80211_get_sband(sdata);
 192        if (!sband)
 193                return;
 194        he_cap = ieee80211_get_he_iftype_cap(sband, sdata->vif.type);
 195        if (!he_cap)
 196                return;
 197
 198        pos = skb_put_zero(skb, 2 + sizeof(struct ieee80211_addba_ext_ie));
 199        *pos++ = WLAN_EID_ADDBA_EXT;
 200        *pos++ = sizeof(struct ieee80211_addba_ext_ie);
 201        resp = (struct ieee80211_addba_ext_ie *)pos;
 202        resp->data = req->data & IEEE80211_ADDBA_EXT_NO_FRAG;
 203
 204        frag_level = u32_get_bits(req->data,
 205                                  IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK);
 206        cap_frag_level = u32_get_bits(he_cap->he_cap_elem.mac_cap_info[0],
 207                                      IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_MASK);
 208        if (frag_level > cap_frag_level)
 209                frag_level = cap_frag_level;
 210        resp->data |= u8_encode_bits(frag_level,
 211                                     IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK);
 212}
 213
 214static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid,
 215                                      u8 dialog_token, u16 status, u16 policy,
 216                                      u16 buf_size, u16 timeout,
 217                                      const struct ieee80211_addba_ext_ie *addbaext)
 218{
 219        struct ieee80211_sub_if_data *sdata = sta->sdata;
 220        struct ieee80211_local *local = sdata->local;
 221        struct sk_buff *skb;
 222        struct ieee80211_mgmt *mgmt;
 223        bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
 224        u16 capab;
 225
 226        skb = dev_alloc_skb(sizeof(*mgmt) +
 227                    2 + sizeof(struct ieee80211_addba_ext_ie) +
 228                    local->hw.extra_tx_headroom);
 229        if (!skb)
 230                return;
 231
 232        skb_reserve(skb, local->hw.extra_tx_headroom);
 233        mgmt = skb_put_zero(skb, 24);
 234        memcpy(mgmt->da, da, ETH_ALEN);
 235        memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
 236        if (sdata->vif.type == NL80211_IFTYPE_AP ||
 237            sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
 238            sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
 239                memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
 240        else if (sdata->vif.type == NL80211_IFTYPE_STATION)
 241                memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
 242        else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 243                memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
 244
 245        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
 246                                          IEEE80211_STYPE_ACTION);
 247
 248        skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
 249        mgmt->u.action.category = WLAN_CATEGORY_BACK;
 250        mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
 251        mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
 252
 253        capab = (u16)(amsdu << 0);      /* bit 0 A-MSDU support */
 254        capab |= (u16)(policy << 1);    /* bit 1 aggregation policy */
 255        capab |= (u16)(tid << 2);       /* bit 5:2 TID number */
 256        capab |= (u16)(buf_size << 6);  /* bit 15:6 max size of aggregation */
 257
 258        mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
 259        mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
 260        mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
 261
 262        if (sta->sta.he_cap.has_he && addbaext)
 263                ieee80211_add_addbaext(sdata, skb, addbaext);
 264
 265        ieee80211_tx_skb(sdata, skb);
 266}
 267
 268void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
 269                                      u8 dialog_token, u16 timeout,
 270                                      u16 start_seq_num, u16 ba_policy, u16 tid,
 271                                      u16 buf_size, bool tx, bool auto_seq,
 272                                      const struct ieee80211_addba_ext_ie *addbaext)
 273{
 274        struct ieee80211_local *local = sta->sdata->local;
 275        struct tid_ampdu_rx *tid_agg_rx;
 276        struct ieee80211_ampdu_params params = {
 277                .sta = &sta->sta,
 278                .action = IEEE80211_AMPDU_RX_START,
 279                .tid = tid,
 280                .amsdu = false,
 281                .timeout = timeout,
 282                .ssn = start_seq_num,
 283        };
 284        int i, ret = -EOPNOTSUPP;
 285        u16 status = WLAN_STATUS_REQUEST_DECLINED;
 286        u16 max_buf_size;
 287
 288        if (tid >= IEEE80211_FIRST_TSPEC_TSID) {
 289                ht_dbg(sta->sdata,
 290                       "STA %pM requests BA session on unsupported tid %d\n",
 291                       sta->sta.addr, tid);
 292                goto end;
 293        }
 294
 295        if (!sta->sta.ht_cap.ht_supported) {
 296                ht_dbg(sta->sdata,
 297                       "STA %pM erroneously requests BA session on tid %d w/o QoS\n",
 298                       sta->sta.addr, tid);
 299                /* send a response anyway, it's an error case if we get here */
 300                goto end;
 301        }
 302
 303        if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
 304                ht_dbg(sta->sdata,
 305                       "Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
 306                       sta->sta.addr, tid);
 307                goto end;
 308        }
 309
 310        if (sta->sta.he_cap.has_he)
 311                max_buf_size = IEEE80211_MAX_AMPDU_BUF;
 312        else
 313                max_buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
 314
 315        /* sanity check for incoming parameters:
 316         * check if configuration can support the BA policy
 317         * and if buffer size does not exceeds max value */
 318        /* XXX: check own ht delayed BA capability?? */
 319        if (((ba_policy != 1) &&
 320             (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) ||
 321            (buf_size > max_buf_size)) {
 322                status = WLAN_STATUS_INVALID_QOS_PARAM;
 323                ht_dbg_ratelimited(sta->sdata,
 324                                   "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
 325                                   sta->sta.addr, tid, ba_policy, buf_size);
 326                goto end;
 327        }
 328        /* determine default buffer size */
 329        if (buf_size == 0)
 330                buf_size = max_buf_size;
 331
 332        /* make sure the size doesn't exceed the maximum supported by the hw */
 333        if (buf_size > sta->sta.max_rx_aggregation_subframes)
 334                buf_size = sta->sta.max_rx_aggregation_subframes;
 335        params.buf_size = buf_size;
 336
 337        ht_dbg(sta->sdata, "AddBA Req buf_size=%d for %pM\n",
 338               buf_size, sta->sta.addr);
 339
 340        /* examine state machine */
 341        lockdep_assert_held(&sta->ampdu_mlme.mtx);
 342
 343        if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) {
 344                if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) {
 345                        struct tid_ampdu_rx *tid_rx;
 346
 347                        ht_dbg_ratelimited(sta->sdata,
 348                                           "updated AddBA Req from %pM on tid %u\n",
 349                                           sta->sta.addr, tid);
 350                        /* We have no API to update the timeout value in the
 351                         * driver so reject the timeout update if the timeout
 352                         * changed. If if did not change, i.e., no real update,
 353                         * just reply with success.
 354                         */
 355                        rcu_read_lock();
 356                        tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
 357                        if (tid_rx && tid_rx->timeout == timeout)
 358                                status = WLAN_STATUS_SUCCESS;
 359                        else
 360                                status = WLAN_STATUS_REQUEST_DECLINED;
 361                        rcu_read_unlock();
 362                        goto end;
 363                }
 364
 365                ht_dbg_ratelimited(sta->sdata,
 366                                   "unexpected AddBA Req from %pM on tid %u\n",
 367                                   sta->sta.addr, tid);
 368
 369                /* delete existing Rx BA session on the same tid */
 370                ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
 371                                                WLAN_STATUS_UNSPECIFIED_QOS,
 372                                                false);
 373        }
 374
 375        if (ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER)) {
 376                ret = drv_ampdu_action(local, sta->sdata, &params);
 377                ht_dbg(sta->sdata,
 378                       "Rx A-MPDU request on %pM tid %d result %d\n",
 379                       sta->sta.addr, tid, ret);
 380                if (!ret)
 381                        status = WLAN_STATUS_SUCCESS;
 382                goto end;
 383        }
 384
 385        /* prepare A-MPDU MLME for Rx aggregation */
 386        tid_agg_rx = kzalloc(sizeof(*tid_agg_rx), GFP_KERNEL);
 387        if (!tid_agg_rx)
 388                goto end;
 389
 390        spin_lock_init(&tid_agg_rx->reorder_lock);
 391
 392        /* rx timer */
 393        timer_setup(&tid_agg_rx->session_timer,
 394                    sta_rx_agg_session_timer_expired, TIMER_DEFERRABLE);
 395
 396        /* rx reorder timer */
 397        timer_setup(&tid_agg_rx->reorder_timer,
 398                    sta_rx_agg_reorder_timer_expired, 0);
 399
 400        /* prepare reordering buffer */
 401        tid_agg_rx->reorder_buf =
 402                kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
 403        tid_agg_rx->reorder_time =
 404                kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
 405        if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
 406                kfree(tid_agg_rx->reorder_buf);
 407                kfree(tid_agg_rx->reorder_time);
 408                kfree(tid_agg_rx);
 409                goto end;
 410        }
 411
 412        for (i = 0; i < buf_size; i++)
 413                __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
 414
 415        ret = drv_ampdu_action(local, sta->sdata, &params);
 416        ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
 417               sta->sta.addr, tid, ret);
 418        if (ret) {
 419                kfree(tid_agg_rx->reorder_buf);
 420                kfree(tid_agg_rx->reorder_time);
 421                kfree(tid_agg_rx);
 422                goto end;
 423        }
 424
 425        /* update data */
 426        tid_agg_rx->ssn = start_seq_num;
 427        tid_agg_rx->head_seq_num = start_seq_num;
 428        tid_agg_rx->buf_size = buf_size;
 429        tid_agg_rx->timeout = timeout;
 430        tid_agg_rx->stored_mpdu_num = 0;
 431        tid_agg_rx->auto_seq = auto_seq;
 432        tid_agg_rx->started = false;
 433        tid_agg_rx->reorder_buf_filtered = 0;
 434        tid_agg_rx->tid = tid;
 435        tid_agg_rx->sta = sta;
 436        status = WLAN_STATUS_SUCCESS;
 437
 438        /* activate it for RX */
 439        rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx);
 440
 441        if (timeout) {
 442                mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout));
 443                tid_agg_rx->last_rx = jiffies;
 444        }
 445
 446end:
 447        if (status == WLAN_STATUS_SUCCESS) {
 448                __set_bit(tid, sta->ampdu_mlme.agg_session_valid);
 449                __clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
 450                sta->ampdu_mlme.tid_rx_token[tid] = dialog_token;
 451        }
 452
 453        if (tx)
 454                ieee80211_send_addba_resp(sta, sta->sta.addr, tid,
 455                                          dialog_token, status, 1, buf_size,
 456                                          timeout, addbaext);
 457}
 458
 459static void __ieee80211_start_rx_ba_session(struct sta_info *sta,
 460                                            u8 dialog_token, u16 timeout,
 461                                            u16 start_seq_num, u16 ba_policy,
 462                                            u16 tid, u16 buf_size, bool tx,
 463                                            bool auto_seq,
 464                                            const struct ieee80211_addba_ext_ie *addbaext)
 465{
 466        mutex_lock(&sta->ampdu_mlme.mtx);
 467        ___ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
 468                                         start_seq_num, ba_policy, tid,
 469                                         buf_size, tx, auto_seq, addbaext);
 470        mutex_unlock(&sta->ampdu_mlme.mtx);
 471}
 472
 473void ieee80211_process_addba_request(struct ieee80211_local *local,
 474                                     struct sta_info *sta,
 475                                     struct ieee80211_mgmt *mgmt,
 476                                     size_t len)
 477{
 478        u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
 479        struct ieee802_11_elems elems = { 0 };
 480        u8 dialog_token;
 481        int ies_len;
 482
 483        /* extract session parameters from addba request frame */
 484        dialog_token = mgmt->u.action.u.addba_req.dialog_token;
 485        timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
 486        start_seq_num =
 487                le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
 488
 489        capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
 490        ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
 491        tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
 492        buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
 493
 494        ies_len = len - offsetof(struct ieee80211_mgmt,
 495                                 u.action.u.addba_req.variable);
 496        if (ies_len) {
 497                ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
 498                                ies_len, true, &elems, mgmt->bssid, NULL);
 499                if (elems.parse_error)
 500                        return;
 501        }
 502
 503        __ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
 504                                        start_seq_num, ba_policy, tid,
 505                                        buf_size, true, false,
 506                                        elems.addba_ext_ie);
 507}
 508
 509void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif,
 510                                 const u8 *addr, unsigned int tid)
 511{
 512        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 513        struct ieee80211_local *local = sdata->local;
 514        struct sta_info *sta;
 515
 516        rcu_read_lock();
 517        sta = sta_info_get_bss(sdata, addr);
 518        if (!sta)
 519                goto unlock;
 520
 521        set_bit(tid, sta->ampdu_mlme.tid_rx_manage_offl);
 522        ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
 523 unlock:
 524        rcu_read_unlock();
 525}
 526EXPORT_SYMBOL(ieee80211_manage_rx_ba_offl);
 527
 528void ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif,
 529                                   const u8 *addr, unsigned int tid)
 530{
 531        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 532        struct ieee80211_local *local = sdata->local;
 533        struct sta_info *sta;
 534
 535        rcu_read_lock();
 536        sta = sta_info_get_bss(sdata, addr);
 537        if (!sta)
 538                goto unlock;
 539
 540        set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired);
 541        ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
 542
 543 unlock:
 544        rcu_read_unlock();
 545}
 546EXPORT_SYMBOL(ieee80211_rx_ba_timer_expired);
 547