int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) { struct iwl_tx_ant_config_cmd tx_ant_cmd = { .valid = cpu_to_le32(valid_tx_ant), }; if (IWL_UCODE_API(priv->ucode_ver) > 1) { IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant); return iwl_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, sizeof(struct iwl_tx_ant_config_cmd), &tx_ant_cmd); } else { IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n"); return -EOPNOTSUPP; } }
static int iwl_generic_cmd_callback(struct iwl_priv *priv, struct iwl_cmd *cmd, struct sk_buff *skb) { struct iwl_rx_packet *pkt = NULL; if (!skb) { IWL_ERR(priv, "Error: Response NULL in %s.\n", get_cmd_string(cmd->hdr.cmd)); return 1; } pkt = (struct iwl_rx_packet *)skb->data; if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); return 1; } #ifdef CONFIG_IWLWIFI_DEBUG switch (cmd->hdr.cmd) { case REPLY_TX_LINK_QUALITY_CMD: case SENSITIVITY_CMD: IWL_DEBUG_HC_DUMP(priv, "back from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); break; default: IWL_DEBUG_HC(priv, "back from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); } #endif /* Let iwl_tx_complete free the response skb */ return 1; }
static void iwl_generic_cmd_callback(struct iwl_priv *priv, struct iwl_device_cmd *cmd, struct iwl_rx_packet *pkt) { if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); return; } #ifdef CONFIG_IWLWIFI_DEBUG switch (cmd->hdr.cmd) { case REPLY_TX_LINK_QUALITY_CMD: case SENSITIVITY_CMD: IWL_DEBUG_HC_DUMP(priv, "back from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); break; default: IWL_DEBUG_HC(priv, "back from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); } #endif }
static int iwl_generic_cmd_callback(struct iwl_priv *priv, struct iwl_cmd *cmd, struct sk_buff *skb) { struct iwl4965_rx_packet *pkt = NULL; if (!skb) { IWL_ERROR("Error: Response NULL in %s.\n", get_cmd_string(cmd->hdr.cmd)); return 1; } pkt = (struct iwl4965_rx_packet *)skb->data; if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERROR("Bad return from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); return 1; } IWL_DEBUG_HC("back from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); /* Let iwl_tx_complete free the response skb */ return 1; }
static void iwl_bg_request_scan(struct work_struct *data) { struct iwl_priv *priv = container_of(data, struct iwl_priv, request_scan); struct iwl_host_cmd cmd = { .id = REPLY_SCAN_CMD, .len = sizeof(struct iwl_scan_cmd), .meta.flags = CMD_SIZE_HUGE, }; struct iwl_scan_cmd *scan; struct ieee80211_conf *conf = NULL; int ret = 0; u32 tx_ant; u16 cmd_len; enum ieee80211_band band; u8 n_probes = 2; u8 rx_chain = priv->hw_params.valid_rx_ant; conf = ieee80211_get_hw_conf(priv->hw); mutex_lock(&priv->mutex); if (!iwl_is_ready(priv)) { IWL_WARNING("request scan called when driver not ready.\n"); goto done; } /* Make sure the scan wasn't cancelled before this queued work * was given the chance to run... */ if (!test_bit(STATUS_SCANNING, &priv->status)) goto done; /* This should never be called or scheduled if there is currently * a scan active in the hardware. */ if (test_bit(STATUS_SCAN_HW, &priv->status)) { IWL_DEBUG_INFO("Multiple concurrent scan requests in parallel. " "Ignoring second request.\n"); ret = -EIO; goto done; } if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { IWL_DEBUG_SCAN("Aborting scan due to device shutdown\n"); goto done; } if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { IWL_DEBUG_HC("Scan request while abort pending. Queuing.\n"); goto done; } if (iwl_is_rfkill(priv)) { IWL_DEBUG_HC("Aborting scan due to RF Kill activation\n"); goto done; } if (!test_bit(STATUS_READY, &priv->status)) { IWL_DEBUG_HC("Scan request while uninitialized. Queuing.\n"); goto done; } if (!priv->scan_bands) { IWL_DEBUG_HC("Aborting scan due to no requested bands\n"); goto done; } if (!priv->scan) { priv->scan = kmalloc(sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE, GFP_KERNEL); if (!priv->scan) { ret = -ENOMEM; goto done; } } scan = priv->scan; memset(scan, 0, sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE); scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; scan->quiet_time = IWL_ACTIVE_QUIET_TIME; if (iwl_is_associated(priv)) { u16 interval = 0; u32 extra; u32 suspend_time = 100; u32 scan_suspend_time = 100; unsigned long flags; IWL_DEBUG_INFO("Scanning while associated...\n"); spin_lock_irqsave(&priv->lock, flags); interval = priv->beacon_int; spin_unlock_irqrestore(&priv->lock, flags); scan->suspend_time = 0; scan->max_out_time = cpu_to_le32(200 * 1024); if (!interval) interval = suspend_time; extra = (suspend_time / interval) << 22; scan_suspend_time = (extra | ((suspend_time % interval) * 1024)); scan->suspend_time = cpu_to_le32(scan_suspend_time); IWL_DEBUG_SCAN("suspend_time 0x%X beacon interval %d\n", scan_suspend_time, interval); } /* We should add the ability for user to lock to PASSIVE ONLY */ if (priv->one_direct_scan) { IWL_DEBUG_SCAN("Start direct scan for '%s'\n", iwl_escape_essid(priv->direct_ssid, priv->direct_ssid_len)); scan->direct_scan[0].id = WLAN_EID_SSID; scan->direct_scan[0].len = priv->direct_ssid_len; memcpy(scan->direct_scan[0].ssid, priv->direct_ssid, priv->direct_ssid_len); n_probes++; } else if (!iwl_is_associated(priv) && priv->essid_len) { IWL_DEBUG_SCAN("Start direct scan for '%s' (not associated)\n", iwl_escape_essid(priv->essid, priv->essid_len)); scan->direct_scan[0].id = WLAN_EID_SSID; scan->direct_scan[0].len = priv->essid_len; memcpy(scan->direct_scan[0].ssid, priv->essid, priv->essid_len); n_probes++; } else { IWL_DEBUG_SCAN("Start indirect scan.\n"); } scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; scan->tx_cmd.sta_id = priv->hw_params.bcast_sta_id; scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) { band = IEEE80211_BAND_2GHZ; scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; tx_ant = iwl_scan_tx_ant(priv, band); if (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(IWL_RATE_6M_PLCP, tx_ant); else scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(IWL_RATE_1M_PLCP, tx_ant | RATE_MCS_CCK_MSK); scan->good_CRC_th = 0; } else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) { band = IEEE80211_BAND_5GHZ; tx_ant = iwl_scan_tx_ant(priv, band); scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(IWL_RATE_6M_PLCP, tx_ant); scan->good_CRC_th = IWL_GOOD_CRC_TH; /* Force use of chains B and C (0x6) for scan Rx for 4965 * Avoid A (0x1) because of its off-channel reception on A-band. * MIMO is not used here, but value is required */ if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) rx_chain = 0x6; } else { IWL_WARNING("Invalid scan band count\n"); goto done; } scan->rx_chain = RXON_RX_CHAIN_DRIVER_FORCE_MSK | cpu_to_le16((0x7 << RXON_RX_CHAIN_VALID_POS) | (rx_chain << RXON_RX_CHAIN_FORCE_SEL_POS) | (0x7 << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS)); cmd_len = iwl_fill_probe_req(priv, band, (struct ieee80211_mgmt *)scan->data, IWL_MAX_SCAN_SIZE - sizeof(*scan)); scan->tx_cmd.len = cpu_to_le16(cmd_len); if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) scan->filter_flags = RXON_FILTER_PROMISC_MSK; scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK); scan->channel_count = iwl_get_channels_for_scan(priv, band, 1, /* active */ n_probes, (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]); if (scan->channel_count == 0) { IWL_DEBUG_SCAN("channel count %d\n", scan->channel_count); goto done; } cmd.len += le16_to_cpu(scan->tx_cmd.len) + scan->channel_count * sizeof(struct iwl_scan_channel); cmd.data = scan; scan->len = cpu_to_le16(cmd.len); set_bit(STATUS_SCAN_HW, &priv->status); ret = iwl_send_cmd_sync(priv, &cmd); if (ret) goto done; queue_delayed_work(priv->workqueue, &priv->scan_check, IWL_SCAN_CHECK_WATCHDOG); mutex_unlock(&priv->mutex); return; done: /* inform mac80211 scan aborted */ queue_work(priv->workqueue, &priv->scan_completed); mutex_unlock(&priv->mutex); } static void iwl_bg_abort_scan(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); if (!iwl_is_ready(priv)) return; mutex_lock(&priv->mutex); set_bit(STATUS_SCAN_ABORTING, &priv->status); iwl_send_scan_abort(priv); mutex_unlock(&priv->mutex); } void iwl_setup_scan_deferred_work(struct iwl_priv *priv) { /* FIXME: move here when resolved PENDING * INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); */ INIT_WORK(&priv->request_scan, iwl_bg_request_scan); INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan); INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check); }
/** * iwl_enqueue_hcmd - enqueue a uCode command * @priv: device private data point * @cmd: a point to the ucode command structure * * The function returns < 0 values to indicate the operation is * failed. On success, it turns the index (> 0) of command in the * command queue. */ static int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; struct iwl_queue *q = &txq->q; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; unsigned long flags; u32 idx; u16 copy_size, cmd_size; bool is_ct_kill = false; bool had_nocopy = false; int i; u8 *cmd_dest; #ifdef CONFIG_IWLWIFI_DEVICE_TRACING const void *trace_bufs[IWL_MAX_CMD_TFDS + 1] = {}; int trace_lens[IWL_MAX_CMD_TFDS + 1] = {}; int trace_idx; #endif if (test_bit(STATUS_FW_ERROR, &priv->status)) { IWL_WARN(priv, "fw recovery, no hcmd send\n"); return -EIO; } if ((priv->ucode_owner == IWL_OWNERSHIP_TM) && !(cmd->flags & CMD_ON_DEMAND)) { IWL_DEBUG_HC(priv, "tm own the uCode, no regular hcmd send\n"); return -EIO; } copy_size = sizeof(out_cmd->hdr); cmd_size = sizeof(out_cmd->hdr); /* need one for the header if the first is NOCOPY */ BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { had_nocopy = true; } else { /* NOCOPY must not be followed by normal! */ if (WARN_ON(had_nocopy)) return -EINVAL; copy_size += cmd->len[i]; } cmd_size += cmd->len[i]; } /* * If any of the command structures end up being larger than * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically * allocated into separate TFDs, then we will need to * increase the size of the buffers. */ if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE)) return -EINVAL; if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { IWL_WARN(priv, "Not sending command - %s KILL\n", iwl_is_rfkill(priv) ? "RF" : "CT"); return -EIO; } spin_lock_irqsave(&priv->hcmd_lock, flags); if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { spin_unlock_irqrestore(&priv->hcmd_lock, flags); IWL_ERR(priv, "No space in command queue\n"); is_ct_kill = iwl_check_for_ct_kill(priv); if (!is_ct_kill) { IWL_ERR(priv, "Restarting adapter due to queue full\n"); iwlagn_fw_error(priv, false); } return -ENOSPC; } idx = get_cmd_index(q, q->write_ptr); out_cmd = txq->cmd[idx]; out_meta = &txq->meta[idx]; memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ if (cmd->flags & CMD_WANT_SKB) out_meta->source = cmd; if (cmd->flags & CMD_ASYNC) out_meta->callback = cmd->callback; /* set up the header */ out_cmd->hdr.cmd = cmd->id; out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(priv->cmd_queue) | INDEX_TO_SEQ(q->write_ptr)); /* and copy the data that needs to be copied */ cmd_dest = &out_cmd->cmd.payload[0]; for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) break; memcpy(cmd_dest, cmd->data[i], cmd->len[i]); cmd_dest += cmd->len[i]; } IWL_DEBUG_HC(priv, "Sending command %s (#%x), seq: 0x%04X, " "%d bytes at %d[%d]:%d\n", get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, priv->cmd_queue); phys_addr = dma_map_single(priv->bus->dev, &out_cmd->hdr, copy_size, DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(priv->bus->dev, phys_addr))) { idx = -ENOMEM; goto out; } dma_unmap_addr_set(out_meta, mapping, phys_addr); dma_unmap_len_set(out_meta, len, copy_size); iwlagn_txq_attach_buf_to_tfd(priv, txq, phys_addr, copy_size, 1); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_bufs[0] = &out_cmd->hdr; trace_lens[0] = copy_size; trace_idx = 1; #endif for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) continue; phys_addr = dma_map_single(priv->bus->dev, (void *)cmd->data[i], cmd->len[i], DMA_BIDIRECTIONAL); if (dma_mapping_error(priv->bus->dev, phys_addr)) { iwlagn_unmap_tfd(priv, out_meta, &txq->tfds[q->write_ptr], DMA_BIDIRECTIONAL); idx = -ENOMEM; goto out; } iwlagn_txq_attach_buf_to_tfd(priv, txq, phys_addr, cmd->len[i], 0); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_bufs[trace_idx] = cmd->data[i]; trace_lens[trace_idx] = cmd->len[i]; trace_idx++; #endif } out_meta->flags = cmd->flags; txq->need_update = 1; /* check that tracing gets all possible blocks */ BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_iwlwifi_dev_hcmd(priv, cmd->flags, trace_bufs[0], trace_lens[0], trace_bufs[1], trace_lens[1], trace_bufs[2], trace_lens[2]); #endif /* Increment and update queue's write index */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); iwl_txq_update_write_ptr(priv, txq); out: spin_unlock_irqrestore(&priv->hcmd_lock, flags); return idx; }
/** * iwl_enqueue_hcmd - enqueue a uCode command * @priv: device private data point * @cmd: a point to the ucode command structure * * The function returns < 0 values to indicate the operation is * failed. On success, it turns the index (> 0) of command in the * command queue. */ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_queue *q = &txq->q; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; u32 idx; u16 copy_size, cmd_size; bool had_nocopy = false; int i; u8 *cmd_dest; #ifdef CONFIG_IWLWIFI_DEVICE_TRACING const void *trace_bufs[IWL_MAX_CMD_TFDS + 1] = {}; int trace_lens[IWL_MAX_CMD_TFDS + 1] = {}; int trace_idx; #endif copy_size = sizeof(out_cmd->hdr); cmd_size = sizeof(out_cmd->hdr); /* need one for the header if the first is NOCOPY */ BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { had_nocopy = true; } else { /* NOCOPY must not be followed by normal! */ if (WARN_ON(had_nocopy)) return -EINVAL; copy_size += cmd->len[i]; } cmd_size += cmd->len[i]; } /* * If any of the command structures end up being larger than * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically * allocated into separate TFDs, then we will need to * increase the size of the buffers. */ if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE)) return -EINVAL; spin_lock_bh(&txq->lock); if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { spin_unlock_bh(&txq->lock); IWL_ERR(trans, "No space in command queue\n"); iwl_op_mode_cmd_queue_full(trans->op_mode); return -ENOSPC; } idx = get_cmd_index(q, q->write_ptr); out_cmd = txq->entries[idx].cmd; out_meta = &txq->entries[idx].meta; memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ if (cmd->flags & CMD_WANT_SKB) out_meta->source = cmd; /* set up the header */ out_cmd->hdr.cmd = cmd->id; out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | INDEX_TO_SEQ(q->write_ptr)); /* and copy the data that needs to be copied */ cmd_dest = out_cmd->payload; for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) break; memcpy(cmd_dest, cmd->data[i], cmd->len[i]); cmd_dest += cmd->len[i]; } IWL_DEBUG_HC(trans, "Sending command %s (#%x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", trans_pcie_get_cmd_string(trans_pcie, out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, copy_size, DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { idx = -ENOMEM; goto out; } dma_unmap_addr_set(out_meta, mapping, phys_addr); dma_unmap_len_set(out_meta, len, copy_size); iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_bufs[0] = &out_cmd->hdr; trace_lens[0] = copy_size; trace_idx = 1; #endif for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) continue; phys_addr = dma_map_single(trans->dev, (void *)cmd->data[i], cmd->len[i], DMA_BIDIRECTIONAL); if (dma_mapping_error(trans->dev, phys_addr)) { iwl_unmap_tfd(trans, out_meta, &txq->tfds[q->write_ptr], DMA_BIDIRECTIONAL); idx = -ENOMEM; goto out; } iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, cmd->len[i], 0); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_bufs[trace_idx] = cmd->data[i]; trace_lens[trace_idx] = cmd->len[i]; trace_idx++; #endif } out_meta->flags = cmd->flags; txq->need_update = 1; /* check that tracing gets all possible blocks */ BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_iwlwifi_dev_hcmd(trans->dev, cmd->flags, trace_bufs[0], trace_lens[0], trace_bufs[1], trace_lens[1], trace_bufs[2], trace_lens[2]); #endif /* start timer if queue currently empty */ if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); /* Increment and update queue's write index */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); iwl_txq_update_write_ptr(trans, txq); out: spin_unlock_bh(&txq->lock); return idx; }
/** * iwl_legacy_enqueue_hcmd - enqueue a uCode command * @priv: device private data point * @cmd: a point to the ucode command structure * * The function returns < 0 values to indicate the operation is * failed. On success, it turns the index (> 0) of command in the * command queue. */ int iwl_legacy_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; struct iwl_queue *q = &txq->q; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; unsigned long flags; int len; u32 idx; u16 fix_size; cmd->len = priv->cfg->ops->utils->get_hcmd_size(cmd->id, cmd->len); fix_size = (u16)(cmd->len + sizeof(out_cmd->hdr)); /* If any of the command structures end up being larger than * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then * we will need to increase the size of the TFD entries * Also, check to see if command buffer should not exceed the size * of device_cmd and max_cmd_size. */ BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && !(cmd->flags & CMD_SIZE_HUGE)); BUG_ON(fix_size > IWL_MAX_CMD_SIZE); if (iwl_legacy_is_rfkill(priv) || iwl_legacy_is_ctkill(priv)) { IWL_WARN(priv, "Not sending command - %s KILL\n", iwl_legacy_is_rfkill(priv) ? "RF" : "CT"); return -EIO; } spin_lock_irqsave(&priv->hcmd_lock, flags); if (iwl_legacy_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { spin_unlock_irqrestore(&priv->hcmd_lock, flags); IWL_ERR(priv, "Restarting adapter due to command queue full\n"); queue_work(priv->workqueue, &priv->restart); return -ENOSPC; } idx = iwl_legacy_get_cmd_index(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); out_cmd = txq->cmd[idx]; out_meta = &txq->meta[idx]; if (WARN_ON(out_meta->flags & CMD_MAPPED)) { spin_unlock_irqrestore(&priv->hcmd_lock, flags); return -ENOSPC; } memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ out_meta->flags = cmd->flags | CMD_MAPPED; if (cmd->flags & CMD_WANT_SKB) out_meta->source = cmd; if (cmd->flags & CMD_ASYNC) out_meta->callback = cmd->callback; out_cmd->hdr.cmd = cmd->id; memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); /* At this point, the out_cmd now has all of the incoming cmd * information */ out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(priv->cmd_queue) | INDEX_TO_SEQ(q->write_ptr)); if (cmd->flags & CMD_SIZE_HUGE) out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; len = sizeof(struct iwl_device_cmd); if (idx == TFD_CMD_SLOTS) len = IWL_MAX_CMD_SIZE; #ifdef CONFIG_IWLWIFI_LEGACY_DEBUG switch (out_cmd->hdr.cmd) { case REPLY_TX_LINK_QUALITY_CMD: case SENSITIVITY_CMD: IWL_DEBUG_HC_DUMP(priv, "Sending command %s (#%x), seq: 0x%04X, " "%d bytes at %d[%d]:%d\n", iwl_legacy_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr, idx, priv->cmd_queue); break; default: IWL_DEBUG_HC(priv, "Sending command %s (#%x), seq: 0x%04X, " "%d bytes at %d[%d]:%d\n", iwl_legacy_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr, idx, priv->cmd_queue); } #endif txq->need_update = 1; if (priv->cfg->ops->lib->txq_update_byte_cnt_tbl) /* Set up entry in queue's byte count circular buffer */ priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq, 0); phys_addr = pci_map_single(priv->pci_dev, &out_cmd->hdr, fix_size, PCI_DMA_BIDIRECTIONAL); dma_unmap_addr_set(out_meta, mapping, phys_addr); dma_unmap_len_set(out_meta, len, fix_size); trace_iwlwifi_legacy_dev_hcmd(priv, &out_cmd->hdr, fix_size, cmd->flags); priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, phys_addr, fix_size, 1, U32_PAD(cmd->len)); /* Increment and update queue's write index */ q->write_ptr = iwl_legacy_queue_inc_wrap(q->write_ptr, q->n_bd); iwl_legacy_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->hcmd_lock, flags); return idx; }
/** * iwl_enqueue_hcmd - enqueue a uCode command * @priv: device private data point * @cmd: a point to the ucode command structure * * The function returns < 0 values to indicate the operation is * failed. On success, it turns the index (> 0) of command in the * command queue. */ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_queue *q = &txq->q; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; u32 idx; u16 copy_size, cmd_size, dma_size; bool had_nocopy = false; int i; u8 *cmd_dest; const u8 *cmddata[IWL_MAX_CMD_TFDS]; u16 cmdlen[IWL_MAX_CMD_TFDS]; #ifdef CONFIG_IWLWIFI_DEVICE_TRACING const void *trace_bufs[IWL_MAX_CMD_TFDS + 1] = {}; int trace_lens[IWL_MAX_CMD_TFDS + 1] = {}; int trace_idx; #endif if (test_bit(STATUS_FW_ERROR, &trans->shrd->status)) { IWL_WARN(trans, "fw recovery, no hcmd send\n"); return -EIO; } copy_size = sizeof(out_cmd->hdr); cmd_size = sizeof(out_cmd->hdr); /* need one for the header if the first is NOCOPY */ BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { cmddata[i] = cmd->data[i]; cmdlen[i] = cmd->len[i]; if (!cmd->len[i]) continue; /* need at least IWL_HCMD_MIN_COPY_SIZE copied */ if (copy_size < IWL_HCMD_MIN_COPY_SIZE) { int copy = IWL_HCMD_MIN_COPY_SIZE - copy_size; if (copy > cmdlen[i]) copy = cmdlen[i]; cmdlen[i] -= copy; cmddata[i] += copy; copy_size += copy; } if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { had_nocopy = true; } else { /* NOCOPY must not be followed by normal! */ if (WARN_ON(had_nocopy)) return -EINVAL; copy_size += cmdlen[i]; } cmd_size += cmd->len[i]; } /* * If any of the command structures end up being larger than * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically * allocated into separate TFDs, then we will need to * increase the size of the buffers. */ if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE)) return -EINVAL; spin_lock_bh(&txq->lock); if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { spin_unlock_bh(&txq->lock); IWL_ERR(trans, "No space in command queue\n"); iwl_op_mode_cmd_queue_full(trans->op_mode); return -ENOSPC; } idx = get_cmd_index(q, q->write_ptr); out_cmd = txq->cmd[idx]; out_meta = &txq->meta[idx]; memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ if (cmd->flags & CMD_WANT_SKB) out_meta->source = cmd; /* set up the header */ out_cmd->hdr.cmd = cmd->id; out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | INDEX_TO_SEQ(q->write_ptr)); /* and copy the data that needs to be copied */ cmd_dest = out_cmd->payload; copy_size = sizeof(out_cmd->hdr); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { int copy = 0; if (!cmd->len) continue; /* need at least IWL_HCMD_MIN_COPY_SIZE copied */ if (copy_size < IWL_HCMD_MIN_COPY_SIZE) { copy = IWL_HCMD_MIN_COPY_SIZE - copy_size; if (copy > cmd->len[i]) copy = cmd->len[i]; } /* copy everything if not nocopy/dup */ if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) copy = cmd->len[i]; if (copy) { memcpy(cmd_dest, cmd->data[i], copy); cmd_dest += copy; copy_size += copy; } } IWL_DEBUG_HC(trans, "Sending command %s (#%x), seq: 0x%04X, " "%d bytes at %d[%d]:%d\n", get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); /* * If the entire command is smaller than IWL_HCMD_MIN_COPY_SIZE, we must * still map at least that many bytes for the hardware to write back to. * We have enough space, so that's not a problem. */ dma_size = max_t(u16, copy_size, IWL_HCMD_MIN_COPY_SIZE); phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, dma_size, DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { idx = -ENOMEM; goto out; } dma_unmap_addr_set(out_meta, mapping, phys_addr); dma_unmap_len_set(out_meta, len, dma_size); iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_bufs[0] = &out_cmd->hdr; trace_lens[0] = copy_size; trace_idx = 1; #endif /* map the remaining (adjusted) nocopy/dup fragments */ for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmdlen[i]) continue; if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) continue; phys_addr = dma_map_single(trans->dev, (void *)cmddata[i], cmdlen[i], DMA_BIDIRECTIONAL); if (dma_mapping_error(trans->dev, phys_addr)) { iwlagn_unmap_tfd(trans, out_meta, &txq->tfds[q->write_ptr], DMA_BIDIRECTIONAL); idx = -ENOMEM; goto out; } iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, cmdlen[i], 0); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_bufs[trace_idx] = cmddata[i]; trace_lens[trace_idx] = cmdlen[i]; trace_idx++; #endif } out_meta->flags = cmd->flags; txq->need_update = 1; /* check that tracing gets all possible blocks */ BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_iwlwifi_dev_hcmd(trans->dev, cmd->flags, trace_bufs[0], trace_lens[0], trace_bufs[1], trace_lens[1], trace_bufs[2], trace_lens[2]); #endif /* Increment and update queue's write index */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); iwl_txq_update_write_ptr(trans, txq); out: spin_unlock_bh(&txq->lock); return idx; }
static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_queue *q = &txq->q; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; u32 idx; u16 copy_size, cmd_size; bool had_nocopy = false; int i; u8 *cmd_dest; #ifdef CONFIG_IWLWIFI_DEVICE_TRACING const void *trace_bufs[IWL_MAX_CMD_TFDS + 1] = {}; int trace_lens[IWL_MAX_CMD_TFDS + 1] = {}; int trace_idx; #endif if (test_bit(STATUS_FW_ERROR, &trans->shrd->status)) { IWL_WARN(trans, "fw recovery, no hcmd send\n"); return -EIO; } copy_size = sizeof(out_cmd->hdr); cmd_size = sizeof(out_cmd->hdr); BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { had_nocopy = true; } else { if (WARN_ON(had_nocopy)) return -EINVAL; copy_size += cmd->len[i]; } cmd_size += cmd->len[i]; } if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE)) return -EINVAL; spin_lock_bh(&txq->lock); if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { spin_unlock_bh(&txq->lock); IWL_ERR(trans, "No space in command queue\n"); iwl_op_mode_cmd_queue_full(trans->op_mode); return -ENOSPC; } idx = get_cmd_index(q, q->write_ptr); out_cmd = txq->cmd[idx]; out_meta = &txq->meta[idx]; memset(out_meta, 0, sizeof(*out_meta)); if (cmd->flags & CMD_WANT_SKB) out_meta->source = cmd; out_cmd->hdr.cmd = cmd->id; out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | INDEX_TO_SEQ(q->write_ptr)); cmd_dest = out_cmd->payload; for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) break; memcpy(cmd_dest, cmd->data[i], cmd->len[i]); cmd_dest += cmd->len[i]; } IWL_DEBUG_HC(trans, "Sending command %s (#%x), seq: 0x%04X, " "%d bytes at %d[%d]:%d\n", get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, copy_size, DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { idx = -ENOMEM; goto out; } dma_unmap_addr_set(out_meta, mapping, phys_addr); dma_unmap_len_set(out_meta, len, copy_size); iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_bufs[0] = &out_cmd->hdr; trace_lens[0] = copy_size; trace_idx = 1; #endif for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) continue; phys_addr = dma_map_single(trans->dev, (void *)cmd->data[i], cmd->len[i], DMA_BIDIRECTIONAL); if (dma_mapping_error(trans->dev, phys_addr)) { iwlagn_unmap_tfd(trans, out_meta, &txq->tfds[q->write_ptr], DMA_BIDIRECTIONAL); idx = -ENOMEM; goto out; } iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, cmd->len[i], 0); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_bufs[trace_idx] = cmd->data[i]; trace_lens[trace_idx] = cmd->len[i]; trace_idx++; #endif } out_meta->flags = cmd->flags; txq->need_update = 1; BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_iwlwifi_dev_hcmd(trans->dev, cmd->flags, trace_bufs[0], trace_lens[0], trace_bufs[1], trace_lens[1], trace_bufs[2], trace_lens[2]); #endif q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); iwl_txq_update_write_ptr(trans, txq); out: spin_unlock_bh(&txq->lock); return idx; }
/* * iwl_pcie_gen2_enqueue_hcmd - enqueue a uCode command * @priv: device private data point * @cmd: a pointer to the ucode command structure * * The function returns < 0 values to indicate the operation * failed. On success, it returns the index (>= 0) of command in the * command queue. */ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq = trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; unsigned long flags; void *dup_buf = NULL; dma_addr_t phys_addr; int i, cmd_pos, idx; u16 copy_size, cmd_size, tb0_size; bool had_nocopy = false; u8 group_id = iwl_cmd_groupid(cmd->id); const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; struct iwl_tfh_tfd *tfd; copy_size = sizeof(struct iwl_cmd_header_wide); cmd_size = sizeof(struct iwl_cmd_header_wide); for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { cmddata[i] = cmd->data[i]; cmdlen[i] = cmd->len[i]; if (!cmd->len[i]) continue; /* need at least IWL_FIRST_TB_SIZE copied */ if (copy_size < IWL_FIRST_TB_SIZE) { int copy = IWL_FIRST_TB_SIZE - copy_size; if (copy > cmdlen[i]) copy = cmdlen[i]; cmdlen[i] -= copy; cmddata[i] += copy; copy_size += copy; } if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { had_nocopy = true; if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) { idx = -EINVAL; goto free_dup_buf; } } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) { /* * This is also a chunk that isn't copied * to the static buffer so set had_nocopy. */ had_nocopy = true; /* only allowed once */ if (WARN_ON(dup_buf)) { idx = -EINVAL; goto free_dup_buf; } dup_buf = kmemdup(cmddata[i], cmdlen[i], GFP_ATOMIC); if (!dup_buf) return -ENOMEM; } else { /* NOCOPY must not be followed by normal! */ if (WARN_ON(had_nocopy)) { idx = -EINVAL; goto free_dup_buf; } copy_size += cmdlen[i]; } cmd_size += cmd->len[i]; } /* * If any of the command structures end up being larger than the * TFD_MAX_PAYLOAD_SIZE and they aren't dynamically allocated into * separate TFDs, then we will need to increase the size of the buffers */ if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE, "Command %s (%#x) is too large (%d bytes)\n", iwl_get_cmd_string(trans, cmd->id), cmd->id, copy_size)) { idx = -EINVAL; goto free_dup_buf; } spin_lock_bh(&txq->lock); idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); tfd = iwl_pcie_get_tfd(trans, txq, txq->write_ptr); memset(tfd, 0, sizeof(*tfd)); if (iwl_queue_space(trans, txq) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { spin_unlock_bh(&txq->lock); IWL_ERR(trans, "No space in command queue\n"); iwl_op_mode_cmd_queue_full(trans->op_mode); idx = -ENOSPC; goto free_dup_buf; } out_cmd = txq->entries[idx].cmd; out_meta = &txq->entries[idx].meta; /* re-initialize to NULL */ memset(out_meta, 0, sizeof(*out_meta)); if (cmd->flags & CMD_WANT_SKB) out_meta->source = cmd; /* set up the header */ out_cmd->hdr_wide.cmd = iwl_cmd_opcode(cmd->id); out_cmd->hdr_wide.group_id = group_id; out_cmd->hdr_wide.version = iwl_cmd_version(cmd->id); out_cmd->hdr_wide.length = cpu_to_le16(cmd_size - sizeof(struct iwl_cmd_header_wide)); out_cmd->hdr_wide.reserved = 0; out_cmd->hdr_wide.sequence = cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | INDEX_TO_SEQ(txq->write_ptr)); cmd_pos = sizeof(struct iwl_cmd_header_wide); copy_size = sizeof(struct iwl_cmd_header_wide); /* and copy the data that needs to be copied */ for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { int copy; if (!cmd->len[i]) continue; /* copy everything if not nocopy/dup */ if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | IWL_HCMD_DFL_DUP))) { copy = cmd->len[i]; memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); cmd_pos += copy; copy_size += copy; continue; } /* * Otherwise we need at least IWL_FIRST_TB_SIZE copied * in total (for bi-directional DMA), but copy up to what * we can fit into the payload for debug dump purposes. */ copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]); memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); cmd_pos += copy; /* However, treat copy_size the proper way, we need it below */ if (copy_size < IWL_FIRST_TB_SIZE) { copy = IWL_FIRST_TB_SIZE - copy_size; if (copy > cmd->len[i]) copy = cmd->len[i]; copy_size += copy; } } IWL_DEBUG_HC(trans, "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", iwl_get_cmd_string(trans, cmd->id), group_id, out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, txq->write_ptr, idx, trans_pcie->cmd_queue); /* start the TFD with the minimum copy bytes */ tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE); memcpy(&txq->first_tb_bufs[idx], &out_cmd->hdr, tb0_size); iwl_pcie_gen2_set_tb(trans, tfd, iwl_pcie_get_first_tb_dma(txq, idx), tb0_size); /* map first command fragment, if any remains */ if (copy_size > tb0_size) { phys_addr = dma_map_single(trans->dev, ((u8 *)&out_cmd->hdr) + tb0_size, copy_size - tb0_size, DMA_TO_DEVICE); if (dma_mapping_error(trans->dev, phys_addr)) { idx = -ENOMEM; iwl_pcie_gen2_tfd_unmap(trans, out_meta, tfd); goto out; } iwl_pcie_gen2_set_tb(trans, tfd, phys_addr, copy_size - tb0_size); } /* map the remaining (adjusted) nocopy/dup fragments */ for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { const void *data = cmddata[i]; if (!cmdlen[i]) continue; if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | IWL_HCMD_DFL_DUP))) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) data = dup_buf; phys_addr = dma_map_single(trans->dev, (void *)data, cmdlen[i], DMA_TO_DEVICE); if (dma_mapping_error(trans->dev, phys_addr)) { idx = -ENOMEM; iwl_pcie_gen2_tfd_unmap(trans, out_meta, tfd); goto out; } iwl_pcie_gen2_set_tb(trans, tfd, phys_addr, cmdlen[i]); } BUILD_BUG_ON(IWL_TFH_NUM_TBS > sizeof(out_meta->tbs) * BITS_PER_BYTE); out_meta->flags = cmd->flags; if (WARN_ON_ONCE(txq->entries[idx].free_buf)) kzfree(txq->entries[idx].free_buf); txq->entries[idx].free_buf = dup_buf; trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide); /* start timer if queue currently empty */ if (txq->read_ptr == txq->write_ptr && txq->wd_timeout) mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); spin_lock_irqsave(&trans_pcie->reg_lock, flags); if (!(cmd->flags & CMD_SEND_IN_IDLE) && !trans_pcie->ref_cmd_in_flight) { trans_pcie->ref_cmd_in_flight = true; IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n"); iwl_trans_ref(trans); } /* Increment and update queue's write index */ txq->write_ptr = iwl_queue_inc_wrap(trans, txq->write_ptr); iwl_pcie_gen2_txq_inc_wr_ptr(trans, txq); spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); out: spin_unlock_bh(&txq->lock); free_dup_buf: if (idx < 0) kfree(dup_buf); return idx; }