/** * iwl_add_station_common - */ int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr, bool is_ap, struct ieee80211_sta *sta, u8 *sta_id_r) { unsigned long flags_spin; int ret = 0; u8 sta_id; struct iwl_addsta_cmd sta_cmd; *sta_id_r = 0; spin_lock_irqsave(&priv->shrd->sta_lock, flags_spin); sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta); if (sta_id == IWL_INVALID_STATION) { IWL_ERR(priv, "Unable to prepare station %pM for addition\n", addr); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin); return -EINVAL; } /* * uCode is not able to deal with multiple requests to add a * station. Keep track if one is in progress so that we do not send * another. */ if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { IWL_DEBUG_INFO(priv, "STA %d already in process of being " "added.\n", sta_id); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin); return -EEXIST; } if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " "adding again.\n", sta_id, addr); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin); return -EEXIST; } priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin); /* Add station to device's station table */ ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); if (ret) { spin_lock_irqsave(&priv->shrd->sta_lock, flags_spin); IWL_ERR(priv, "Adding station %pM failed.\n", priv->stations[sta_id].sta.sta.addr); priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; spin_unlock_irqrestore(&priv->shrd->sta_lock, flags_spin); } *sta_id_r = sta_id; return ret; }
static int iwlagn_send_sta_key(struct iwl_priv *priv, struct ieee80211_key_conf *keyconf, u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags) { unsigned long flags; __le16 key_flags; struct iwl_addsta_cmd sta_cmd; int i; spin_lock_irqsave(&priv->shrd->sta_lock, flags); memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); key_flags |= STA_KEY_FLG_MAP_KEY_MSK; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_CCMP: key_flags |= STA_KEY_FLG_CCMP; memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); break; case WLAN_CIPHER_SUITE_TKIP: key_flags |= STA_KEY_FLG_TKIP; sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; for (i = 0; i < 5; i++) sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); break; case WLAN_CIPHER_SUITE_WEP104: key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; /* fall through */ case WLAN_CIPHER_SUITE_WEP40: key_flags |= STA_KEY_FLG_WEP; memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen); break; default: WARN_ON(1); return -EINVAL; } if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) key_flags |= STA_KEY_MULTICAST_MSK; /* key pointer (offset) */ sta_cmd.key.key_offset = keyconf->hw_key_idx; sta_cmd.key.key_flags = key_flags; sta_cmd.mode = STA_CONTROL_MODIFY_MSK; sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; return iwl_send_add_sta(priv, &sta_cmd, cmd_flags); }
void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { unsigned long flags; int sta_id = ctx->ap_sta_id; int ret; struct iwl_addsta_cmd sta_cmd; struct iwl_link_quality_cmd lq; bool active; spin_lock_irqsave(&priv->sta_lock, flags); if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { spin_unlock_irqrestore(&priv->sta_lock, flags); return; } memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); sta_cmd.mode = 0; memcpy(&lq, priv->stations[sta_id].lq, sizeof(lq)); active = priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE; priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; spin_unlock_irqrestore(&priv->sta_lock, flags); if (active) { ret = iwl_send_remove_station( priv, priv->stations[sta_id].sta.sta.addr, sta_id, true); if (ret) IWL_ERR(priv, "failed to remove STA %pM (%d)\n", priv->stations[sta_id].sta.sta.addr, ret); } spin_lock_irqsave(&priv->sta_lock, flags); priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE; spin_unlock_irqrestore(&priv->sta_lock, flags); ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); if (ret) IWL_ERR(priv, "failed to re-add STA %pM (%d)\n", priv->stations[sta_id].sta.sta.addr, ret); iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true); }
int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta) { unsigned long flags; struct iwl_addsta_cmd sta_cmd; u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); /* if station isn't there, neither is the key */ if (sta_id == IWL_INVALID_STATION) return -ENOENT; spin_lock_irqsave(&priv->shrd->sta_lock, flags); memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) sta_id = IWL_INVALID_STATION; spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); if (sta_id == IWL_INVALID_STATION) return 0; lockdep_assert_held(&priv->shrd->mutex); ctx->key_mapping_keys--; IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id); if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table)) IWL_ERR(priv, "offset %d not used in uCode key table.\n", keyconf->hw_key_idx); sta_cmd.key.key_flags = STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID; sta_cmd.key.key_offset = WEP_INVALID_OFFSET; sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; sta_cmd.mode = STA_CONTROL_MODIFY_MSK; return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); }
/** * iwl_restore_stations() - Restore driver known stations to device * * All stations considered active by driver, but not present in ucode, is * restored. * * Function sleeps. */ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { struct iwl_addsta_cmd sta_cmd; struct iwl_link_quality_cmd lq; unsigned long flags_spin; int i; bool found = false; int ret; bool send_lq; if (!iwl_is_ready(priv)) { IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n"); return; } IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); spin_lock_irqsave(&priv->sta_lock, flags_spin); for (i = 0; i < priv->hw_params.max_stations; i++) { if (ctx->ctxid != priv->stations[i].ctxid) continue; if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", priv->stations[i].sta.sta.addr); priv->stations[i].sta.mode = 0; priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; found = true; } } for (i = 0; i < priv->hw_params.max_stations; i++) { if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { memcpy(&sta_cmd, &priv->stations[i].sta, sizeof(struct iwl_addsta_cmd)); send_lq = false; if (priv->stations[i].lq) { memcpy(&lq, priv->stations[i].lq, sizeof(struct iwl_link_quality_cmd)); send_lq = true; } spin_unlock_irqrestore(&priv->sta_lock, flags_spin); ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); if (ret) { spin_lock_irqsave(&priv->sta_lock, flags_spin); IWL_ERR(priv, "Adding station %pM failed.\n", priv->stations[i].sta.sta.addr); priv->stations[i].used &= ~IWL_STA_DRIVER_ACTIVE; priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; spin_unlock_irqrestore(&priv->sta_lock, flags_spin); } /* * Rate scaling has already been initialized, send * current LQ command */ if (send_lq) iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true); spin_lock_irqsave(&priv->sta_lock, flags_spin); priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; } } spin_unlock_irqrestore(&priv->sta_lock, flags_spin); if (!found) IWL_DEBUG_INFO(priv, "Restoring all known stations .... no stations to be restored.\n"); else IWL_DEBUG_INFO(priv, "Restoring all known stations .... complete.\n"); }