summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2012-03-09 14:57:30 -0500
committerJohn W. Linville <linville@tuxdriver.com>2012-03-09 14:57:30 -0500
commit74dd1521d0b4f940cdd3ce7b9d988836bef589b8 (patch)
tree42f3d4ee1d4e6d60a91aaadab771f766a3024bc0 /drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
parent2f2d76cc3e938389feee671b46252dde6880b3b7 (diff)
parent1745e4405b2c0da6db2ec4b6bc0ad930612d8295 (diff)
downloadop-kernel-dev-74dd1521d0b4f940cdd3ce7b9d988836bef589b8.zip
op-kernel-dev-74dd1521d0b4f940cdd3ce7b9d988836bef589b8.tar.gz
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.c164
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 4e1e74e..eb430b6 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);
OpenPOWER on IntegriCloud