int iwl_legacy_send_add_sta(struct iwl_priv *priv, struct iwl_legacy_addsta_cmd *sta, u8 flags) { struct iwl_rx_packet *pkt = NULL; int ret = 0; u8 data[sizeof(*sta)]; struct iwl_host_cmd cmd = { .id = REPLY_ADD_STA, .flags = flags, .data = data, }; u8 sta_id __maybe_unused = sta->sta.sta_id; IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); if (flags & CMD_ASYNC) cmd.callback = iwl_legacy_add_sta_callback; else { cmd.flags |= CMD_WANT_SKB; might_sleep(); } cmd.len = priv->cfg->ops->utils->build_addsta_hcmd(sta, data); ret = iwl_legacy_send_cmd(priv, &cmd); if (ret || (flags & CMD_ASYNC)) return ret; if (ret == 0) { pkt = (struct iwl_rx_packet *)cmd.reply_page; ret = iwl_legacy_process_add_sta_resp(priv, sta, pkt, true); } iwl_legacy_free_pages(priv, cmd.reply_page); return ret; }
static int iwl_legacy_send_remove_station(struct iwl_priv *priv, const u8 *addr, int sta_id, bool temporary) { struct iwl_rx_packet *pkt; int ret; unsigned long flags_spin; struct iwl_rem_sta_cmd rm_sta_cmd; struct iwl_host_cmd cmd = { .id = REPLY_REMOVE_STA, .len = sizeof(struct iwl_rem_sta_cmd), .flags = CMD_SYNC, .data = &rm_sta_cmd, }; memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); rm_sta_cmd.num_sta = 1; memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); cmd.flags |= CMD_WANT_SKB; ret = iwl_legacy_send_cmd(priv, &cmd); if (ret) return ret; pkt = (struct iwl_rx_packet *)cmd.reply_page; if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", pkt->hdr.flags); ret = -EIO; } if (!ret) { switch (pkt->u.rem_sta.status) { case REM_STA_SUCCESS_MSK: if (!temporary) { spin_lock_irqsave(&priv->sta_lock, flags_spin); iwl_legacy_sta_ucode_deactivate(priv, sta_id); spin_unlock_irqrestore(&priv->sta_lock, flags_spin); } IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); break; default: ret = -EIO; IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); break; } } iwl_legacy_free_pages(priv, cmd.reply_page); return ret; } /** * iwl_legacy_remove_station - Remove driver's knowledge of station. */ int iwl_legacy_remove_station(struct iwl_priv *priv, const u8 sta_id, const u8 *addr) { unsigned long flags; if (!iwl_legacy_is_ready(priv)) { IWL_DEBUG_INFO(priv, "Unable to remove station %pM, device not ready.\n", addr); /* * It is typical for stations to be removed when we are * going down. Return success since device will be down * soon anyway */ return 0; } IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", sta_id, addr); if (WARN_ON(sta_id == IWL_INVALID_STATION)) return -EINVAL; spin_lock_irqsave(&priv->sta_lock, flags); if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", addr); goto out_err; } if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", addr); goto out_err; } if (priv->stations[sta_id].used & IWL_STA_LOCAL) { kfree(priv->stations[sta_id].lq); priv->stations[sta_id].lq = NULL; } priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; priv->num_stations--; BUG_ON(priv->num_stations < 0); spin_unlock_irqrestore(&priv->sta_lock, flags); return iwl_legacy_send_remove_station(priv, addr, sta_id, false); out_err: spin_unlock_irqrestore(&priv->sta_lock, flags); return -EINVAL; }
int iwl_legacy_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { int cmd_idx; int ret; BUG_ON(cmd->flags & CMD_ASYNC); /* A synchronous command can not have a callback set. */ BUG_ON(cmd->callback); IWL_DEBUG_INFO(priv, "Attempting to send sync command %s\n", iwl_legacy_get_cmd_string(cmd->id)); mutex_lock(&priv->sync_cmd_mutex); set_bit(STATUS_HCMD_ACTIVE, &priv->status); IWL_DEBUG_INFO(priv, "Setting HCMD_ACTIVE for command %s\n", iwl_legacy_get_cmd_string(cmd->id)); cmd_idx = iwl_legacy_enqueue_hcmd(priv, cmd); if (cmd_idx < 0) { ret = cmd_idx; IWL_ERR(priv, "Error sending %s: enqueue_hcmd failed: %d\n", iwl_legacy_get_cmd_string(cmd->id), ret); goto out; } ret = wait_event_interruptible_timeout(priv->wait_command_queue, !test_bit(STATUS_HCMD_ACTIVE, &priv->status), HOST_COMPLETE_TIMEOUT); if (!ret) { if (test_bit(STATUS_HCMD_ACTIVE, &priv->status)) { IWL_ERR(priv, "Error sending %s: time out after %dms.\n", iwl_legacy_get_cmd_string(cmd->id), jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); clear_bit(STATUS_HCMD_ACTIVE, &priv->status); IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s\n", iwl_legacy_get_cmd_string(cmd->id)); ret = -ETIMEDOUT; goto cancel; } } if (test_bit(STATUS_RF_KILL_HW, &priv->status)) { IWL_ERR(priv, "Command %s aborted: RF KILL Switch\n", iwl_legacy_get_cmd_string(cmd->id)); ret = -ECANCELED; goto fail; } if (test_bit(STATUS_FW_ERROR, &priv->status)) { IWL_ERR(priv, "Command %s failed: FW Error\n", iwl_legacy_get_cmd_string(cmd->id)); ret = -EIO; goto fail; } if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { IWL_ERR(priv, "Error: Response NULL in '%s'\n", iwl_legacy_get_cmd_string(cmd->id)); ret = -EIO; goto cancel; } ret = 0; goto out; cancel: if (cmd->flags & CMD_WANT_SKB) { /* * Cancel the CMD_WANT_SKB flag for the cmd in the * TX cmd queue. Otherwise in case the cmd comes * in later, it will possibly set an invalid * address (cmd->meta.source). */ priv->txq[priv->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB; } fail: if (cmd->reply_page) { iwl_legacy_free_pages(priv, cmd->reply_page); cmd->reply_page = 0; } out: mutex_unlock(&priv->sync_cmd_mutex); return ret; }