void iwlagn_dev_txfifo_flush(struct iwl_priv *priv) { mutex_lock(&priv->mutex); ieee80211_stop_queues(priv->hw); if (iwlagn_txfifo_flush(priv, 0)) { IWL_ERR(priv, "flush request fail\n"); goto done; } IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff); done: ieee80211_wake_queues(priv->hw); mutex_unlock(&priv->mutex); }
/* send station add/update command to firmware */ int iwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, boolean_t update) { struct iwm_vap *ivp = IWM_VAP(in->in_ni.ni_vap); struct iwm_mvm_add_sta_cmd add_sta_cmd = { .sta_id = IWM_STATION_ID, .mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color)), .add_modify = update ? 1 : 0, .station_flags_msk = htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK), .tid_disable_tx = htole16(0xffff), }; int ret; uint32_t status; uint32_t agg_size = 0, mpdu_dens = 0; if (!update) { int ac; for (ac = 0; ac < WME_NUM_AC; ac++) { add_sta_cmd.tfd_queue_msk |= htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]); } IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid); } add_sta_cmd.station_flags |= htole32(agg_size << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT); add_sta_cmd.station_flags |= htole32(mpdu_dens << IWM_STA_FLG_AGG_MPDU_DENS_SHIFT); status = IWM_ADD_STA_SUCCESS; ret = iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, iwm_mvm_add_sta_cmd_size(sc), &add_sta_cmd, &status); if (ret) return ret; switch (status & IWM_ADD_STA_STATUS_MASK) { case IWM_ADD_STA_SUCCESS: IWM_DPRINTF(sc, IWM_DEBUG_NODE, "IWM_ADD_STA PASSED\n"); break; default: ret = EIO; device_printf(sc->sc_dev, "IWM_ADD_STA failed\n"); break; } return ret; } int iwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in) { return iwm_mvm_sta_send_to_fw(sc, in, FALSE); } int iwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in) { return iwm_mvm_sta_send_to_fw(sc, in, TRUE); } int iwm_mvm_drain_sta(struct iwm_softc *sc, struct iwm_vap *ivp, boolean_t drain) { struct iwm_mvm_add_sta_cmd cmd = {}; int ret; uint32_t status; cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color)); cmd.sta_id = IWM_STATION_ID; cmd.add_modify = IWM_STA_MODE_MODIFY; cmd.station_flags = drain ? htole32(IWM_STA_FLG_DRAIN_FLOW) : 0; cmd.station_flags_msk = htole32(IWM_STA_FLG_DRAIN_FLOW); status = IWM_ADD_STA_SUCCESS; ret = iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, iwm_mvm_add_sta_cmd_size(sc), &cmd, &status); if (ret) return ret; switch (status & IWM_ADD_STA_STATUS_MASK) { case IWM_ADD_STA_SUCCESS: IWM_DPRINTF(sc, IWM_DEBUG_NODE, "Frames for staid %d will drained in fw\n", IWM_STATION_ID); break; default: ret = EIO; device_printf(sc->sc_dev, "Couldn't drain frames for staid %d\n", IWM_STATION_ID); break; } return ret; } /* * Remove a station from the FW table. Before sending the command to remove * the station validate that the station is indeed known to the driver (sanity * only). */ static int iwm_mvm_rm_sta_common(struct iwm_softc *sc) { struct iwm_mvm_rm_sta_cmd rm_sta_cmd = { .sta_id = IWM_STATION_ID, }; int ret; ret = iwm_mvm_send_cmd_pdu(sc, IWM_REMOVE_STA, 0, sizeof(rm_sta_cmd), &rm_sta_cmd); if (ret) { device_printf(sc->sc_dev, "Failed to remove station. Id=%d\n", IWM_STATION_ID); return ret; } return 0; } int iwm_mvm_rm_sta(struct iwm_softc *sc, struct ieee80211vap *vap, boolean_t is_assoc) { uint32_t tfd_queue_msk = 0; int ret; int ac; ret = iwm_mvm_drain_sta(sc, IWM_VAP(vap), TRUE); if (ret) return ret; for (ac = 0; ac < WME_NUM_AC; ac++) { tfd_queue_msk |= htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]); } ret = iwm_mvm_flush_tx_path(sc, tfd_queue_msk, 0); if (ret) return ret; #ifdef notyet /* function not yet implemented */ ret = iwl_trans_wait_tx_queue_empty(mvm->trans, mvm_sta->tfd_queue_msk); if (ret) return ret; #endif ret = iwm_mvm_drain_sta(sc, IWM_VAP(vap), FALSE); /* if we are associated - we can't remove the AP STA now */ if (is_assoc) return ret; /* XXX wait until STA is drained */ ret = iwm_mvm_rm_sta_common(sc); return ret; }