/* 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; }
static void iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in, struct iwm_mac_ctx_cmd *cmd, uint32_t action) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni = vap->iv_bss; int cck_ack_rates, ofdm_ack_rates; int i; int is2ghz; /* * id is the MAC address ID - something to do with MAC filtering. * color - not sure. * * These are both functions of the vap, not of the node. * So, for now, hard-code both to 0 (default). */ cmd->id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID, IWM_DEFAULT_COLOR)); cmd->action = htole32(action); cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA); /* * The TSF ID is one of four TSF tracking resources in the firmware. * Read the iwlwifi/mvm code for more details. * * For now, just hard-code it to TSF tracking ID 0; we only support * a single STA mode VAP. * * It's per-vap, not per-node. */ cmd->tsf_id = htole32(IWM_DEFAULT_TSFID); IEEE80211_ADDR_COPY(cmd->node_addr, ic->ic_macaddr); /* * XXX should we error out if in_assoc is 1 and ni == NULL? */ if (in->in_assoc) { IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid); } else { /* eth broadcast address */ memset(cmd->bssid_addr, 0xff, sizeof(cmd->bssid_addr)); } /* * Default to 2ghz if no node information is given. */ if (in) { is2ghz = !! IEEE80211_IS_CHAN_2GHZ(in->in_ni.ni_chan); } else { is2ghz = 1; } iwm_mvm_ack_rates(sc, is2ghz, &cck_ack_rates, &ofdm_ack_rates); cmd->cck_rates = htole32(cck_ack_rates); cmd->ofdm_rates = htole32(ofdm_ack_rates); cmd->cck_short_preamble = htole32((ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? IWM_MAC_FLG_SHORT_PREAMBLE : 0); cmd->short_slot = htole32((ic->ic_flags & IEEE80211_F_SHSLOT) ? IWM_MAC_FLG_SHORT_SLOT : 0); /* XXX TODO: set wme parameters; also handle getting updated wme parameters */ for (i = 0; i < IWM_AC_NUM+1; i++) { int txf = i; cmd->ac[txf].cw_min = htole16(0x0f); cmd->ac[txf].cw_max = htole16(0x3f); cmd->ac[txf].aifsn = 1; cmd->ac[txf].fifos_mask = (1 << txf); cmd->ac[txf].edca_txop = 0; } if (ic->ic_flags & IEEE80211_F_USEPROT) cmd->protection_flags |= htole32(IWM_MAC_PROT_FLG_TGG_PROTECT); cmd->filter_flags = htole32(IWM_MAC_FILTER_ACCEPT_GRP); }