diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-tx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-tx.c | 231 |
1 files changed, 124 insertions, 107 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index a47558f0ee3d..2f6b38cfcc13 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -125,25 +125,13 @@ static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd) return tfd->num_tbs & 0x1f; } -/** - * iwlagn_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] - * @priv - driver private data - * @txq - tx queue - * - * Does NOT advance any TFD circular buffer read/write indexes - * Does NOT free the TFD itself (which is within circular buffer) - */ -void iwlagn_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) +static void iwlagn_unmap_tfd(struct iwl_priv *priv, struct iwl_cmd_meta *meta, + struct iwl_tfd *tfd) { - struct iwl_tfd *tfd_tmp = (struct iwl_tfd *)txq->tfds; - struct iwl_tfd *tfd; struct pci_dev *dev = priv->pci_dev; - int index = txq->q.read_ptr; int i; int num_tbs; - tfd = &tfd_tmp[index]; - /* Sanity check on number of chunks */ num_tbs = iwl_tfd_get_num_tbs(tfd); @@ -156,14 +144,30 @@ void iwlagn_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) /* Unmap tx_cmd */ if (num_tbs) pci_unmap_single(dev, - dma_unmap_addr(&txq->meta[index], mapping), - dma_unmap_len(&txq->meta[index], len), + dma_unmap_addr(meta, mapping), + dma_unmap_len(meta, len), PCI_DMA_BIDIRECTIONAL); /* Unmap chunks, if any. */ for (i = 1; i < num_tbs; i++) pci_unmap_single(dev, iwl_tfd_tb_get_addr(tfd, i), iwl_tfd_tb_get_len(tfd, i), PCI_DMA_TODEVICE); +} + +/** + * iwlagn_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] + * @priv - driver private data + * @txq - tx queue + * + * Does NOT advance any TFD circular buffer read/write indexes + * Does NOT free the TFD itself (which is within circular buffer) + */ +void iwlagn_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) +{ + struct iwl_tfd *tfd_tmp = txq->tfds; + int index = txq->q.read_ptr; + + iwlagn_unmap_tfd(priv, &txq->meta[index], &tfd_tmp[index]); /* free SKB */ if (txq->txb) { @@ -189,7 +193,7 @@ int iwlagn_txq_attach_buf_to_tfd(struct iwl_priv *priv, u32 num_tbs; q = &txq->q; - tfd_tmp = (struct iwl_tfd *)txq->tfds; + tfd_tmp = txq->tfds; tfd = &tfd_tmp[q->write_ptr]; if (reset) @@ -303,7 +307,7 @@ void iwl_cmd_queue_unmap(struct iwl_priv *priv) return; while (q->read_ptr != q->write_ptr) { - i = get_cmd_index(q, q->read_ptr, 0); + i = get_cmd_index(q, q->read_ptr); if (txq->meta[i].flags & CMD_MAPPED) { pci_unmap_single(priv->pci_dev, @@ -315,15 +319,6 @@ void iwl_cmd_queue_unmap(struct iwl_priv *priv) q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); } - - i = q->n_window; - if (txq->meta[i].flags & CMD_MAPPED) { - pci_unmap_single(priv->pci_dev, - dma_unmap_addr(&txq->meta[i], mapping), - dma_unmap_len(&txq->meta[i], len), - PCI_DMA_BIDIRECTIONAL); - txq->meta[i].flags = 0; - } } /** @@ -343,7 +338,7 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) iwl_cmd_queue_unmap(priv); /* De-alloc array of command/tx buffers */ - for (i = 0; i <= TFD_CMD_SLOTS; i++) + for (i = 0; i < TFD_CMD_SLOTS; i++) kfree(txq->cmd[i]); /* De-alloc circular buffer of TFDs */ @@ -483,33 +478,17 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, { int i, len; int ret; - int actual_slots = slots_num; - - /* - * Alloc buffer array for commands (Tx or other types of commands). - * For the command queue (#4/#9), allocate command space + one big - * command for scan, since scan command is very huge; the system will - * not have two scans at the same time, so only one is needed. - * For normal Tx queues (all other queues), no super-size command - * space is needed. - */ - if (txq_id == priv->cmd_queue) - actual_slots++; - txq->meta = kzalloc(sizeof(struct iwl_cmd_meta) * actual_slots, + txq->meta = kzalloc(sizeof(struct iwl_cmd_meta) * slots_num, GFP_KERNEL); - txq->cmd = kzalloc(sizeof(struct iwl_device_cmd *) * actual_slots, + txq->cmd = kzalloc(sizeof(struct iwl_device_cmd *) * slots_num, GFP_KERNEL); if (!txq->meta || !txq->cmd) goto out_free_arrays; len = sizeof(struct iwl_device_cmd); - for (i = 0; i < actual_slots; i++) { - /* only happens for cmd queue */ - if (i == slots_num) - len = IWL_MAX_CMD_SIZE; - + for (i = 0; i < slots_num; i++) { txq->cmd[i] = kmalloc(len, GFP_KERNEL); if (!txq->cmd[i]) goto err; @@ -544,7 +523,7 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, return 0; err: - for (i = 0; i < actual_slots; i++) + for (i = 0; i < slots_num; i++) kfree(txq->cmd[i]); out_free_arrays: kfree(txq->meta); @@ -592,23 +571,44 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) dma_addr_t phys_addr; unsigned long flags; u32 idx; - u16 fix_size; + 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 - fix_size = (u16)(cmd->len[0] + sizeof(out_cmd->hdr)); + 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 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. + * 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((fix_size > TFD_MAX_PAYLOAD_SIZE) && - !(cmd->flags & CMD_SIZE_HUGE))) - return -EINVAL; - - if (WARN_ON(fix_size > IWL_MAX_CMD_SIZE)) + if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE)) return -EINVAL; if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { @@ -617,14 +617,6 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) return -EIO; } - /* - * As we only have a single huge buffer, check that the command - * is synchronous (otherwise buffers could end up being reused). - */ - - if (WARN_ON((cmd->flags & CMD_ASYNC) && (cmd->flags & CMD_SIZE_HUGE))) - return -EINVAL; - spin_lock_irqsave(&priv->hcmd_lock, flags); if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { @@ -639,7 +631,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) return -ENOSPC; } - idx = get_cmd_index(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); + idx = get_cmd_index(q, q->write_ptr); out_cmd = txq->cmd[idx]; out_meta = &txq->meta[idx]; @@ -654,55 +646,84 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) if (cmd->flags & CMD_ASYNC) out_meta->callback = cmd->callback; - out_cmd->hdr.cmd = cmd->id; - memcpy(&out_cmd->cmd.payload, cmd->data[0], cmd->len[0]); - - /* At this point, the out_cmd now has all of the incoming cmd - * information */ + /* 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)); - if (cmd->flags & CMD_SIZE_HUGE) - out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; - -#ifdef CONFIG_IWLWIFI_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", - 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", - 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); + 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]; } -#endif + + 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 = pci_map_single(priv->pci_dev, &out_cmd->hdr, - fix_size, PCI_DMA_BIDIRECTIONAL); + copy_size, PCI_DMA_BIDIRECTIONAL); if (unlikely(pci_dma_mapping_error(priv->pci_dev, phys_addr))) { idx = -ENOMEM; goto out; } dma_unmap_addr_set(out_meta, mapping, phys_addr); - dma_unmap_len_set(out_meta, len, fix_size); + 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 = pci_map_single(priv->pci_dev, (void *)cmd->data[i], + cmd->len[i], PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pci_dev, phys_addr)) { + iwlagn_unmap_tfd(priv, out_meta, + &txq->tfds[q->write_ptr]); + 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 | CMD_MAPPED; txq->need_update = 1; - trace_iwlwifi_dev_hcmd(priv, &out_cmd->hdr, fix_size, cmd->flags); - - iwlagn_txq_attach_buf_to_tfd(priv, txq, phys_addr, fix_size, 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); @@ -761,7 +782,6 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) int txq_id = SEQ_TO_QUEUE(sequence); int index = SEQ_TO_INDEX(sequence); int cmd_index; - bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); struct iwl_device_cmd *cmd; struct iwl_cmd_meta *meta; struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; @@ -779,14 +799,11 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) return; } - cmd_index = get_cmd_index(&txq->q, index, huge); + cmd_index = get_cmd_index(&txq->q, index); cmd = txq->cmd[cmd_index]; meta = &txq->meta[cmd_index]; - pci_unmap_single(priv->pci_dev, - dma_unmap_addr(meta, mapping), - dma_unmap_len(meta, len), - PCI_DMA_BIDIRECTIONAL); + iwlagn_unmap_tfd(priv, meta, &txq->tfds[index]); /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { |