/** * iwl_legacy_tx_cmd_complete - Pull unused buffers off the queue and reclaim them * @rxb: Rx buffer to reclaim * * 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_legacy_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); 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]; unsigned long flags; /* 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 != priv->cmd_queue, "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", txq_id, priv->cmd_queue, sequence, priv->txq[priv->cmd_queue].q.read_ptr, priv->txq[priv->cmd_queue].q.write_ptr)) { iwl_print_hex_error(priv, pkt, 32); return; } cmd_index = iwl_legacy_get_cmd_index(&txq->q, index, huge); cmd = txq->cmd[cmd_index]; meta = &txq->meta[cmd_index]; txq->time_stamp = jiffies; pci_unmap_single(priv->pci_dev, dma_unmap_addr(meta, mapping), dma_unmap_len(meta, len), PCI_DMA_BIDIRECTIONAL); /* 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); rxb->page = NULL; } else if (meta->callback) meta->callback(priv, cmd, pkt); spin_lock_irqsave(&priv->hcmd_lock, flags); iwl_legacy_hcmd_queue_reclaim(priv, txq_id, index, cmd_index); if (!(meta->flags & CMD_ASYNC)) { clear_bit(STATUS_HCMD_ACTIVE, &priv->status); IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s\n", iwl_legacy_get_cmd_string(cmd->hdr.cmd)); wake_up(&priv->wait_command_queue); } /* Mark as unmapped */ meta->flags = 0; spin_unlock_irqrestore(&priv->hcmd_lock, flags); }
/** * il3945_hdl_tx - Handle Tx response */ static void il3945_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) { struct il_rx_pkt *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int idx = SEQ_TO_IDX(sequence); struct il_tx_queue *txq = &il->txq[txq_id]; struct ieee80211_tx_info *info; struct il3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; u32 status = le32_to_cpu(tx_resp->status); int rate_idx; int fail; if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) { IL_ERR("Read idx for DMA queue txq_id (%d) idx %d " "is out of range [0-%d] %d %d\n", txq_id, idx, txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); return; } /* * Firmware will not transmit frame on passive channel, if it not yet * received some valid frame on that channel. When this error happen * we have to wait until firmware will unblock itself i.e. when we * note received beacon or other frame. We unblock queues in * il3945_pass_packet_to_mac80211 or in il_mac_bss_info_changed. */ if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && il->iw_mode == NL80211_IFTYPE_STATION) { il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); D_INFO("Stopped queues - RX waiting on passive channel\n"); } txq->time_stamp = jiffies; info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]); ieee80211_tx_info_clear_status(info); /* Fill the MRR chain with some info about on-chip retransmissions */ rate_idx = il3945_hwrate_to_plcp_idx(tx_resp->rate); if (info->band == IEEE80211_BAND_5GHZ) rate_idx -= IL_FIRST_OFDM_RATE; fail = tx_resp->failure_frame; info->status.rates[0].idx = rate_idx; info->status.rates[0].count = fail + 1; /* add final attempt */ /* tx_status->rts_retry_count = tx_resp->failure_rts; */ info->flags |= ((status & TX_STATUS_MSK) == TX_STATUS_SUCCESS) ? IEEE80211_TX_STAT_ACK : 0; D_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n", txq_id, il3945_get_tx_fail_reason(status), status, tx_resp->rate, tx_resp->failure_frame); D_TX_REPLY("Tx queue reclaim %d\n", idx); il3945_tx_queue_reclaim(il, txq_id, idx); if (status & TX_ABORT_REQUIRED_MSK) IL_ERR("TODO: Implement Tx ABORT REQUIRED!!!\n"); }
/** * iwl_tx_cmd_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_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, int handler_status) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int index = SEQ_TO_INDEX(sequence); int cmd_index; 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_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_pcie->cmd_queue, "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", 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]; txq->time_stamp = jiffies; iwlagn_unmap_tfd(trans, meta, &txq->tfds[index], DMA_BIDIRECTIONAL); /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { 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; } iwl_hcmd_queue_reclaim(trans, txq_id, index); if (!(meta->flags & CMD_ASYNC)) { if (!test_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status)) { IWL_WARN(trans, "HCMD_ACTIVE already clear for command %s\n", get_cmd_string(cmd->hdr.cmd)); } clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", get_cmd_string(cmd->hdr.cmd)); wake_up(&trans->wait_command_queue); } meta->flags = 0; spin_unlock(&txq->lock); }
static void iwl5000_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int index = SEQ_TO_INDEX(sequence); struct iwl_tx_queue *txq = &priv->txq[txq_id]; struct ieee80211_tx_info *info; struct iwl5000_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; u32 status = le16_to_cpu(tx_resp->status.status); int tid; int sta_id; int freed; if ((index >= txq->q.n_bd) || (iwl_queue_used(&txq->q, index) == 0)) { IWL_ERR(priv, "Read index for DMA queue txq_id (%d) index %d " "is out of range [0-%d] %d %d\n", txq_id, index, txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); return; } info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb[0]); memset(&info->status, 0, sizeof(info->status)); tid = (tx_resp->ra_tid & IWL50_TX_RES_TID_MSK) >> IWL50_TX_RES_TID_POS; sta_id = (tx_resp->ra_tid & IWL50_TX_RES_RA_MSK) >> IWL50_TX_RES_RA_POS; if (txq->sched_retry) { const u32 scd_ssn = iwl5000_get_scd_ssn(tx_resp); struct iwl_ht_agg *agg = NULL; agg = &priv->stations[sta_id].tid[tid].agg; iwl5000_tx_status_reply_tx(priv, agg, tx_resp, txq_id, index); /* check if BAR is needed */ if ((tx_resp->frame_count == 1) && !iwl_is_tx_success(status)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; if (txq->q.read_ptr != (scd_ssn & 0xff)) { index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd); IWL_DEBUG_TX_REPLY(priv, "Retry scheduler reclaim " "scd_ssn=%d idx=%d txq=%d swq=%d\n", scd_ssn , index, txq_id, txq->swq_id); freed = iwl_tx_queue_reclaim(priv, txq_id, index); priv->stations[sta_id].tid[tid].tfds_in_queue -= freed; if (priv->mac80211_registered && (iwl_queue_space(&txq->q) > txq->q.low_mark) && (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) { if (agg->state == IWL_AGG_OFF) iwl_wake_queue(priv, txq_id); else iwl_wake_queue(priv, txq->swq_id); } } } else { BUG_ON(txq_id != txq->swq_id); info->status.rates[0].count = tx_resp->failure_frame + 1; info->flags |= iwl_is_tx_success(status) ? IEEE80211_TX_STAT_ACK : 0; iwl_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), info); IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags " "0x%x retries %d\n", txq_id, iwl_get_tx_fail_reason(status), status, le32_to_cpu(tx_resp->rate_n_flags), tx_resp->failure_frame); freed = iwl_tx_queue_reclaim(priv, txq_id, index); if (ieee80211_is_data_qos(tx_resp->frame_ctrl)) priv->stations[sta_id].tid[tid].tfds_in_queue -= freed; if (priv->mac80211_registered && (iwl_queue_space(&txq->q) > txq->q.low_mark)) iwl_wake_queue(priv, txq_id); } if (ieee80211_is_data_qos(tx_resp->frame_ctrl)) iwl_txq_check_empty(priv, sta_id, tid, txq_id); if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK)) IWL_ERR(priv, "TODO: Implement Tx ABORT REQUIRED!!!\n"); }
static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv, struct iwl_ht_agg *agg, struct iwl5000_tx_resp *tx_resp, int txq_id, u16 start_idx) { u16 status; struct agg_tx_status *frame_status = &tx_resp->status; struct ieee80211_tx_info *info = NULL; struct ieee80211_hdr *hdr = NULL; u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); int i, sh, idx; u16 seq; if (agg->wait_for_ba) IWL_DEBUG_TX_REPLY(priv, "got tx response w/o block-ack\n"); agg->frame_count = tx_resp->frame_count; agg->start_idx = start_idx; agg->rate_n_flags = rate_n_flags; agg->bitmap = 0; /* # frames attempted by Tx command */ if (agg->frame_count == 1) { /* Only one frame was attempted; no block-ack will arrive */ status = le16_to_cpu(frame_status[0].status); idx = start_idx; /* FIXME: code repetition */ IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n", agg->frame_count, agg->start_idx, idx); info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]); info->status.rates[0].count = tx_resp->failure_frame + 1; info->flags &= ~IEEE80211_TX_CTL_AMPDU; info->flags |= iwl_is_tx_success(status) ? IEEE80211_TX_STAT_ACK : 0; iwl_hwrate_to_tx_control(priv, rate_n_flags, info); /* FIXME: code repetition end */ IWL_DEBUG_TX_REPLY(priv, "1 Frame 0x%x failure :%d\n", status & 0xff, tx_resp->failure_frame); IWL_DEBUG_TX_REPLY(priv, "Rate Info rate_n_flags=%x\n", rate_n_flags); agg->wait_for_ba = 0; } else { /* Two or more frames were attempted; expect block-ack */ u64 bitmap = 0; int start = agg->start_idx; /* Construct bit-map of pending frames within Tx window */ for (i = 0; i < agg->frame_count; i++) { u16 sc; status = le16_to_cpu(frame_status[i].status); seq = le16_to_cpu(frame_status[i].sequence); idx = SEQ_TO_INDEX(seq); txq_id = SEQ_TO_QUEUE(seq); if (status & (AGG_TX_STATE_FEW_BYTES_MSK | AGG_TX_STATE_ABORT_MSK)) continue; IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, txq_id=%d idx=%d\n", agg->frame_count, txq_id, idx); hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx); sc = le16_to_cpu(hdr->seq_ctrl); if (idx != (SEQ_TO_SN(sc) & 0xff)) { IWL_ERR(priv, "BUG_ON idx doesn't match seq control" " idx=%d, seq_idx=%d, seq=%d\n", idx, SEQ_TO_SN(sc), hdr->seq_ctrl); return -1; } IWL_DEBUG_TX_REPLY(priv, "AGG Frame i=%d idx %d seq=%d\n", i, idx, SEQ_TO_SN(sc)); sh = idx - start; if (sh > 64) { sh = (start - idx) + 0xff; bitmap = bitmap << sh; sh = 0; start = idx; } else if (sh < -64) sh = 0xff - (start - idx); else if (sh < 0) { sh = start - idx; start = idx; bitmap = bitmap << sh; sh = 0; } bitmap |= 1ULL << sh; IWL_DEBUG_TX_REPLY(priv, "start=%d bitmap=0x%llx\n", start, (unsigned long long)bitmap); } agg->bitmap = bitmap; agg->start_idx = start; IWL_DEBUG_TX_REPLY(priv, "Frames %d start_idx=%d bitmap=0x%llx\n", agg->frame_count, agg->start_idx, (unsigned long long)agg->bitmap); if (bitmap) agg->wait_for_ba = 1; } return 0; }
int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence); struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; struct ieee80211_hdr *hdr; u32 status = le16_to_cpu(tx_resp->status.status); u32 ssn = iwlagn_get_scd_ssn(tx_resp); int tid; int sta_id; int freed; struct ieee80211_tx_info *info; unsigned long flags; struct sk_buff_head skbs; struct sk_buff *skb; struct iwl_rxon_context *ctx; bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> IWLAGN_TX_RES_TID_POS; sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> IWLAGN_TX_RES_RA_POS; spin_lock_irqsave(&priv->shrd->sta_lock, flags); if (is_agg) iwl_rx_reply_tx_agg(priv, tx_resp); if (tx_resp->frame_count == 1) { IWL_DEBUG_TX_REPLY(priv, "Q %d, ssn %d", txq_id, ssn); __skb_queue_head_init(&skbs); /*we can free until ssn % q.n_bd not inclusive */ iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id, ssn, status, &skbs); freed = 0; while (!skb_queue_empty(&skbs)) { skb = __skb_dequeue(&skbs); hdr = (struct ieee80211_hdr *)skb->data; if (!ieee80211_is_data_qos(hdr->frame_control)) priv->last_seq_ctl = tx_resp->seq_ctl; info = IEEE80211_SKB_CB(skb); ctx = info->driver_data[0]; kmem_cache_free(priv->tx_cmd_pool, (info->driver_data[1])); memset(&info->status, 0, sizeof(info->status)); if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && iwl_is_associated_ctx(ctx) && ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) { ctx->last_tx_rejected = true; iwl_trans_stop_queue(trans(priv), txq_id, "Tx on passive channel"); IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) " "rate_n_flags 0x%x retries %d\n", txq_id, iwl_get_tx_fail_reason(status), status, le32_to_cpu(tx_resp->rate_n_flags), tx_resp->failure_frame); IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, idx=%d\n", tx_resp->frame_count, cmd_index); } /* check if BAR is needed */ if (is_agg && !iwl_is_tx_success(status)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), tx_resp, is_agg); if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); ieee80211_tx_status_irqsafe(priv->hw, skb); freed++; } WARN_ON(!is_agg && freed != 1); } iwl_check_abort_status(priv, tx_resp->frame_count, status); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); return 0; }
int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence); struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; struct ieee80211_hdr *hdr; u32 status = le16_to_cpu(tx_resp->status.status); u16 ssn = iwlagn_get_scd_ssn(tx_resp); int tid; int sta_id; int freed; struct ieee80211_tx_info *info; unsigned long flags; struct sk_buff_head skbs; struct sk_buff *skb; struct iwl_rxon_context *ctx; bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> IWLAGN_TX_RES_TID_POS; sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> IWLAGN_TX_RES_RA_POS; spin_lock_irqsave(&priv->shrd->sta_lock, flags); if (is_agg) iwl_rx_reply_tx_agg(priv, tx_resp); if (tx_resp->frame_count == 1) { u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10); if (is_agg) { /* If this is an aggregation queue, we can rely on the * ssn since the wifi sequence number corresponds to * the index in the TFD ring (%256). * The seq_ctl is the sequence control of the packet * to which this Tx response relates. But if there is a * hole in the bitmap of the BA we received, this Tx * response may allow to reclaim the hole and all the * subsequent packets that were already acked. * In that case, seq_ctl != ssn, and the next packet * to be reclaimed will be ssn and not seq_ctl. */ next_reclaimed = ssn; } __skb_queue_head_init(&skbs); priv->tid_data[sta_id][tid].next_reclaimed = next_reclaimed; IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d", next_reclaimed); /*we can free until ssn % q.n_bd not inclusive */ WARN_ON(iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id, ssn, status, &skbs)); iwlagn_check_ratid_empty(priv, sta_id, tid); freed = 0; while (!skb_queue_empty(&skbs)) { skb = __skb_dequeue(&skbs); hdr = (struct ieee80211_hdr *)skb->data; if (!ieee80211_is_data_qos(hdr->frame_control)) priv->last_seq_ctl = tx_resp->seq_ctl; info = IEEE80211_SKB_CB(skb); ctx = info->driver_data[0]; kmem_cache_free(priv->tx_cmd_pool, (info->driver_data[1])); memset(&info->status, 0, sizeof(info->status)); if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && iwl_is_associated_ctx(ctx) && ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) { ctx->last_tx_rejected = true; iwl_trans_stop_queue(trans(priv), txq_id, "Tx on passive channel"); IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) " "rate_n_flags 0x%x retries %d\n", txq_id, iwl_get_tx_fail_reason(status), status, le32_to_cpu(tx_resp->rate_n_flags), tx_resp->failure_frame); IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, idx=%d\n", tx_resp->frame_count, cmd_index); } /* check if BAR is needed */ if (is_agg && !iwl_is_tx_success(status)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), tx_resp, is_agg); if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); ieee80211_tx_status_irqsafe(priv->hw, skb); freed++; } WARN_ON(!is_agg && freed != 1); } iwl_check_abort_status(priv, tx_resp->frame_count, status); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); return 0; }