[iwlwifi][uprev] port pcie/tx-gen2.c
Use Fuchisa iobuf for packet buffer DMA.
Bug: b/299517585
Tested: NUC 7 is still working.
Change-Id: Id384e4a71ca8294ea35bd32ce2e8a4ed66bb4bbd
Reviewed-on: https://fuchsia-review.googlesource.com/c/drivers/wlan/intel/iwlwifi/+/915717
Reviewed-by: Zhiyi Chen <zhiyichen@google.com>
Commit-Queue: Louis Lo <yjlou@google.com>
diff --git a/third_party/iwlwifi/pcie/tx-gen2.c b/third_party/iwlwifi/pcie/tx-gen2.c
index bb79005..2e4192f 100644
--- a/third_party/iwlwifi/pcie/tx-gen2.c
+++ b/third_party/iwlwifi/pcie/tx-gen2.c
@@ -36,13 +36,8 @@
#include "third_party/iwlwifi/iwl-debug.h"
#include "third_party/iwlwifi/iwl-io.h"
#include "third_party/iwlwifi/pcie/internal.h"
+#include "third_party/iwlwifi/queue/tx.h"
-#if 1 // NEED_PORTING
-int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans* trans, struct iwl_host_cmd* cmd) {
- // TODO(fxbug.dev/95472): Will continue on fxr/815268.
- return ZX_ERR_NOT_SUPPORTED;
-}
-#else
/*************** HOST COMMAND QUEUE FUNCTIONS *****/
/*
@@ -50,11 +45,11 @@
* @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.
+ * The function returns ZX_ERR_* values to indicate the operation failed. On success, opposite to
+ * the original driver, the index (>= 0) of command in the command queue is NOT returned (since the
+ * caller doesn't use it anyway).
*/
-int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
+zx_status_t 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);
@@ -70,7 +65,8 @@
const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD];
u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD];
struct iwl_tfh_tfd *tfd;
- unsigned long flags;
+ __UNUSED unsigned long flags;
+ zx_status_t ret = ZX_OK;
copy_size = sizeof(struct iwl_cmd_header_wide);
cmd_size = sizeof(struct iwl_cmd_header_wide);
@@ -95,8 +91,9 @@
if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) {
had_nocopy = true;
- if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) {
- idx = -EINVAL;
+ if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) {
+ IWL_WARN(trans, "hcmd contains DFL_DUP but DFL_NOCOPY is asserted too.\n");
+ ret = ZX_ERR_INVALID_ARGS;
goto free_dup_buf;
}
} else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) {
@@ -107,19 +104,21 @@
had_nocopy = true;
/* only allowed once */
- if (WARN_ON(dup_buf)) {
- idx = -EINVAL;
+ if (dup_buf) {
+ IWL_WARN(trans, "Only allowed once: dup_buf=%p\n", dup_buf);
+ ret = ZX_ERR_INVALID_ARGS;
goto free_dup_buf;
}
- dup_buf = kmemdup(cmddata[i], cmdlen[i],
- GFP_ATOMIC);
+ dup_buf = calloc(1, cmdlen[i]);
if (!dup_buf)
- return -ENOMEM;
+ return ZX_ERR_NO_MEMORY;
+ memcpy(dup_buf, cmddata[i], cmdlen[i]);
} else {
/* NOCOPY must not be followed by normal! */
- if (WARN_ON(had_nocopy)) {
- idx = -EINVAL;
+ if (had_nocopy) {
+ IWL_WARN(trans, "NOCOPY must not be followed by normal!\n");
+ ret = ZX_ERR_INVALID_ARGS;
goto free_dup_buf;
}
copy_size += cmdlen[i];
@@ -132,10 +131,10 @@
* 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;
+ if (copy_size > TFD_MAX_PAYLOAD_SIZE) {
+ IWL_WARN(trans, "Command %s (%#x) is too large (%d bytes)\n",
+ iwl_get_cmd_string(trans, cmd->id), cmd->id, copy_size);
+ ret = ZX_ERR_INVALID_ARGS;
goto free_dup_buf;
}
@@ -150,11 +149,11 @@
IWL_ERR(trans, "No space in command queue\n");
iwl_op_mode_cmd_queue_full(trans->op_mode);
- idx = -ENOSPC;
+ ret = ZX_ERR_NO_SPACE;
goto free_dup_buf;
}
- out_cmd = txq->entries[idx].cmd;
+ out_cmd = iwl_iobuf_virtual(txq->entries[idx].cmd);
out_meta = &txq->entries[idx].meta;
/* re-initialize to NULL */
@@ -221,24 +220,17 @@
cmd_size, txq->write_ptr, idx, trans->txqs.cmd.q_id);
/* start the TFD with the minimum copy bytes */
+ struct iwl_pcie_first_tb_buf* tb_bufs = iwl_iobuf_virtual(txq->first_tb_bufs);
tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE);
- memcpy(&txq->first_tb_bufs[idx], out_cmd, tb0_size);
- iwl_txq_gen2_set_tb(trans, tfd, iwl_txq_get_first_tb_dma(txq, idx),
- tb0_size);
+ memcpy(&tb_bufs[idx], out_cmd, tb0_size);
+ dma_addr_t first_tb_dma = iwl_txq_get_first_tb_dma(txq, idx);
+ iwl_txq_gen2_set_tb(trans, tfd, first_tb_dma, tb0_size, NULL);
/* map first command fragment, if any remains */
if (copy_size > tb0_size) {
- phys_addr = dma_map_single(trans->dev,
- (u8 *)out_cmd + tb0_size,
- copy_size - tb0_size,
- DMA_TO_DEVICE);
- if (dma_mapping_error(trans->dev, phys_addr)) {
- idx = -ENOMEM;
- iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd);
- goto out;
- }
+ phys_addr = first_tb_dma + tb0_size;
iwl_txq_gen2_set_tb(trans, tfd, phys_addr,
- copy_size - tb0_size);
+ copy_size - tb0_size, NULL);
}
/* map the remaining (adjusted) nocopy/dup fragments */
@@ -252,27 +244,36 @@
continue;
if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP)
data = dup_buf;
- phys_addr = dma_map_single(trans->dev, data,
- cmdlen[i], DMA_TO_DEVICE);
- if (dma_mapping_error(trans->dev, phys_addr)) {
- idx = -ENOMEM;
- iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd);
- goto out;
- }
- iwl_txq_gen2_set_tb(trans, tfd, phys_addr, cmdlen[i]);
+
+ // Leverage the 'dup_io_buf' for the DMA address used for 'iwl_txq_gen2_set_tb()'.
+ // Check more comments in 'iwl_pcie_enqueue_hcmd()' in pcie/tx.c.
+ struct iwl_iobuf* dup_io_buf = txq->entries[idx].dup_io_buf;
+ ZX_ASSERT(dup_io_buf == NULL);
+ uint16_t dup_len = cmdlen[i];
+ iwl_iobuf_allocate_contiguous(&trans_pcie->pci_dev->dev, dup_len, &dup_io_buf);
+ void* virt_addr = iwl_iobuf_virtual(dup_io_buf);
+ memcpy(virt_addr, data, dup_len);
+ phys_addr = iwl_iobuf_physical(dup_io_buf);
+ iwl_txq_gen2_set_tb(trans, tfd, phys_addr, dup_len, NULL);
+ iwl_iobuf_cache_flush(dup_io_buf, 0, dup_len);
+ txq->entries[idx].dup_io_buf = dup_io_buf;
}
BUILD_BUG_ON(IWL_TFH_NUM_TBS > sizeof(out_meta->tbs) * BITS_PER_BYTE);
out_meta->flags = cmd->flags;
+#if 0 // NEEDS_PORTING
if (WARN_ON_ONCE(txq->entries[idx].free_buf))
kfree_sensitive(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);
+#endif // NEEDS_PORTING
/* 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);
+ if (txq->read_ptr == txq->write_ptr && txq->wd_timeout) {
+ iwl_irq_timer_stop(txq->stuck_timer);
+ iwl_irq_timer_start(txq->stuck_timer, txq->wd_timeout);
+ }
spin_lock(&trans_pcie->reg_lock);
/* Increment and update queue's write index */
@@ -280,11 +281,8 @@
iwl_txq_inc_wr_ptr(trans, txq);
spin_unlock(&trans_pcie->reg_lock);
-out:
spin_unlock_irqrestore(&txq->lock, flags);
free_dup_buf:
- if (idx < 0)
- kfree(dup_buf);
- return idx;
+ free(dup_buf);
+ return ret;
}
-#endif