int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_sta_info *sta_entry = NULL; if (sta == NULL) return -EINVAL; if (!sta->addr) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "ra = NULL\n"); return -EINVAL; } RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "on ra = %pM tid = %d\n", sta->addr, tid); if (unlikely(tid >= MAX_TID_COUNT)) return -EINVAL; sta_entry = (struct rtl_sta_info *)sta->drv_priv; sta_entry->tids[tid].agg.agg_state = RTL_AGG_STOP; ieee80211_stop_tx_ba_cb_irqsafe(mac->vif, sta->addr, tid); return 0; }
static int mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct mt7601u_dev *dev = hw->priv; struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; WARN_ON(msta->wcid.idx > GROUP_WCID(0)); switch (action) { case IEEE80211_AMPDU_RX_START: mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); break; case IEEE80211_AMPDU_RX_STOP: mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ieee80211_send_bar(vif, sta->addr, tid, msta->agg_ssn[tid]); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: break; case IEEE80211_AMPDU_TX_START: msta->agg_ssn[tid] = *ssn << 4; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP_CONT: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; } return 0; }
int rtl_tx_agg_stop(struct ieee80211_hw *hw, const u8 * ra, u16 tid) { int ssn = -1; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_tid_data *tid_data; if (!ra) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("ra = NULL\n")); return -EINVAL; } if (unlikely(tid >= MAX_TID_COUNT)) return -EINVAL; if (mac->tids[tid].agg.agg_state != RTL_AGG_ON) RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, ("Stopping AGG while state not ON or starting\n")); tid_data = &mac->tids[tid]; ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; mac->tids[tid].agg.agg_state = RTL_AGG_OFF; ieee80211_stop_tx_ba_cb_irqsafe(mac->vif, ra, tid); return 0; }
static int brcms_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct scb *scb = (struct scb *)sta->drv_priv; struct brcms_info *wl = hw->priv; int status; if (WARN_ON(scb->magic != SCB_MAGIC)) return -EIDRM; switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: LOCK(wl); status = brcms_c_aggregatable(wl->wlc, tid); UNLOCK(wl); if (!status) { wiphy_err(wl->wiphy, "START: tid %d is not agg\'able\n", tid); return -EINVAL; } /* Future improvement: Use the starting sequence number provided ... */ *ssn = 0; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: LOCK(wl); brcms_c_ampdu_flush(wl->wlc, sta, tid); UNLOCK(wl); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: /* * BA window size from ADDBA response ('buf_size') defines how * many outstanding MPDUs are allowed for the BA stream by * recipient and traffic class. 'ampdu_factor' gives maximum * AMPDU size. */ LOCK(wl); brcms_c_ampdu_tx_operational(wl->wlc, tid, buf_size, (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor)) - 1); UNLOCK(wl); /* Power save wakeup */ break; default: wiphy_err(wl->wiphy, "%s: Invalid command, ignoring\n", __func__); } return 0; }
static int brcms_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct brcms_info *wl = hw->priv; struct scb *scb = &wl->wlc->pri_scb; int status; if (WARN_ON(scb->magic != SCB_MAGIC)) return -EIDRM; switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: spin_lock_bh(&wl->lock); status = brcms_c_aggregatable(wl->wlc, tid); spin_unlock_bh(&wl->lock); if (!status) { brcms_err(wl->wlc->hw->d11core, "START: tid %d is not agg\'able\n", tid); return -EINVAL; } ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: spin_lock_bh(&wl->lock); brcms_c_ampdu_flush(wl->wlc, sta, tid); spin_unlock_bh(&wl->lock); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: /* * BA window size from ADDBA response ('buf_size') defines how * many outstanding MPDUs are allowed for the BA stream by * recipient and traffic class. 'ampdu_factor' gives maximum * AMPDU size. */ spin_lock_bh(&wl->lock); brcms_c_ampdu_tx_operational(wl->wlc, tid, buf_size, (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor)) - 1); spin_unlock_bh(&wl->lock); /* Power save wakeup */ break; default: brcms_err(wl->wlc->hw->d11core, "%s: Invalid command, ignoring\n", __func__); } return 0; }
static int wl_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { #if defined(BCMDBG) struct scb *scb = (struct scb *)sta->drv_priv; #endif struct wl_info *wl = hw->priv; int status; ASSERT(scb->magic == SCB_MAGIC); switch (action) { case IEEE80211_AMPDU_RX_START: WL_NONE("%s: action = IEEE80211_AMPDU_RX_START\n", __func__); break; case IEEE80211_AMPDU_RX_STOP: WL_NONE("%s: action = IEEE80211_AMPDU_RX_STOP\n", __func__); break; case IEEE80211_AMPDU_TX_START: WL_LOCK(wl); status = wlc_aggregatable(wl->wlc, tid); WL_UNLOCK(wl); if (!status) { /* WL_ERROR("START: tid %d is not agg' able, return FAILURE to stack\n", tid); */ return -1; } /* XXX: Use the starting sequence number provided ... */ *ssn = 0; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: WL_LOCK(wl); wlc_ampdu_flush(wl->wlc, sta, tid); WL_UNLOCK(wl); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: /* Not sure what to do here */ /* Power save wakeup */ WL_NONE("%s: action = IEEE80211_AMPDU_TX_OPERATIONAL\n", __func__); break; default: WL_ERROR("%s: Invalid command, ignoring\n", __func__); } return 0; }
static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 tid) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; struct ieee80211_vif *vif = mvmsta->vif; lockdep_assert_held(&mvmsta->lock); if ((tid_data->state == IWL_AGG_ON || tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && iwl_mvm_tid_queued(tid_data) == 0) { /* * Now that this aggregation queue is empty tell mac80211 so it * knows we no longer have frames buffered for the station on * this TID (for the TIM bitmap calculation.) */ ieee80211_sta_set_buffered(sta, tid, false); } if (tid_data->ssn != tid_data->next_reclaimed) return; switch (tid_data->state) { case IWL_EMPTYING_HW_QUEUE_ADDBA: IWL_DEBUG_TX_QUEUES(mvm, "Can continue addBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); tid_data->state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IWL_EMPTYING_HW_QUEUE_DELBA: IWL_DEBUG_TX_QUEUES(mvm, "Can continue DELBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); iwl_mvm_disable_txq(mvm, tid_data->txq_id); tid_data->state = IWL_AGG_OFF; /* * we can't hold the mutex - but since we are after a sequence * point (call to iwl_mvm_disable_txq(), so we don't even need * a memory barrier. */ mvm->queue_to_mac80211[tid_data->txq_id] = IWL_INVALID_MAC80211_QUEUE; ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; default: break; } }
int mt76x02_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params) { enum ieee80211_ampdu_mlme_action action = params->action; struct ieee80211_sta *sta = params->sta; struct mt76x02_dev *dev = hw->priv; struct mt76x02_sta *msta = (struct mt76x02_sta *) sta->drv_priv; struct ieee80211_txq *txq = sta->txq[params->tid]; u16 tid = params->tid; u16 *ssn = ¶ms->ssn; struct mt76_txq *mtxq; if (!txq) return -EINVAL; mtxq = (struct mt76_txq *)txq->drv_priv; switch (action) { case IEEE80211_AMPDU_RX_START: mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, *ssn, params->buf_size); mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); break; case IEEE80211_AMPDU_RX_STOP: mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); break; case IEEE80211_AMPDU_TX_OPERATIONAL: mtxq->aggr = true; mtxq->send_bar = false; ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: mtxq->aggr = false; ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); break; case IEEE80211_AMPDU_TX_START: mtxq->agg_ssn = *ssn << 4; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP_CONT: mtxq->aggr = false; ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; } return 0; }
static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; int ret = 0; struct ieee80211_sta *sta = params->sta; enum ieee80211_ampdu_mlme_action action = params->action; u16 tid = params->tid; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); if (!ret) ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: if (tid >= ATH9K_HTC_MAX_TID) { ret = -EINVAL; break; } ista = (struct ath9k_htc_sta *) sta->drv_priv; spin_lock_bh(&priv->tx.tx_lock); ista->tid_state[tid] = AGGR_OPERATIONAL; spin_unlock_bh(&priv->tx.tx_lock); break; default: ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n"); } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static int wl_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct scb *scb = (struct scb *)sta->drv_priv; struct wl_info *wl = hw->priv; int status; if (WARN_ON(scb->magic != SCB_MAGIC)) return -EIDRM; switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: WL_LOCK(wl); status = wlc_aggregatable(wl->wlc, tid); WL_UNLOCK(wl); if (!status) { wiphy_err(wl->wiphy, "START: tid %d is not agg\'able\n", tid); return -EINVAL; } /* XXX: Use the starting sequence number provided ... */ *ssn = 0; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: WL_LOCK(wl); wlc_ampdu_flush(wl->wlc, sta, tid); WL_UNLOCK(wl); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: /* Not sure what to do here */ /* Power save wakeup */ break; default: wiphy_err(wl->wiphy, "%s: Invalid command, ignoring\n", __func__); } return 0; }
static int mt7603_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params) { enum ieee80211_ampdu_mlme_action action = params->action; struct mt7603_dev *dev = hw->priv; struct ieee80211_sta *sta = params->sta; struct ieee80211_txq *txq = sta->txq[params->tid]; struct mt7603_sta *msta = (struct mt7603_sta *) sta->drv_priv; struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv; u16 tid = params->tid; u16 *ssn = ¶ms->ssn; u8 ba_size = params->buf_size; if (!txq) return -EINVAL; switch (action) { case IEEE80211_AMPDU_RX_START: mt7603_mac_rx_ba_reset(dev, sta->addr, tid); break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_OPERATIONAL: mtxq->aggr = true; mtxq->send_bar = false; mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, *ssn, ba_size); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: mtxq->aggr = false; ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, *ssn, -1); break; case IEEE80211_AMPDU_TX_START: mtxq->agg_ssn = *ssn << 4; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP_CONT: mtxq->aggr = false; mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, *ssn, -1); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; } return 0; }
static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; int ret = 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); if (!ret) ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ista = (struct ath9k_htc_sta *) sta->drv_priv; spin_lock_bh(&priv->tx.tx_lock); ista->tid_state[tid] = AGGR_OPERATIONAL; spin_unlock_bh(&priv->tx.tx_lock); break; default: ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n"); } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 tid) { struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; struct ieee80211_vif *vif = mvmsta->vif; lockdep_assert_held(&mvmsta->lock); if (tid_data->ssn != tid_data->next_reclaimed) return; switch (tid_data->state) { case IWL_EMPTYING_HW_QUEUE_ADDBA: IWL_DEBUG_TX_QUEUES(mvm, "Can continue addBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); tid_data->state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IWL_EMPTYING_HW_QUEUE_DELBA: IWL_DEBUG_TX_QUEUES(mvm, "Can continue DELBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); iwl_trans_txq_disable(mvm->trans, tid_data->txq_id); tid_data->state = IWL_AGG_OFF; /* * we can't hold the mutex - but since we are after a sequence * point (call to iwl_trans_txq_disable), so we don't even need * a memory barrier. */ mvm->queue_to_mac80211[tid_data->txq_id] = IWL_INVALID_MAC80211_QUEUE; ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; default: break; } }
static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) { struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid]; enum iwl_rxon_context_id ctx; struct ieee80211_vif *vif; u8 *addr; lockdep_assert_held(&priv->shrd->sta_lock); addr = priv->stations[sta_id].sta.sta.addr; ctx = priv->stations[sta_id].ctxid; vif = priv->contexts[ctx].vif; switch (priv->tid_data[sta_id][tid].agg.state) { case IWL_EMPTYING_HW_QUEUE_DELBA: /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, "Can continue DELBA flow ssn = next_recl =" " %d", tid_data->next_reclaimed); iwl_trans_tx_agg_disable(trans(priv), sta_id, tid); tid_data->agg.state = IWL_AGG_OFF; ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); } break; case IWL_EMPTYING_HW_QUEUE_ADDBA: /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, "Can continue ADDBA flow ssn = next_recl =" " %d", tid_data->next_reclaimed); tid_data->agg.state = IWL_AGG_ON; ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); } break; default: break; } }
static int mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta,u16 tid, u16 *ssn, u8 buf_size) { struct mt76_dev *dev = hw->priv; struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; struct ieee80211_txq *txq = sta->txq[tid]; struct mt76_txq *mtxq = (struct mt76_txq *) txq->drv_priv; if (!txq) return -EINVAL; switch (action) { case IEEE80211_AMPDU_RX_START: mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx)+4, BIT(16 + tid)); break; case IEEE80211_AMPDU_RX_STOP: mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx)+4, BIT(16 + tid)); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ieee80211_send_bar(vif, sta->addr, tid, cpu_to_le16(mtxq->agg_ssn)); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: break; case IEEE80211_AMPDU_TX_START: mtxq->agg_ssn = *ssn << 4; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP_CONT: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; } return 0; }
static int mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params) { struct mt76x0_dev *dev = hw->priv; struct ieee80211_sta *sta = params->sta; enum ieee80211_ampdu_mlme_action action = params->action; u16 tid = params->tid; u16 *ssn = ¶ms->ssn; struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; WARN_ON(msta->wcid.idx > N_WCIDS); switch (action) { case IEEE80211_AMPDU_RX_START: mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); break; case IEEE80211_AMPDU_RX_STOP: mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ieee80211_send_bar(vif, sta->addr, tid, msta->agg_ssn[tid]); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: break; case IEEE80211_AMPDU_TX_START: msta->agg_ssn[tid] = *ssn << 4; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP_CONT: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; } return 0; }
static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, bool amsdu) #endif { int rc = 0; struct mwl_priv *priv = hw->priv; struct mwl_ampdu_stream *stream; u8 *addr = sta->addr, idx; struct mwl_sta *sta_info; sta_info = mwl_dev_get_sta(sta); spin_lock_bh(&priv->stream_lock); stream = mwl_fwcmd_lookup_stream(hw, addr, tid); switch (action) { case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: if (!sta_info->is_ampdu_allowed) { wiphy_warn(hw->wiphy, "ampdu not allowed\n"); rc = -EPERM; break; } if (!stream) { stream = mwl_fwcmd_add_stream(hw, sta, tid); if (!stream) { wiphy_warn(hw->wiphy, "no stream found\n"); rc = -EPERM; break; } } spin_unlock_bh(&priv->stream_lock); rc = mwl_fwcmd_check_ba(hw, stream, vif); spin_lock_bh(&priv->stream_lock); if (rc) { mwl_fwcmd_remove_stream(hw, stream); wiphy_err(hw->wiphy, "ampdu start error code: %d\n", rc); rc = -EPERM; break; } stream->state = AMPDU_STREAM_IN_PROGRESS; *ssn = 0; ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); break; case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: if (stream) { if (stream->state == AMPDU_STREAM_ACTIVE) { mwl_tx_del_ampdu_pkts(hw, sta, tid); idx = stream->idx; spin_unlock_bh(&priv->stream_lock); mwl_fwcmd_destroy_ba(hw, idx); spin_lock_bh(&priv->stream_lock); } mwl_fwcmd_remove_stream(hw, stream); ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); } else { rc = -EPERM; } break; case IEEE80211_AMPDU_TX_OPERATIONAL: if (stream) { if (WARN_ON(stream->state != AMPDU_STREAM_IN_PROGRESS)) { rc = -EPERM; break; } spin_unlock_bh(&priv->stream_lock); rc = mwl_fwcmd_create_ba(hw, stream, buf_size, vif); spin_lock_bh(&priv->stream_lock); if (!rc) { stream->state = AMPDU_STREAM_ACTIVE; } else { idx = stream->idx; spin_unlock_bh(&priv->stream_lock); mwl_fwcmd_destroy_ba(hw, idx); spin_lock_bh(&priv->stream_lock); mwl_fwcmd_remove_stream(hw, stream); wiphy_err(hw->wiphy, "ampdu operation error code: %d\n", rc); } } else { rc = -EPERM; } break; default: rc = -ENOTSUPP; break; } spin_unlock_bh(&priv->stream_lock); return rc; }
int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid) { struct iwl_tid_data *tid_data; unsigned long flags; int sta_id; sta_id = iwl_sta_id(sta); if (sta_id == IWL_INVALID_STATION) { IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); return -ENXIO; } spin_lock_irqsave(&priv->shrd->sta_lock, flags); tid_data = &priv->tid_data[sta_id][tid]; switch (priv->tid_data[sta_id][tid].agg.state) { case IWL_EMPTYING_HW_QUEUE_ADDBA: /* * This can happen if the peer stops aggregation * again before we've had a chance to drain the * queue we selected previously, i.e. before the * session was really started completely. */ IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); goto turn_off; case IWL_AGG_ON: break; default: IWL_WARN(priv, "Stopping AGG while state not ON " "or starting for %d on %d (%d)\n", sta_id, tid, priv->tid_data[sta_id][tid].agg.state); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); return 0; } tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); /* There are still packets for this RA / TID in the HW */ if (tid_data->agg.ssn != tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " "next_recl = %d", tid_data->agg.ssn, tid_data->next_reclaimed); priv->tid_data[sta_id][tid].agg.state = IWL_EMPTYING_HW_QUEUE_DELBA; spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); return 0; } IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d", tid_data->agg.ssn); turn_off: priv->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF; /* do not restore/save irqs */ spin_unlock(&priv->shrd->sta_lock); spin_lock(&priv->shrd->lock); iwl_trans_tx_agg_disable(trans(priv), sta_id, tid); spin_unlock_irqrestore(&priv->shrd->lock, flags); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); return 0; }