From 94ce9e5e73c71b26a19b83844f9840859f3b035b Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Sun, 14 Jun 2015 11:17:07 +0300 Subject: iwlwifi: pcie: Set scheduler to work on auto mode During NIC initialization shared HW is reset and this disables the scheduler. Some HW platforms do not activate the scheduler after it. Consequently all HCMD sent by the driver stay at the queues which cause to queue stuck. Set the scheduler to work on auto active mode so it would be activated upon change over one of the queues' write pointer. Signed-off-by: Haim Dreyfuss Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/pcie/tx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/iwlwifi/pcie/tx.c') diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 2b86c2135de3..d711f0b8b62f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -915,6 +915,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) } } + iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE); if (trans->cfg->base_params->num_of_queues > 20) iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_ENABLE_31_QUEUES); -- cgit v1.2.3 From f7e6469fc90aa388ac5047e1ba914b31af9240ae Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Jun 2015 21:58:17 +0200 Subject: iwlwifi: remove command and return value from opmode RX With the previous patch series, no opmode continues using the command or handler_status (i.e. the return value from the RX) so it can be removed now. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/dvm/agn.h | 5 ++--- drivers/net/wireless/iwlwifi/dvm/rx.c | 5 +---- drivers/net/wireless/iwlwifi/iwl-op-mode.h | 10 ++++------ drivers/net/wireless/iwlwifi/iwl-trans.h | 3 --- drivers/net/wireless/iwlwifi/mvm/ops.c | 13 +++++-------- drivers/net/wireless/iwlwifi/pcie/internal.h | 2 +- drivers/net/wireless/iwlwifi/pcie/rx.c | 12 +++--------- drivers/net/wireless/iwlwifi/pcie/tx.c | 5 +---- 8 files changed, 17 insertions(+), 38 deletions(-) (limited to 'drivers/net/wireless/iwlwifi/pcie/tx.c') diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index f549dc3852a6..101ef3109292 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -122,9 +122,8 @@ static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) void iwl_down(struct iwl_priv *priv); void iwl_cancel_deferred_work(struct iwl_priv *priv); void iwlagn_prepare_restart(struct iwl_priv *priv); -int __must_check iwl_rx_dispatch(struct iwl_op_mode *op_mode, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); +void iwl_rx_dispatch(struct iwl_op_mode *op_mode, + struct iwl_rx_cmd_buffer *rxb); bool iwl_check_for_ct_kill(struct iwl_priv *priv); diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index ca270a6234e7..c91374fe9a58 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -1073,8 +1073,7 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv) iwlagn_bt_rx_handler_setup(priv); } -int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) +void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); @@ -1098,6 +1097,4 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, iwl_dvm_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); } - - return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index ce1cdd7604e8..71b450adbda0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -148,8 +148,7 @@ struct iwl_op_mode_ops { const struct iwl_fw *fw, struct dentry *dbgfs_dir); void (*stop)(struct iwl_op_mode *op_mode); - int (*rx)(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); + void (*rx)(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb); void (*napi_add)(struct iwl_op_mode *op_mode, struct napi_struct *napi, struct net_device *napi_dev, @@ -188,11 +187,10 @@ static inline void iwl_op_mode_stop(struct iwl_op_mode *op_mode) op_mode->ops->stop(op_mode); } -static inline int iwl_op_mode_rx(struct iwl_op_mode *op_mode, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) +static inline void iwl_op_mode_rx(struct iwl_op_mode *op_mode, + struct iwl_rx_cmd_buffer *rxb) { - return op_mode->ops->rx(op_mode, rxb, cmd); + return op_mode->ops->rx(op_mode, rxb); } static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode, diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 87a230a7f4b6..7b6f095bac48 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -261,8 +261,6 @@ enum iwl_hcmd_dataflag { * @resp_pkt: response packet, if %CMD_WANT_SKB was set * @_rx_page_order: (internally used to free response packet) * @_rx_page_addr: (internally used to free response packet) - * @handler_status: return value of the handler of the command - * (put in setup_rx_handlers) - valid for SYNC mode only * @flags: can be CMD_* * @len: array of the lengths of the chunks in data * @dataflags: IWL_HCMD_DFL_* @@ -273,7 +271,6 @@ struct iwl_host_cmd { struct iwl_rx_packet *resp_pkt; unsigned long _rx_page_addr; u32 _rx_page_order; - int handler_status; u32 flags; u16 len[IWL_MAX_CMD_TBS_PER_TFD]; diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 00ca24064c02..51c2fd098c71 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -703,9 +703,8 @@ static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, } } -static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) +static void iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, + struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -713,7 +712,7 @@ static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) { iwl_mvm_rx_rx_mpdu(mvm, rxb); - return 0; + return; } iwl_mvm_rx_check_trigger(mvm, pkt); @@ -734,13 +733,13 @@ static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, if (!rx_h->async) { rx_h->fn(mvm, rxb); - return 0; + return; } entry = kzalloc(sizeof(*entry), GFP_ATOMIC); /* we can't do much... */ if (!entry) - return 0; + return; entry->rxb._page = rxb_steal_page(rxb); entry->rxb._offset = rxb->_offset; @@ -752,8 +751,6 @@ static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, schedule_work(&mvm->async_handlers_wk); break; } - - return 0; } static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 4f0640767d30..e27d5a3dcb86 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -423,7 +423,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans); int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); void iwl_pcie_hcmd_complete(struct iwl_trans *trans, - struct iwl_rx_cmd_buffer *rxb, int handler_status); + struct iwl_rx_cmd_buffer *rxb); void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs); void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 93062f2e8f56..32ec95bd4973 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -823,10 +823,9 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) { struct iwl_rx_packet *pkt; - struct iwl_device_cmd *cmd; u16 sequence; bool reclaim; - int index, cmd_index, err, len; + int index, cmd_index, len; struct iwl_rx_cmd_buffer rxcb = { ._offset = offset, ._rx_page_order = trans_pcie->rx_page_order, @@ -874,12 +873,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, index = SEQ_TO_INDEX(sequence); cmd_index = get_cmd_index(&txq->q, index); - if (reclaim) - cmd = txq->entries[cmd_index].cmd; - else - cmd = NULL; - - err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd); + iwl_op_mode_rx(trans->op_mode, &rxcb); if (reclaim) { kzfree(txq->entries[cmd_index].free_buf); @@ -897,7 +891,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, * iwl_trans_send_cmd() * as we reclaim the driver command queue */ if (!rxcb._page_stolen) - iwl_pcie_hcmd_complete(trans, &rxcb, err); + iwl_pcie_hcmd_complete(trans, &rxcb); else IWL_WARN(trans, "Claim null rxb?\n"); } diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index d711f0b8b62f..c6572ffb8ae2 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1553,15 +1553,13 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, /* * iwl_pcie_hcmd_complete - Pull unused buffers off the queue and reclaim them * @rxb: Rx buffer to reclaim - * @handler_status: return value of the handler of the command - * (put in setup_rx_handlers) * * If an Rx buffer has an async callback associated with it the callback * will be executed. The attached skb (if present) will only be freed * if the callback returns 1 */ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, - struct iwl_rx_cmd_buffer *rxb, int handler_status) + struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); @@ -1600,7 +1598,6 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, meta->source->resp_pkt = pkt; meta->source->_rx_page_addr = (unsigned long)page_address(p); meta->source->_rx_page_order = trans_pcie->rx_page_order; - meta->source->handler_status = handler_status; } iwl_pcie_cmdq_reclaim(trans, txq_id, index); -- cgit v1.2.3 From 31f920b63ae8ff5bf278a00dd8478e58ed89ff95 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 2 Jul 2015 14:53:02 +0300 Subject: iwlwifi: pcie: don't warn on long MPDUs when supported In iwlmvm firmwares, the Byte count written in the scheduler byte count table is in DWORDs and not in bytes. We should check that this value fits in the 12 bits and the value can be either in bits of in DWORD or bytes depending on the firmware. Check the value after the translation to DWORDs is done (if needed). Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/pcie/tx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/iwlwifi/pcie/tx.c') diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index c6572ffb8ae2..90d02d5f4881 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -219,8 +219,6 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; - WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); - sta_id = tx_cmd->sta_id; sec_ctl = tx_cmd->sec_ctl; @@ -239,6 +237,9 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, if (trans_pcie->bc_table_dword) len = DIV_ROUND_UP(len, 4); + if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX)) + return; + bc_ent = cpu_to_le16(len | (sta_id << 12)); scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; -- cgit v1.2.3 From 30f27df9ba44422b718d244a677b1cc6a5bd5ded Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 30 Jun 2015 14:45:40 +0200 Subject: iwlwifi: remove command header flags field The 'flags' field really has been reserved in the firmware API for a very long time, probably since 4965. As a consequence, the field is always 0 and checking for a IWL_CMD_FAILED_MSK flag makes no sense. Rename the field to 'reserved', get rid of IWL_CMD_FAILED_MSK and all the code for it. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/dvm/sta.c | 44 ++++++++++++-------------------- drivers/net/wireless/iwlwifi/iwl-trans.h | 6 +---- drivers/net/wireless/iwlwifi/mvm/fw.c | 7 ----- drivers/net/wireless/iwlwifi/mvm/nvm.c | 12 --------- drivers/net/wireless/iwlwifi/mvm/tdls.c | 9 +------ drivers/net/wireless/iwlwifi/mvm/utils.c | 5 ---- drivers/net/wireless/iwlwifi/pcie/tx.c | 2 +- 7 files changed, 19 insertions(+), 66 deletions(-) (limited to 'drivers/net/wireless/iwlwifi/pcie/tx.c') diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index c18aa3e950d5..0fa67d3b7235 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -65,12 +65,6 @@ static void iwl_process_add_sta_resp(struct iwl_priv *priv, { struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data; - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", - pkt->hdr.flags); - return; - } - IWL_DEBUG_INFO(priv, "Processing response for adding station\n"); spin_lock_bh(&priv->sta_lock); @@ -136,8 +130,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, add_sta_resp = (void *)pkt->data; /* debug messages are printed in the handler */ - if (!(pkt->hdr.flags & IWL_CMD_FAILED_MSK) && - add_sta_resp->status == ADD_STA_SUCCESS_MSK) { + if (add_sta_resp->status == ADD_STA_SUCCESS_MSK) { spin_lock_bh(&priv->sta_lock); ret = iwl_sta_ucode_activate(priv, sta_id); spin_unlock_bh(&priv->sta_lock); @@ -431,6 +424,7 @@ static int iwl_send_remove_station(struct iwl_priv *priv, struct iwl_rx_packet *pkt; int ret; struct iwl_rem_sta_cmd rm_sta_cmd; + struct iwl_rem_sta_resp *rem_sta_resp; struct iwl_host_cmd cmd = { .id = REPLY_REMOVE_STA, @@ -450,29 +444,23 @@ static int iwl_send_remove_station(struct iwl_priv *priv, return ret; pkt = cmd.resp_pkt; - 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; - } + rem_sta_resp = (void *)pkt->data; - if (!ret) { - struct iwl_rem_sta_resp *rem_sta_resp = (void *)pkt->data; - switch (rem_sta_resp->status) { - case REM_STA_SUCCESS_MSK: - if (!temporary) { - spin_lock_bh(&priv->sta_lock); - iwl_sta_ucode_deactivate(priv, sta_id); - spin_unlock_bh(&priv->sta_lock); - } - IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); - break; - default: - ret = -EIO; - IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); - break; + switch (rem_sta_resp->status) { + case REM_STA_SUCCESS_MSK: + if (!temporary) { + spin_lock_bh(&priv->sta_lock); + iwl_sta_ucode_deactivate(priv, sta_id); + spin_unlock_bh(&priv->sta_lock); } + IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); + break; + default: + ret = -EIO; + IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); + break; } + iwl_free_resp(&cmd); return ret; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 7b6f095bac48..64769e44059e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -130,7 +130,7 @@ */ struct iwl_cmd_header { u8 cmd; /* Command ID: REPLY_RXON, etc. */ - u8 flags; /* 0:5 reserved, 6 abort, 7 internal */ + u8 reserved; /* * The driver sets up the sequence number to values of its choosing. * uCode does not use this value, but passes it back to the driver @@ -154,10 +154,6 @@ struct iwl_cmd_header { __le16 sequence; } __packed; -/* iwl_cmd_header flags value */ -#define IWL_CMD_FAILED_MSK 0x40 - - #define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */ #define FH_RSCSR_FRAME_INVALID 0x55550000 #define FH_RSCSR_FRAME_ALIGN 0x40 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index a5dae09869fc..e905c07127fb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -444,12 +444,6 @@ static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) return; pkt = cmd.resp_pkt; - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(mvm, "Bad return from SHARED_MEM_CFG (0x%08X)\n", - pkt->hdr.flags); - goto exit; - } - mem_cfg = (void *)pkt->data; mvm->shared_mem_cfg.shared_mem_addr = @@ -473,7 +467,6 @@ static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) le32_to_cpu(mem_cfg->page_buff_size); IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n"); -exit: iwl_free_resp(&cmd); } diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 6dcdee725ff8..328187da7541 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -139,12 +139,6 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, return ret; pkt = cmd.resp_pkt; - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(mvm, "Bad return from NVM_ACCES_COMMAND (0x%08X)\n", - pkt->hdr.flags); - ret = -EIO; - goto exit; - } /* Extract NVM response */ nvm_resp = (void *)pkt->data; @@ -652,12 +646,6 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, return ERR_PTR(ret); pkt = cmd.resp_pkt; - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(mvm, "Bad return from MCC_UPDATE_COMMAND (0x%08X)\n", - pkt->hdr.flags); - ret = -EIO; - goto exit; - } /* Extract MCC response */ mcc_resp = (void *)pkt->data; diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/iwlwifi/mvm/tdls.c index 71802e7f7448..d44d02d16fe2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/iwlwifi/mvm/tdls.c @@ -169,18 +169,11 @@ static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return; pkt = cmd.resp_pkt; - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(mvm, "Bad return from TDLS_CONFIG_COMMAND (0x%08X)\n", - pkt->hdr.flags); - goto exit; - } - if (WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp))) - goto exit; + WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp)); /* we don't really care about the response at this point */ -exit: iwl_free_resp(&cmd); } diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 3b016f95a8ed..f50ef822d65e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -166,11 +166,6 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, goto out_free_resp; } - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - ret = -EIO; - goto out_free_resp; - } - resp_len = iwl_rx_packet_payload_len(pkt); if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { ret = -EIO; diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 90d02d5f4881..553ae135464a 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1420,7 +1420,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, /* set up the header */ out_cmd->hdr.cmd = cmd->id; - out_cmd->hdr.flags = 0; + out_cmd->hdr.reserved = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | INDEX_TO_SEQ(q->write_ptr)); -- cgit v1.2.3 From ab02165ccec4c78162501acedeef1a768acdb811 Mon Sep 17 00:00:00 2001 From: Aviya Erenfeld Date: Tue, 9 Jun 2015 16:45:52 +0300 Subject: iwlwifi: add wide firmware command infrastructure for TX As the firmware is slowly running out of command IDs and grouping of commands is desirable anyway, the firmware is extending the command header from 4 bytes to 8 bytes to introduce a group (in place of the former flags field, since that's always 0 on commands and thus can be easily used to distinguish between the two. In order to support this most easily in the driver widen the command command ID used in the command sending functions and encode the new values (group and version) in the ID. That way existing code doesn't have to be changed (since the higher bits are 0 automatically) and newer code can easily use the new ID generation function to create a value to use in place of just the command ID. Signed-off-by: Aviya Erenfeld Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- .../net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h | 14 +++-- drivers/net/wireless/iwlwifi/iwl-fw-file.h | 2 + drivers/net/wireless/iwlwifi/iwl-trans.h | 66 ++++++++++++++++++++-- drivers/net/wireless/iwlwifi/mvm/mvm.h | 4 +- drivers/net/wireless/iwlwifi/mvm/ops.c | 2 + drivers/net/wireless/iwlwifi/mvm/utils.c | 4 +- drivers/net/wireless/iwlwifi/pcie/internal.h | 2 + drivers/net/wireless/iwlwifi/pcie/trans.c | 1 + drivers/net/wireless/iwlwifi/pcie/tx.c | 53 ++++++++++++----- 9 files changed, 121 insertions(+), 27 deletions(-) (limited to 'drivers/net/wireless/iwlwifi/pcie/tx.c') diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h index 948ce0802fa7..eb4b99a1c8cd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h @@ -36,7 +36,7 @@ TRACE_EVENT(iwlwifi_dev_hcmd, TP_PROTO(const struct device *dev, struct iwl_host_cmd *cmd, u16 total_size, - struct iwl_cmd_header *hdr), + struct iwl_cmd_header_wide *hdr), TP_ARGS(dev, cmd, total_size, hdr), TP_STRUCT__entry( DEV_ENTRY @@ -44,11 +44,14 @@ TRACE_EVENT(iwlwifi_dev_hcmd, __field(u32, flags) ), TP_fast_assign( - int i, offset = sizeof(*hdr); + int i, offset = sizeof(struct iwl_cmd_header); + + if (hdr->group_id) + offset = sizeof(struct iwl_cmd_header_wide); DEV_ASSIGN; __entry->flags = cmd->flags; - memcpy(__get_dynamic_array(hcmd), hdr, sizeof(*hdr)); + memcpy(__get_dynamic_array(hcmd), hdr, offset); for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { if (!cmd->len[i]) @@ -58,8 +61,9 @@ TRACE_EVENT(iwlwifi_dev_hcmd, offset += cmd->len[i]; } ), - TP_printk("[%s] hcmd %#.2x (%ssync)", - __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[0], + TP_printk("[%s] hcmd %#.2x.%#.2x (%ssync)", + __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[1], + ((u8 *)__get_dynamic_array(hcmd))[0], __entry->flags & CMD_ASYNC ? "a" : "") ); diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index c31cf828fb25..f99ec4e22052 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -247,6 +247,7 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. * IWL_UCODE_TLV_API_HDC_PHASE_0: ucode supports finer configuration of LTR * @IWL_UCODE_TLV_API_TX_POWER_DEV: new API for tx power. + * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header * @IWL_UCODE_TLV_API_SCD_CFG: This firmware can configure the scheduler * through the dedicated host command. * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too. @@ -263,6 +264,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9, IWL_UCODE_TLV_API_HDC_PHASE_0 = (__force iwl_ucode_tlv_api_t)10, IWL_UCODE_TLV_API_TX_POWER_DEV = (__force iwl_ucode_tlv_api_t)11, + IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14, IWL_UCODE_TLV_API_SCD_CFG = (__force iwl_ucode_tlv_api_t)15, IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = (__force iwl_ucode_tlv_api_t)16, IWL_UCODE_TLV_API_ASYNC_DTM = (__force iwl_ucode_tlv_api_t)17, diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 64769e44059e..df71aa3e6401 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -122,6 +122,32 @@ #define INDEX_TO_SEQ(i) ((i) & 0xff) #define SEQ_RX_FRAME cpu_to_le16(0x8000) +/* + * those functions retrieve specific information from + * the id field in the iwl_host_cmd struct which contains + * the command id, the group id and the version of the command + * and vice versa +*/ +static inline u8 iwl_cmd_opcode(u32 cmdid) +{ + return cmdid & 0xFF; +} + +static inline u8 iwl_cmd_groupid(u32 cmdid) +{ + return ((cmdid & 0xFF00) >> 8); +} + +static inline u8 iwl_cmd_version(u32 cmdid) +{ + return ((cmdid & 0xFF0000) >> 16); +} + +static inline u32 iwl_cmd_id(u8 opcode, u8 groupid, u8 version) +{ + return opcode + (groupid << 8) + (version << 16); +} + /** * struct iwl_cmd_header * @@ -130,7 +156,7 @@ */ struct iwl_cmd_header { u8 cmd; /* Command ID: REPLY_RXON, etc. */ - u8 reserved; + u8 group_id; /* * The driver sets up the sequence number to values of its choosing. * uCode does not use this value, but passes it back to the driver @@ -154,6 +180,23 @@ struct iwl_cmd_header { __le16 sequence; } __packed; +/** + * struct iwl_cmd_header_wide + * + * This header format appears in the beginning of each command sent from the + * driver, and each response/notification received from uCode. + * this is the wide version that contains more information about the command + * like length, version and command type + */ +struct iwl_cmd_header_wide { + u8 cmd; + u8 group_id; + __le16 sequence; + __le16 length; + u8 reserved; + u8 version; +} __packed; + #define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */ #define FH_RSCSR_FRAME_INVALID 0x55550000 #define FH_RSCSR_FRAME_ALIGN 0x40 @@ -218,8 +261,18 @@ enum CMD_MODE { * aren't fully copied and use other TFD space. */ struct iwl_device_cmd { - struct iwl_cmd_header hdr; /* uCode API */ - u8 payload[DEF_CMD_PAYLOAD_SIZE]; + union { + struct { + struct iwl_cmd_header hdr; /* uCode API */ + u8 payload[DEF_CMD_PAYLOAD_SIZE]; + }; + struct { + struct iwl_cmd_header_wide hdr_wide; + u8 payload_wide[DEF_CMD_PAYLOAD_SIZE - + sizeof(struct iwl_cmd_header_wide) + + sizeof(struct iwl_cmd_header)]; + }; + }; } __packed; #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) @@ -260,7 +313,8 @@ enum iwl_hcmd_dataflag { * @flags: can be CMD_* * @len: array of the lengths of the chunks in data * @dataflags: IWL_HCMD_DFL_* - * @id: id of the host command + * @id: command id of the host command, for wide commands encoding the + * version and group as well */ struct iwl_host_cmd { const void *data[IWL_MAX_CMD_TBS_PER_TFD]; @@ -269,9 +323,9 @@ struct iwl_host_cmd { u32 _rx_page_order; u32 flags; + u32 id; u16 len[IWL_MAX_CMD_TBS_PER_TFD]; u8 dataflags[IWL_MAX_CMD_TBS_PER_TFD]; - u8 id; }; static inline void iwl_free_resp(struct iwl_host_cmd *cmd) @@ -372,6 +426,7 @@ enum iwl_trans_status { * @bc_table_dword: set to true if the BC table expects the byte count to be * in DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue + * @wide_cmd_header: firmware supports wide host command header * @command_names: array of command names, must be 256 entries * (one for each command); for debugging only * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until @@ -389,6 +444,7 @@ struct iwl_trans_config { bool rx_buf_size_8k; bool bc_table_dword; bool scd_set_active; + bool wide_cmd_header; const char *const *command_names; u32 sdio_adma_addr; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index c980e8d9ac5a..d78af9f0f81b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -977,12 +977,12 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); /* Tx / Host Commands */ int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd); -int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id, +int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, u32 flags, u16 len, const void *data); int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, u32 *status); -int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id, +int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len, const void *data, u32 *status); int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 76b5557c1dde..c9b9c98c6ff8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -469,6 +469,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; + trans_cfg.wide_cmd_header = fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_WIDE_CMD_HDR); if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) trans_cfg.bc_table_dword = true; diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index f50ef822d65e..a7d434256423 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -108,7 +108,7 @@ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd) return ret; } -int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id, +int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, u32 flags, u16 len, const void *data) { struct iwl_host_cmd cmd = { @@ -182,7 +182,7 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, /* * We assume that the caller set the status to the sucess value */ -int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id, u16 len, +int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len, const void *data, u32 *status) { struct iwl_host_cmd cmd = { diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index e27d5a3dcb86..17f65dc89472 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -299,6 +299,7 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) * @rx_buf_size_8k: 8 kB RX buffer size * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue + * @wide_cmd_header: true when ucode supports wide command header format * @rx_page_order: page order for receive buffer size * @reg_lock: protect hw register access * @mutex: to protect stop_device / start_fw / start_hw @@ -352,6 +353,7 @@ struct iwl_trans_pcie { bool rx_buf_size_8k; bool bc_table_dword; bool scd_set_active; + bool wide_cmd_header; u32 rx_page_order; const char *const *command_names; diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 4cdfb2fd992a..88ab79fc6249 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1394,6 +1394,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, else trans_pcie->rx_page_order = get_order(4 * 1024); + trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header; trans_pcie->command_names = trans_cfg->command_names; trans_pcie->bc_table_dword = trans_cfg->bc_table_dword; trans_pcie->scd_set_active = trans_cfg->scd_set_active; diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 553ae135464a..3a5d54ed494f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1322,13 +1322,23 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, int idx; u16 copy_size, cmd_size, scratch_size; bool had_nocopy = false; + u8 group_id = iwl_cmd_groupid(cmd->id); int i, ret; u32 cmd_pos; const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; - copy_size = sizeof(out_cmd->hdr); - cmd_size = sizeof(out_cmd->hdr); + if (WARN(!trans_pcie->wide_cmd_header && group_id != 0, + "unsupported wide command %#x\n", cmd->id)) + return -EINVAL; + + if (group_id != 0) { + copy_size = sizeof(struct iwl_cmd_header_wide); + cmd_size = sizeof(struct iwl_cmd_header_wide); + } else { + copy_size = sizeof(struct iwl_cmd_header); + cmd_size = sizeof(struct iwl_cmd_header); + } /* need one for the header if the first is NOCOPY */ BUILD_BUG_ON(IWL_MAX_CMD_TBS_PER_TFD > IWL_NUM_OF_TBS - 1); @@ -1418,16 +1428,32 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, out_meta->source = cmd; /* set up the header */ - - out_cmd->hdr.cmd = cmd->id; - out_cmd->hdr.reserved = 0; - out_cmd->hdr.sequence = - cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | - INDEX_TO_SEQ(q->write_ptr)); + if (group_id != 0) { + 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(q->write_ptr)); + + cmd_pos = sizeof(struct iwl_cmd_header_wide); + copy_size = sizeof(struct iwl_cmd_header_wide); + } else { + out_cmd->hdr.cmd = iwl_cmd_opcode(cmd->id); + out_cmd->hdr.sequence = + cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | + INDEX_TO_SEQ(q->write_ptr)); + out_cmd->hdr.group_id = 0; + + cmd_pos = sizeof(struct iwl_cmd_header); + copy_size = sizeof(struct iwl_cmd_header); + } /* and copy the data that needs to be copied */ - cmd_pos = offsetof(struct iwl_device_cmd, payload); - copy_size = sizeof(out_cmd->hdr); for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { int copy; @@ -1466,9 +1492,10 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, } IWL_DEBUG_HC(trans, - "Sending command %s (#%x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", + "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", get_cmd_string(trans_pcie, out_cmd->hdr.cmd), - out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), + group_id, out_cmd->hdr.cmd, + le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); /* start the TFD with the scratchbuf */ @@ -1523,7 +1550,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, 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); + trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide); /* start timer if queue currently empty */ if (q->read_ptr == q->write_ptr && txq->wd_timeout) -- cgit v1.2.3 From 88742c9e84f73eeea4abe967d751b0e6318c2c2e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 30 Jun 2015 15:31:22 +0200 Subject: iwlwifi: mvm: move existing UMAC commands to group 1 Existing UMAC commands already use the long header, but are sent with group 0 and the long header inserted manually. Move them to the group 1 to take advantage of the header building in the low- level transport. Existing firmware ignores the group_id field (it's reserved) and the first firmware that really supports long command headers can parse all commands in both group 0 (with short header) and group 1 (with long header.) Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-trans.h | 5 +++++ drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h | 18 --------------- drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h | 5 ----- drivers/net/wireless/iwlwifi/mvm/scan.c | 22 ++++++++---------- drivers/net/wireless/iwlwifi/mvm/tof.c | 31 ++++++++++---------------- drivers/net/wireless/iwlwifi/pcie/tx.c | 3 ++- 6 files changed, 28 insertions(+), 56 deletions(-) (limited to 'drivers/net/wireless/iwlwifi/pcie/tx.c') diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index df71aa3e6401..8f67004faf4d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -148,6 +148,11 @@ static inline u32 iwl_cmd_id(u8 opcode, u8 groupid, u8 version) return opcode + (groupid << 8) + (version << 16); } +/* due to the conversion, this group is special; new groups + * should be defined in the appropriate fw-api header files + */ +#define IWL_ALWAYS_LONG_GROUP 1 + /** * struct iwl_cmd_header * diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 5e4cbdb44c60..4787c32b3a61 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -550,18 +550,6 @@ struct iwl_periodic_scan_complete { /* UMAC Scan API */ -/** - * struct iwl_mvm_umac_cmd_hdr - Command header for UMAC commands - * @size: size of the command (not including header) - * @reserved0: for future use and alignment - * @ver: API version number - */ -struct iwl_mvm_umac_cmd_hdr { - __le16 size; - u8 reserved0; - u8 ver; -} __packed; - /* The maximum of either of these cannot exceed 8, because we use an * 8-bit mask (see IWL_MVM_SCAN_MASK in mvm.h). */ @@ -621,7 +609,6 @@ enum iwl_channel_flags { /** * struct iwl_scan_config - * @hdr: umac command header * @flags: enum scan_config_flags * @tx_chains: valid_tx antenna - ANT_* definitions * @rx_chains: valid_rx antenna - ANT_* definitions @@ -639,7 +626,6 @@ enum iwl_channel_flags { * @channel_array: default supported channels */ struct iwl_scan_config { - struct iwl_mvm_umac_cmd_hdr hdr; __le32 flags; __le32 tx_chains; __le32 rx_chains; @@ -734,7 +720,6 @@ struct iwl_scan_req_umac_tail { /** * struct iwl_scan_req_umac - * @hdr: umac command header * @flags: &enum iwl_umac_scan_flags * @uid: scan id, &enum iwl_umac_scan_uid_offsets * @ooc_priority: out of channel priority - &enum iwl_scan_priority @@ -753,7 +738,6 @@ struct iwl_scan_req_umac_tail { * &struct iwl_scan_req_umac_tail */ struct iwl_scan_req_umac { - struct iwl_mvm_umac_cmd_hdr hdr; __le32 flags; __le32 uid; __le32 ooc_priority; @@ -775,12 +759,10 @@ struct iwl_scan_req_umac { /** * struct iwl_umac_scan_abort - * @hdr: umac command header * @uid: scan id, &enum iwl_umac_scan_uid_offsets * @flags: reserved */ struct iwl_umac_scan_abort { - struct iwl_mvm_umac_cmd_hdr hdr; __le32 uid; __le32 flags; } __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h index 287c9ecb0fb1..eed6271d01a3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h @@ -88,7 +88,6 @@ enum iwl_mvm_tof_sub_grp_ids { * @is_buf_required: 1 channel estimation buffer required, 0 - otherwise */ struct iwl_tof_config_cmd { - struct iwl_mvm_umac_cmd_hdr hdr; __le32 sub_grp_cmd_id; u8 tof_disabled; u8 one_sided_disabled; @@ -136,7 +135,6 @@ struct iwl_tof_config_cmd { * @bssid: Current AP BSSID */ struct iwl_tof_responder_config_cmd { - struct iwl_mvm_umac_cmd_hdr hdr; __le32 sub_grp_cmd_id; __le16 burst_period; u8 min_delta_ftm; @@ -171,7 +169,6 @@ struct iwl_tof_responder_config_cmd { * value to be sent to the AP */ struct iwl_tof_range_req_ext_cmd { - struct iwl_mvm_umac_cmd_hdr hdr; __le32 sub_grp_cmd_id; __le16 tsf_timer_offset_msec; __le16 reserved; @@ -273,7 +270,6 @@ enum iwl_tof_response_mode { * Bits set to 1 shall be randomized by the UMAC */ struct iwl_tof_range_req_cmd { - struct iwl_mvm_umac_cmd_hdr hdr; __le32 sub_grp_cmd_id; u8 request_id; u8 initiator; @@ -382,7 +378,6 @@ struct iwl_tof_neighbor_report { * @request_id: corresponds to a range request */ struct iwl_tof_range_abort_cmd { - struct iwl_mvm_umac_cmd_hdr hdr; __le32 sub_grp_cmd_id; u8 request_id; u8 reserved[3]; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index b891fa54fa4a..d34b2e578358 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -884,9 +884,9 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) int num_channels = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; - int ret, i, j = 0, cmd_size, data_size; + int ret, i, j = 0, cmd_size; struct iwl_host_cmd cmd = { - .id = SCAN_CFG_CMD, + .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0), }; if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) @@ -898,8 +898,6 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) if (!scan_config) return -ENOMEM; - data_size = cmd_size - sizeof(struct iwl_mvm_umac_cmd_hdr); - scan_config->hdr.size = cpu_to_le16(data_size); scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE | SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS | SCAN_CONFIG_FLAG_SET_TX_CHAINS | @@ -1045,8 +1043,6 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return uid; memset(cmd, 0, ksize(cmd)); - cmd->hdr.size = cpu_to_le16(iwl_mvm_scan_size(mvm) - - sizeof(struct iwl_mvm_umac_cmd_hdr)); iwl_mvm_scan_umac_dwell(mvm, cmd, params); @@ -1183,7 +1179,7 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - hcmd.id = SCAN_REQ_UMAC; + hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, IWL_MVM_SCAN_REGULAR); } else { @@ -1291,7 +1287,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - hcmd.id = SCAN_REQ_UMAC; + hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, IWL_MVM_SCAN_SCHED); } else { hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; @@ -1369,10 +1365,7 @@ void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) { - struct iwl_umac_scan_abort cmd = { - .hdr.size = cpu_to_le16(sizeof(struct iwl_umac_scan_abort) - - sizeof(struct iwl_mvm_umac_cmd_hdr)), - }; + struct iwl_umac_scan_abort cmd = {}; int uid, ret; lockdep_assert_held(&mvm->mutex); @@ -1389,7 +1382,10 @@ static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid); - ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_UMAC, 0, sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, + iwl_cmd_id(SCAN_ABORT_UMAC, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(cmd), &cmd); if (!ret) mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT; diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.c b/drivers/net/wireless/iwlwifi/mvm/tof.c index 5a8e96f0293d..d060e12c3239 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tof.c +++ b/drivers/net/wireless/iwlwifi/mvm/tof.c @@ -74,33 +74,21 @@ void iwl_mvm_tof_init(struct iwl_mvm *mvm) memset(tof_data, 0, sizeof(*tof_data)); - tof_data->tof_cfg.hdr.size = - cpu_to_le16(sizeof(struct iwl_tof_config_cmd) - - sizeof(struct iwl_mvm_umac_cmd_hdr)); tof_data->tof_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_CONFIG_CMD); #ifdef CONFIG_IWLWIFI_DEBUGFS if (IWL_MVM_TOF_IS_RESPONDER) { - tof_data->responder_cfg.hdr.size = - cpu_to_le16(sizeof(struct iwl_tof_responder_config_cmd) - - sizeof(struct iwl_mvm_umac_cmd_hdr)); tof_data->responder_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_RESPONDER_CONFIG_CMD); tof_data->responder_cfg.sta_id = IWL_MVM_STATION_COUNT; } #endif - tof_data->range_req.hdr.size = - cpu_to_le16(sizeof(struct iwl_tof_range_req_cmd) - - sizeof(struct iwl_mvm_umac_cmd_hdr)); tof_data->range_req.sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_REQ_CMD); tof_data->range_req.req_timeout = 1; tof_data->range_req.initiator = 1; tof_data->range_req.report_policy = 3; - tof_data->range_req_ext.hdr.size = - cpu_to_le16(sizeof(struct iwl_tof_range_req_ext_cmd) - - sizeof(struct iwl_mvm_umac_cmd_hdr)); tof_data->range_req_ext.sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_REQ_EXT_CMD); @@ -147,15 +135,15 @@ int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm) } mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; - return iwl_mvm_send_cmd_pdu(mvm, TOF_CMD, 0, sizeof(*cmd), cmd); + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(*cmd), cmd); } int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id) { struct iwl_tof_range_abort_cmd cmd = { .sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_ABORT_CMD), - .hdr.size = cpu_to_le16(sizeof(struct iwl_tof_range_abort_cmd) - - sizeof(struct iwl_mvm_umac_cmd_hdr)), .request_id = id, }; @@ -173,7 +161,9 @@ int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id) /* after abort is sent there's no active request anymore */ mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; - return iwl_mvm_send_cmd_pdu(mvm, TOF_CMD, 0, sizeof(cmd), &cmd); + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(cmd), &cmd); } #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -194,7 +184,9 @@ int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm, } cmd->sta_id = mvmvif->bcast_sta.sta_id; - return iwl_mvm_send_cmd_pdu(mvm, TOF_CMD, 0, sizeof(*cmd), cmd); + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(*cmd), cmd); } #endif @@ -245,8 +237,9 @@ int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm, return -EIO; } - return iwl_mvm_send_cmd_pdu(mvm, TOF_CMD, 0, - sizeof(mvm->tof_data.range_req_ext), + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(mvm->tof_data.range_req_ext), &mvm->tof_data.range_req_ext); } diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 3a5d54ed494f..601eee1ad60b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1328,7 +1328,8 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; - if (WARN(!trans_pcie->wide_cmd_header && group_id != 0, + if (WARN(!trans_pcie->wide_cmd_header && + group_id > IWL_ALWAYS_LONG_GROUP, "unsupported wide command %#x\n", cmd->id)) return -EINVAL; -- cgit v1.2.3 From 206eea7833859f9e39c8a7439196743a2d8dfc9b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 17 Apr 2015 16:38:31 +0200 Subject: iwlwifi: pcie: support frag SKBs Allow frag SKBs in PCIe and advertise the maximum number of frags to the opmode. As a fallback. linearize the SKB if it exceeds the maximum number of fragments. This allows using the hardware better (filling more TBs) and should improve performance when used by the opmode. Also adjust tracing to be able to deal with this. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-devtrace-data.h | 7 ++- drivers/net/wireless/iwlwifi/iwl-trans.h | 7 +++ drivers/net/wireless/iwlwifi/pcie/internal.h | 6 ++ drivers/net/wireless/iwlwifi/pcie/trans.c | 2 + drivers/net/wireless/iwlwifi/pcie/tx.c | 70 ++++++++++++++++++++---- 5 files changed, 77 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless/iwlwifi/pcie/tx.c') diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h index 04e6649340b8..71a78cede9b0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h @@ -35,8 +35,8 @@ TRACE_EVENT(iwlwifi_dev_tx_data, TP_PROTO(const struct device *dev, struct sk_buff *skb, - void *data, size_t data_len), - TP_ARGS(dev, skb, data, data_len), + u8 hdr_len, size_t data_len), + TP_ARGS(dev, skb, hdr_len, data_len), TP_STRUCT__entry( DEV_ENTRY @@ -45,7 +45,8 @@ TRACE_EVENT(iwlwifi_dev_tx_data, TP_fast_assign( DEV_ASSIGN; if (iwl_trace_data(skb)) - memcpy(__get_dynamic_array(data), data, data_len); + skb_copy_bits(skb, hdr_len, + __get_dynamic_array(data), data_len); ), TP_printk("[%s] TX frame data", __get_str(dev)) ); diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 2f79e54823c4..e68497acf9b3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -248,6 +248,8 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt) * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle. * @CMD_WAKE_UP_TRANS: The command response should wake up the trans * (i.e. mark it as non-idle). + * @CMD_TB_BITMAP_POS: Position of the first bit for the TB bitmap. We need to + * check that we leave enough room for the TBs bitmap which needs 20 bits. */ enum CMD_MODE { CMD_ASYNC = BIT(0), @@ -257,6 +259,8 @@ enum CMD_MODE { CMD_SEND_IN_IDLE = BIT(4), CMD_MAKE_TRANS_IDLE = BIT(5), CMD_WAKE_UP_TRANS = BIT(6), + + CMD_TB_BITMAP_POS = 11, }; #define DEF_CMD_PAYLOAD_SIZE 320 @@ -641,6 +645,8 @@ enum iwl_d0i3_mode { * @cfg - pointer to the configuration * @status: a bit-mask of transport status flags * @dev - pointer to struct device * that represents the device + * @max_skb_frags: maximum number of fragments an SKB can have when transmitted. + * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported. * @hw_id: a u32 with the ID of the device / sub-device. * Set during transport allocation. * @hw_id_str: a string with info about HW ID. Set during transport allocation. @@ -669,6 +675,7 @@ struct iwl_trans { unsigned long status; struct device *dev; + u32 max_skb_frags; u32 hw_rev; u32 hw_id; char hw_id_str[52]; diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 17f65dc89472..feb2f7e81134 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -44,6 +44,12 @@ #include "iwl-io.h" #include "iwl-op-mode.h" +/* We need 2 entries for the TX command and header, and another one might + * be needed for potential data in the SKB's head. The remaining ones can + * be used for frags. + */ +#define IWL_PCIE_MAX_FRAGS (IWL_NUM_OF_TBS - 3) + /* * RX related structures and functions */ diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 9ebdd8009717..fb55810f5aae 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -2627,6 +2627,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, if (!trans) return ERR_PTR(-ENOMEM); + trans->max_skb_frags = IWL_PCIE_MAX_FRAGS; + trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); trans_pcie->trans = trans; diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 601eee1ad60b..b7d1268bf969 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -388,11 +388,18 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, /* first TB is never freed - it's the scratchbuf data */ - for (i = 1; i < num_tbs; i++) - dma_unmap_single(trans->dev, iwl_pcie_tfd_tb_get_addr(tfd, i), - iwl_pcie_tfd_tb_get_len(tfd, i), - DMA_TO_DEVICE); - + for (i = 1; i < num_tbs; i++) { + if (meta->flags & BIT(i + CMD_TB_BITMAP_POS)) + dma_unmap_page(trans->dev, + iwl_pcie_tfd_tb_get_addr(tfd, i), + iwl_pcie_tfd_tb_get_len(tfd, i), + DMA_TO_DEVICE); + else + dma_unmap_single(trans->dev, + iwl_pcie_tfd_tb_get_addr(tfd, i), + iwl_pcie_tfd_tb_get_len(tfd, i), + DMA_TO_DEVICE); + } tfd->num_tbs = 0; } @@ -468,7 +475,7 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len); - return 0; + return num_tbs; } static int iwl_pcie_txq_alloc(struct iwl_trans *trans, @@ -1546,6 +1553,8 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false); } + BUILD_BUG_ON(IWL_NUM_OF_TBS + CMD_TB_BITMAP_POS > + sizeof(out_meta->flags) * BITS_PER_BYTE); out_meta->flags = cmd->flags; if (WARN_ON_ONCE(txq->entries[idx].free_buf)) kzfree(txq->entries[idx].free_buf); @@ -1789,7 +1798,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_hdr *hdr; struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; struct iwl_cmd_meta *out_meta; struct iwl_txq *txq; @@ -1798,9 +1807,10 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, void *tb1_addr; u16 len, tb1_len, tb2_len; bool wait_write_ptr; - __le16 fc = hdr->frame_control; - u8 hdr_len = ieee80211_hdrlen(fc); + __le16 fc; + u8 hdr_len; u16 wifi_seq; + int i; txq = &trans_pcie->txq[txq_id]; q = &txq->q; @@ -1809,6 +1819,18 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, "TX on unused queue %d\n", txq_id)) return -EINVAL; + if (skb_is_nonlinear(skb) && + skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS && + __skb_linearize(skb)) + return -ENOMEM; + + /* mac80211 always puts the full header into the SKB's head, + * so there's no need to check if it's readable there + */ + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + hdr_len = ieee80211_hdrlen(fc); + spin_lock(&txq->lock); /* In AGG mode, the index in the ring must correspond to the WiFi @@ -1839,6 +1861,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, /* Set up first empty entry in queue's array of Tx/cmd buffers */ out_meta = &txq->entries[q->write_ptr].meta; + out_meta->flags = 0; /* * The second TB (tb1) points to the remainder of the TX command @@ -1872,9 +1895,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, /* * Set up TFD's third entry to point directly to remainder - * of skb, if any (802.11 null frames have no payload). + * of skb's head, if any */ - tb2_len = skb->len - hdr_len; + tb2_len = skb_headlen(skb) - hdr_len; if (tb2_len > 0) { dma_addr_t tb2_phys = dma_map_single(trans->dev, skb->data + hdr_len, @@ -1887,6 +1910,29 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false); } + /* set up the remaining entries to point to the data */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + dma_addr_t tb_phys; + int tb_idx; + + if (!skb_frag_size(frag)) + continue; + + tb_phys = skb_frag_dma_map(trans->dev, frag, 0, + skb_frag_size(frag), DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr]); + goto out_err; + } + tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys, + skb_frag_size(frag), false); + + out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS); + } + /* Set up entry for this TFD in Tx byte-count array */ iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); @@ -1896,7 +1942,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, skb->data + hdr_len, tb2_len); trace_iwlwifi_dev_tx_data(trans->dev, skb, - skb->data + hdr_len, tb2_len); + hdr_len, skb->len - hdr_len); wait_write_ptr = ieee80211_has_morefrags(fc); -- cgit v1.2.3