diff options
author | John W. Linville <linville@tuxdriver.com> | 2012-03-09 23:57:30 +0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-03-09 23:57:30 +0400 |
commit | 74dd1521d0b4f940cdd3ce7b9d988836bef589b8 (patch) | |
tree | 42f3d4ee1d4e6d60a91aaadab771f766a3024bc0 /drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | |
parent | 2f2d76cc3e938389feee671b46252dde6880b3b7 (diff) | |
parent | 1745e4405b2c0da6db2ec4b6bc0ad930612d8295 (diff) | |
download | linux-74dd1521d0b4f940cdd3ce7b9d988836bef589b8.tar.xz |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | 164 |
1 files changed, 97 insertions, 67 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c index 4e1e74e1b794..eb430b6e1cde 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c @@ -41,6 +41,43 @@ #define IWL_TX_CRC_SIZE 4 #define IWL_TX_DELIMITER_SIZE 4 +/* + * mac80211 queues, ACs, hardware queues, FIFOs. + * + * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues + * + * Mac80211 uses the following numbers, which we get as from it + * by way of skb_get_queue_mapping(skb): + * + * VO 0 + * VI 1 + * BE 2 + * BK 3 + * + * + * Regular (not A-MPDU) frames are put into hardware queues corresponding + * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their + * own queue per aggregation session (RA/TID combination), such queues are + * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In + * order to map frames to the right queue, we also need an AC->hw queue + * mapping. This is implemented here. + * + * Due to the way hw queues are set up (by the hw specific code), the AC->hw + * queue mapping is the identity mapping. + */ + +static const u8 tid_to_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO +}; + + /** * iwl_trans_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array */ @@ -99,7 +136,7 @@ void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq) if (txq->need_update == 0) return; - if (hw_params(trans).shadow_reg_enable) { + if (cfg(trans)->base_params->shadow_reg_enable) { /* shadow register enabled */ iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); @@ -217,6 +254,8 @@ void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, { struct iwl_tfd *tfd_tmp = txq->tfds; + lockdep_assert_held(&txq->lock); + iwlagn_unmap_tfd(trans, &txq->meta[index], &tfd_tmp[index], dma_dir); /* free SKB */ @@ -358,7 +397,7 @@ static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); - if (txq_id != trans->shrd->cmd_queue) + if (txq_id != trans_pcie->cmd_queue) sta_id = tx_cmd->sta_id; bc_ent = cpu_to_le16(1 | (sta_id << 12)); @@ -440,6 +479,15 @@ void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, scd_retry ? "BA" : "AC/CMD", txq_id); } +static inline int get_ac_from_tid(u16 tid) +{ + if (likely(tid < ARRAY_SIZE(tid_to_ac))) + return tid_to_ac[tid]; + + /* no support for TIDs 8-15 yet */ + return -EINVAL; +} + static inline int get_fifo_from_tid(struct iwl_trans_pcie *trans_pcie, u8 ctx, u16 tid) { @@ -547,7 +595,8 @@ static int iwlagn_txq_ctx_activate_free(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int txq_id; - for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++) + for (txq_id = 0; txq_id < cfg(trans)->base_params->num_of_queues; + txq_id++) if (!test_and_set_bit(txq_id, &trans_pcie->txq_ctx_active_msk)) return txq_id; @@ -616,15 +665,13 @@ int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int sta_id, int tid) 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->shrd->cmd_queue]; + 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; - unsigned long flags; u32 idx; u16 copy_size, cmd_size; - bool is_ct_kill = false; bool had_nocopy = false; int i; u8 *cmd_dest; @@ -639,12 +686,6 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) return -EIO; } - if ((trans->shrd->ucode_owner == IWL_OWNERSHIP_TM) && - !(cmd->flags & CMD_ON_DEMAND)) { - IWL_DEBUG_HC(trans, "tm own the uCode, no regular hcmd send\n"); - return -EIO; - } - copy_size = sizeof(out_cmd->hdr); cmd_size = sizeof(out_cmd->hdr); @@ -674,23 +715,13 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE)) return -EINVAL; - if (iwl_is_rfkill(trans->shrd) || iwl_is_ctkill(trans->shrd)) { - IWL_WARN(trans, "Not sending command - %s KILL\n", - iwl_is_rfkill(trans->shrd) ? "RF" : "CT"); - return -EIO; - } - - spin_lock_irqsave(&trans->hcmd_lock, flags); + spin_lock_bh(&txq->lock); if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { - spin_unlock_irqrestore(&trans->hcmd_lock, flags); + spin_unlock_bh(&txq->lock); IWL_ERR(trans, "No space in command queue\n"); - is_ct_kill = iwl_check_for_ct_kill(priv(trans)); - if (!is_ct_kill) { - IWL_ERR(trans, "Restarting adapter queue is full\n"); - iwl_op_mode_nic_error(trans->op_mode); - } + iwl_op_mode_cmd_queue_full(trans->op_mode); return -ENOSPC; } @@ -707,7 +738,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) out_cmd->hdr.cmd = cmd->id; out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = - cpu_to_le16(QUEUE_TO_SEQ(trans->shrd->cmd_queue) | + 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 */ @@ -727,7 +758,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) 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->shrd->cmd_queue); + q->write_ptr, idx, trans_pcie->cmd_queue); phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, copy_size, DMA_BIDIRECTIONAL); @@ -779,7 +810,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) /* 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(trans), cmd->flags, + 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]); @@ -790,7 +821,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) iwl_txq_update_write_ptr(trans, txq); out: - spin_unlock_irqrestore(&trans->hcmd_lock, flags); + spin_unlock_bh(&txq->lock); return idx; } @@ -809,6 +840,8 @@ static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, struct iwl_queue *q = &txq->q; int nfreed = 0; + lockdep_assert_held(&txq->lock); + if ((idx >= q->n_bd) || (iwl_queue_used(q, idx) == 0)) { IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), " "index %d is out of range [0-%d] %d %d.\n", __func__, @@ -838,7 +871,7 @@ static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, * will be executed. The attached skb (if present) will only be freed * if the callback returns 1 */ -void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb, +void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, int handler_status) { struct iwl_rx_packet *pkt = rxb_addr(rxb); @@ -849,21 +882,22 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb, struct iwl_device_cmd *cmd; struct iwl_cmd_meta *meta; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[trans->shrd->cmd_queue]; - unsigned long flags; + struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; /* If a Tx command is being handled and it isn't in the actual * command queue then there a command routing bug has been introduced * in the queue management code. */ - if (WARN(txq_id != trans->shrd->cmd_queue, + if (WARN(txq_id != trans_pcie->cmd_queue, "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", - txq_id, trans->shrd->cmd_queue, sequence, - trans_pcie->txq[trans->shrd->cmd_queue].q.read_ptr, - trans_pcie->txq[trans->shrd->cmd_queue].q.write_ptr)) { + txq_id, trans_pcie->cmd_queue, sequence, + trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr, + trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) { iwl_print_hex_error(trans, pkt, 32); return; } + spin_lock(&txq->lock); + cmd_index = get_cmd_index(&txq->q, index); cmd = txq->cmd[cmd_index]; meta = &txq->meta[cmd_index]; @@ -875,13 +909,14 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb, /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { - meta->source->reply_page = (unsigned long)rxb_addr(rxb); + struct page *p = rxb_steal_page(rxb); + + meta->source->resp_pkt = pkt; + meta->source->_rx_page_addr = (unsigned long)page_address(p); + meta->source->_rx_page_order = hw_params(trans).rx_page_order; meta->source->handler_status = handler_status; - rxb->page = NULL; } - spin_lock_irqsave(&trans->hcmd_lock, flags); - iwl_hcmd_queue_reclaim(trans, txq_id, index); if (!(meta->flags & CMD_ASYNC)) { @@ -898,7 +933,7 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb, meta->flags = 0; - spin_unlock_irqrestore(&trans->hcmd_lock, flags); + spin_unlock(&txq->lock); } #define HOST_COMPLETE_TIMEOUT (2 * HZ) @@ -912,12 +947,9 @@ static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) return -EINVAL; - if (test_bit(STATUS_EXIT_PENDING, &trans->shrd->status)) - return -EBUSY; - ret = iwl_enqueue_hcmd(trans, cmd); if (ret < 0) { - IWL_DEBUG_QUIET_RFKILL(trans, + IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", get_cmd_string(cmd->id), ret); return ret; @@ -931,26 +963,22 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) int cmd_idx; int ret; - lockdep_assert_held(&trans->shrd->mutex); - IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", get_cmd_string(cmd->id)); - if (test_bit(STATUS_EXIT_PENDING, &trans->shrd->status)) - return -EBUSY; - - - if (test_bit(STATUS_RF_KILL_HW, &trans->shrd->status)) { - IWL_ERR(trans, "Command %s aborted: RF KILL Switch\n", - get_cmd_string(cmd->id)); - return -ECANCELED; - } if (test_bit(STATUS_FW_ERROR, &trans->shrd->status)) { IWL_ERR(trans, "Command %s failed: FW Error\n", get_cmd_string(cmd->id)); return -EIO; } - set_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status); + + if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE, + &trans->shrd->status))) { + IWL_ERR(trans, "Command %s: a command is already active!\n", + get_cmd_string(cmd->id)); + return -EIO; + } + IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", get_cmd_string(cmd->id)); @@ -958,7 +986,7 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (cmd_idx < 0) { ret = cmd_idx; clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status); - IWL_DEBUG_QUIET_RFKILL(trans, + IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", get_cmd_string(cmd->id), ret); return ret; @@ -970,15 +998,15 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (!ret) { if (test_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status)) { struct iwl_tx_queue *txq = - &trans_pcie->txq[trans->shrd->cmd_queue]; + &trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_queue *q = &txq->q; - IWL_DEBUG_QUIET_RFKILL(trans, + IWL_ERR(trans, "Error sending %s: time out after %dms.\n", get_cmd_string(cmd->id), jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); - IWL_DEBUG_QUIET_RFKILL(trans, + IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", q->read_ptr, q->write_ptr); @@ -990,7 +1018,7 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) } } - if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { + if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { IWL_ERR(trans, "Error: Response NULL in '%s'\n", get_cmd_string(cmd->id)); ret = -EIO; @@ -1007,13 +1035,13 @@ cancel: * in later, it will possibly set an invalid * address (cmd->meta.source). */ - trans_pcie->txq[trans->shrd->cmd_queue].meta[cmd_idx].flags &= + trans_pcie->txq[trans_pcie->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB; } - if (cmd->reply_page) { - iwl_free_pages(trans->shrd, cmd->reply_page); - cmd->reply_page = 0; + if (cmd->resp_pkt) { + iwl_free_resp(cmd); + cmd->resp_pkt = NULL; } return ret; @@ -1038,9 +1066,11 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, int freed = 0; /* This function is not meant to release cmd queue*/ - if (WARN_ON(txq_id == trans->shrd->cmd_queue)) + if (WARN_ON(txq_id == trans_pcie->cmd_queue)) return 0; + lockdep_assert_held(&txq->lock); + /*Since we free until index _not_ inclusive, the one before index is * the last we will free. This one must be used */ last_to_free = iwl_queue_dec_wrap(index, q->n_bd); |