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-2020 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            sta->sdata->vif.bss_conf.chandef.chan->band != NL80211_BAND_6GHZ) {
 297                ht_dbg(sta->sdata,
 298                       "STA %pM erroneously requests BA session on tid %d w/o QoS\n",
 299                       sta->sta.addr, tid);
 300                /* send a response anyway, it's an error case if we get here */
 301                goto end;
 302        }
 303
 304        if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
 305                ht_dbg(sta->sdata,
 306                       "Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
 307                       sta->sta.addr, tid);
 308                goto end;
 309        }
 310
 311        if (sta->sta.he_cap.has_he)
 312                max_buf_size = IEEE80211_MAX_AMPDU_BUF;
 313        else
 314                max_buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
 315
 316        /* sanity check for incoming parameters:
 317         * check if configuration can support the BA policy
 318         * and if buffer size does not exceeds max value */
 319        /* XXX: check own ht delayed BA capability?? */
 320        if (((ba_policy != 1) &&
 321             (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) ||
 322            (buf_size > max_buf_size)) {
 323                status = WLAN_STATUS_INVALID_QOS_PARAM;
 324                ht_dbg_ratelimited(sta->sdata,
 325                                   "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
 326                                   sta->sta.addr, tid, ba_policy, buf_size);
 327                goto end;
 328        }
 329        /* determine default buffer size */
 330        if (buf_size == 0)
 331                buf_size = max_buf_size;
 332
 333        /* make sure the size doesn't exceed the maximum supported by the hw */
 334        if (buf_size > sta->sta.max_rx_aggregation_subframes)
 335                buf_size = sta->sta.max_rx_aggregation_subframes;
 336        params.buf_size = buf_size;
 337
 338        ht_dbg(sta->sdata, "AddBA Req buf_size=%d for %pM\n",
 339               buf_size, sta->sta.addr);
 340
 341        /* examine state machine */
 342        lockdep_assert_held(&sta->ampdu_mlme.mtx);
 343
 344        if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) {
 345                if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) {
 346                        struct tid_ampdu_rx *tid_rx;
 347
 348                        ht_dbg_ratelimited(sta->sdata,
 349                                           "updated AddBA Req from %pM on tid %u\n",
 350                                           sta->sta.addr, tid);
 351                        /* We have no API to update the timeout value in the
 352                         * driver so reject the timeout update if the timeout
 353                         * changed. If it did not change, i.e., no real update,
 354                         * just reply with success.
 355                         */
 356                        rcu_read_lock();
 357                        tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
 358                        if (tid_rx && tid_rx->timeout == timeout)
 359                                status = WLAN_STATUS_SUCCESS;
 360                        else
 361                                status = WLAN_STATUS_REQUEST_DECLINED;
 362                        rcu_read_unlock();
 363                        goto end;
 364                }
 365
 366                ht_dbg_ratelimited(sta->sdata,
 367                                   "unexpected AddBA Req from %pM on tid %u\n",
 368                                   sta->sta.addr, tid);
 369
 370                /* delete existing Rx BA session on the same tid */
 371                ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
 372                                                WLAN_STATUS_UNSPECIFIED_QOS,
 373                                                false);
 374        }
 375
 376        if (ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER)) {
 377                ret = drv_ampdu_action(local, sta->sdata, &params);
 378                ht_dbg(sta->sdata,
 379                       "Rx A-MPDU request on %pM tid %d result %d\n",
 380                       sta->sta.addr, tid, ret);
 381                if (!ret)
 382                        status = WLAN_STATUS_SUCCESS;
 383                goto end;
 384        }
 385
 386        /* prepare A-MPDU MLME for Rx aggregation */
 387        tid_agg_rx = kzalloc(sizeof(*tid_agg_rx), GFP_KERNEL);
 388        if (!tid_agg_rx)
 389                goto end;
 390
 391        spin_lock_init(&tid_agg_rx->reorder_lock);
 392
 393        /* rx timer */
 394        timer_setup(&tid_agg_rx->session_timer,
 395                    sta_rx_agg_session_timer_expired, TIMER_DEFERRABLE);
 396
 397        /* rx reorder timer */
 398        timer_setup(&tid_agg_rx->reorder_timer,
 399                    sta_rx_agg_reorder_timer_expired, 0);
 400
 401        /* prepare reordering buffer */
 402        tid_agg_rx->reorder_buf =
 403                kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
 404        tid_agg_rx->reorder_time =
 405                kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
 406        if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
 407                kfree(tid_agg_rx->reorder_buf);
 408                kfree(tid_agg_rx->reorder_time);
 409                kfree(tid_agg_rx);
 410                goto end;
 411        }
 412
 413        for (i = 0; i < buf_size; i++)
 414                __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
 415
 416        ret = drv_ampdu_action(local, sta->sdata, &params);
 417        ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
 418               sta->sta.addr, tid, ret);
 419        if (ret) {
 420                kfree(tid_agg_rx->reorder_buf);
 421                kfree(tid_agg_rx->reorder_time);
 422                kfree(tid_agg_rx);
 423                goto end;
 424        }
 425
 426        /* update data */
 427        tid_agg_rx->ssn = start_seq_num;
 428        tid_agg_rx->head_seq_num = start_seq_num;
 429        tid_agg_rx->buf_size = buf_size;
 430        tid_agg_rx->timeout = timeout;
 431        tid_agg_rx->stored_mpdu_num = 0;
 432        tid_agg_rx->auto_seq = auto_seq;
 433        tid_agg_rx->started = false;
 434        tid_agg_rx->reorder_buf_filtered = 0;
 435        tid_agg_rx->tid = tid;
 436        tid_agg_rx->sta = sta;
 437        status = WLAN_STATUS_SUCCESS;
 438
 439        /* activate it for RX */
 440        rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx);
 441
 442        if (timeout) {
 443                mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout));
 444                tid_agg_rx->last_rx = jiffies;
 445        }
 446
 447end:
 448        if (status == WLAN_STATUS_SUCCESS) {
 449                __set_bit(tid, sta->ampdu_mlme.agg_session_valid);
 450                __clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
 451                sta->ampdu_mlme.tid_rx_token[tid] = dialog_token;
 452        }
 453
 454        if (tx)
 455                ieee80211_send_addba_resp(sta, sta->sta.addr, tid,
 456                                          dialog_token, status, 1, buf_size,
 457                                          timeout, addbaext);
 458}
 459
 460static void __ieee80211_start_rx_ba_session(struct sta_info *sta,
 461                                            u8 dialog_token, u16 timeout,
 462                                            u16 start_seq_num, u16 ba_policy,
 463                                            u16 tid, u16 buf_size, bool tx,
 464                                            bool auto_seq,
 465                                            const struct ieee80211_addba_ext_ie *addbaext)
 466{
 467        mutex_lock(&sta->ampdu_mlme.mtx);
 468        ___ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
 469                                         start_seq_num, ba_policy, tid,
 470                                         buf_size, tx, auto_seq, addbaext);
 471        mutex_unlock(&sta->ampdu_mlme.mtx);
 472}
 473
 474void ieee80211_process_addba_request(struct ieee80211_local *local,
 475                                     struct sta_info *sta,
 476                                     struct ieee80211_mgmt *mgmt,
 477                                     size_t len)
 478{
 479        u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
 480        struct ieee802_11_elems elems = { };
 481        u8 dialog_token;
 482        int ies_len;
 483
 484        /* extract session parameters from addba request frame */
 485        dialog_token = mgmt->u.action.u.addba_req.dialog_token;
 486        timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
 487        start_seq_num =
 488                le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
 489
 490        capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
 491        ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
 492        tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
 493        buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
 494
 495        ies_len = len - offsetof(struct ieee80211_mgmt,
 496                                 u.action.u.addba_req.variable);
 497        if (ies_len) {
 498                ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
 499                                ies_len, true, &elems, mgmt->bssid, NULL);
 500                if (elems.parse_error)
 501                        return;
 502        }
 503
 504        __ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
 505                                        start_seq_num, ba_policy, tid,
 506                                        buf_size, true, false,
 507                                        elems.addba_ext_ie);
 508}
 509
 510void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif,
 511                                 const u8 *addr, unsigned int tid)
 512{
 513        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 514        struct ieee80211_local *local = sdata->local;
 515        struct sta_info *sta;
 516
 517        rcu_read_lock();
 518        sta = sta_info_get_bss(sdata, addr);
 519        if (!sta)
 520                goto unlock;
 521
 522        set_bit(tid, sta->ampdu_mlme.tid_rx_manage_offl);
 523        ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
 524 unlock:
 525        rcu_read_unlock();
 526}
 527EXPORT_SYMBOL(ieee80211_manage_rx_ba_offl);
 528
 529void ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif,
 530                                   const u8 *addr, unsigned int tid)
 531{
 532        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 533        struct ieee80211_local *local = sdata->local;
 534        struct sta_info *sta;
 535
 536        rcu_read_lock();
 537        sta = sta_info_get_bss(sdata, addr);
 538        if (!sta)
 539                goto unlock;
 540
 541        set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired);
 542        ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
 543
 544 unlock:
 545        rcu_read_unlock();
 546}
 547EXPORT_SYMBOL(ieee80211_rx_ba_timer_expired);
 548