diff options
author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2011-07-03 04:22:15 -0400 |
---|---|---|
committer | Wey-Yi Guy <wey-yi.w.guy@intel.com> | 2011-07-16 10:36:36 -0400 |
commit | 47c1b496015e41e1068878814596af9a45d4fa84 (patch) | |
tree | 26f086a3b95e858a9bd897a655a320f520c43467 | |
parent | 94f9b97be5b3bf67392e43fb7f567721b09142c2 (diff) |
iwlagn: move Tx datapath to transport layer
Split the Tx datapath in two parts:
* the first deals with the Tx cmd composition
* the second attaches the skb + Tx cmd to the queues
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 158 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.c | 156 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.h | 13 |
5 files changed, 189 insertions, 146 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 7d3aad83e0d..f306824157e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include "iwl-helpers.h" | 39 | #include "iwl-helpers.h" |
40 | #include "iwl-agn-hw.h" | 40 | #include "iwl-agn-hw.h" |
41 | #include "iwl-agn.h" | 41 | #include "iwl-agn.h" |
42 | #include "iwl-trans.h" | ||
42 | 43 | ||
43 | /* | 44 | /* |
44 | * mac80211 queues, ACs, hardware queues, FIFOs. | 45 | * mac80211 queues, ACs, hardware queues, FIFOs. |
@@ -98,7 +99,7 @@ static inline int get_fifo_from_tid(struct iwl_rxon_context *ctx, u16 tid) | |||
98 | /** | 99 | /** |
99 | * iwlagn_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array | 100 | * iwlagn_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array |
100 | */ | 101 | */ |
101 | static void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv, | 102 | void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv, |
102 | struct iwl_tx_queue *txq, | 103 | struct iwl_tx_queue *txq, |
103 | u16 byte_cnt) | 104 | u16 byte_cnt) |
104 | { | 105 | { |
@@ -547,26 +548,17 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
547 | { | 548 | { |
548 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 549 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; |
549 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 550 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |
550 | struct ieee80211_sta *sta = info->control.sta; | ||
551 | struct iwl_station_priv *sta_priv = NULL; | 551 | struct iwl_station_priv *sta_priv = NULL; |
552 | struct iwl_tx_queue *txq; | ||
553 | struct iwl_queue *q; | ||
554 | struct iwl_device_cmd *out_cmd; | ||
555 | struct iwl_cmd_meta *out_meta; | ||
556 | struct iwl_tx_cmd *tx_cmd; | ||
557 | struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; | 552 | struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; |
553 | struct iwl_tx_cmd *tx_cmd; | ||
558 | int txq_id; | 554 | int txq_id; |
559 | dma_addr_t phys_addr = 0; | 555 | |
560 | dma_addr_t txcmd_phys; | ||
561 | dma_addr_t scratch_phys; | ||
562 | u16 len, firstlen, secondlen; | ||
563 | u16 seq_number = 0; | 556 | u16 seq_number = 0; |
564 | __le16 fc; | 557 | __le16 fc; |
565 | u8 hdr_len; | 558 | u8 hdr_len; |
559 | u16 len; | ||
566 | u8 sta_id; | 560 | u8 sta_id; |
567 | u8 wait_write_ptr = 0; | ||
568 | u8 tid = 0; | 561 | u8 tid = 0; |
569 | u8 *qc = NULL; | ||
570 | unsigned long flags; | 562 | unsigned long flags; |
571 | bool is_agg = false; | 563 | bool is_agg = false; |
572 | 564 | ||
@@ -614,8 +606,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
614 | 606 | ||
615 | IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); | 607 | IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); |
616 | 608 | ||
617 | if (sta) | 609 | if (info->control.sta) |
618 | sta_priv = (void *)sta->drv_priv; | 610 | sta_priv = (void *)info->control.sta->drv_priv; |
619 | 611 | ||
620 | if (sta_priv && sta_priv->asleep && | 612 | if (sta_priv && sta_priv->asleep && |
621 | (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) { | 613 | (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) { |
@@ -650,6 +642,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
650 | spin_lock(&priv->sta_lock); | 642 | spin_lock(&priv->sta_lock); |
651 | 643 | ||
652 | if (ieee80211_is_data_qos(fc)) { | 644 | if (ieee80211_is_data_qos(fc)) { |
645 | u8 *qc = NULL; | ||
653 | qc = ieee80211_get_qos_ctl(hdr); | 646 | qc = ieee80211_get_qos_ctl(hdr); |
654 | tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; | 647 | tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; |
655 | 648 | ||
@@ -670,38 +663,13 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
670 | } | 663 | } |
671 | } | 664 | } |
672 | 665 | ||
673 | txq = &priv->txq[txq_id]; | 666 | tx_cmd = trans_get_tx_cmd(priv, txq_id); |
674 | q = &txq->q; | 667 | if (unlikely(!tx_cmd)) |
675 | |||
676 | if (unlikely(iwl_queue_space(q) < q->high_mark)) | ||
677 | goto drop_unlock_sta; | 668 | goto drop_unlock_sta; |
678 | 669 | ||
679 | /* Set up driver data for this TFD */ | ||
680 | memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info)); | ||
681 | txq->txb[q->write_ptr].skb = skb; | ||
682 | txq->txb[q->write_ptr].ctx = ctx; | ||
683 | |||
684 | /* Set up first empty entry in queue's array of Tx/cmd buffers */ | ||
685 | out_cmd = txq->cmd[q->write_ptr]; | ||
686 | out_meta = &txq->meta[q->write_ptr]; | ||
687 | tx_cmd = &out_cmd->cmd.tx; | ||
688 | memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); | ||
689 | memset(tx_cmd, 0, sizeof(struct iwl_tx_cmd)); | ||
690 | |||
691 | /* | ||
692 | * Set up the Tx-command (not MAC!) header. | ||
693 | * Store the chosen Tx queue and TFD index within the sequence field; | ||
694 | * after Tx, uCode's Tx response will return this value so driver can | ||
695 | * locate the frame within the tx queue and do post-tx processing. | ||
696 | */ | ||
697 | out_cmd->hdr.cmd = REPLY_TX; | ||
698 | out_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | | ||
699 | INDEX_TO_SEQ(q->write_ptr))); | ||
700 | |||
701 | /* Copy MAC header from skb into command buffer */ | 670 | /* Copy MAC header from skb into command buffer */ |
702 | memcpy(tx_cmd->hdr, hdr, hdr_len); | 671 | memcpy(tx_cmd->hdr, hdr, hdr_len); |
703 | 672 | ||
704 | |||
705 | /* Total # bytes to be transmitted */ | 673 | /* Total # bytes to be transmitted */ |
706 | len = (u16)skb->len; | 674 | len = (u16)skb->len; |
707 | tx_cmd->len = cpu_to_le16(len); | 675 | tx_cmd->len = cpu_to_le16(len); |
@@ -716,54 +684,9 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
716 | iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, fc); | 684 | iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, fc); |
717 | 685 | ||
718 | iwl_update_stats(priv, true, fc, len); | 686 | iwl_update_stats(priv, true, fc, len); |
719 | /* | ||
720 | * Use the first empty entry in this queue's command buffer array | ||
721 | * to contain the Tx command and MAC header concatenated together | ||
722 | * (payload data will be in another buffer). | ||
723 | * Size of this varies, due to varying MAC header length. | ||
724 | * If end is not dword aligned, we'll have 2 extra bytes at the end | ||
725 | * of the MAC header (device reads on dword boundaries). | ||
726 | * We'll tell device about this padding later. | ||
727 | */ | ||
728 | len = sizeof(struct iwl_tx_cmd) + | ||
729 | sizeof(struct iwl_cmd_header) + hdr_len; | ||
730 | firstlen = (len + 3) & ~3; | ||
731 | |||
732 | /* Tell NIC about any 2-byte padding after MAC header */ | ||
733 | if (firstlen != len) | ||
734 | tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; | ||
735 | |||
736 | /* Physical address of this Tx command's header (not MAC header!), | ||
737 | * within command buffer array. */ | ||
738 | txcmd_phys = dma_map_single(priv->bus.dev, | ||
739 | &out_cmd->hdr, firstlen, | ||
740 | DMA_BIDIRECTIONAL); | ||
741 | if (unlikely(dma_mapping_error(priv->bus.dev, txcmd_phys))) | ||
742 | goto drop_unlock_sta; | ||
743 | dma_unmap_addr_set(out_meta, mapping, txcmd_phys); | ||
744 | dma_unmap_len_set(out_meta, len, firstlen); | ||
745 | 687 | ||
746 | if (!ieee80211_has_morefrags(hdr->frame_control)) { | 688 | if (trans_tx(priv, skb, tx_cmd, txq_id, fc, is_agg, ctx)) |
747 | txq->need_update = 1; | 689 | goto drop_unlock_sta; |
748 | } else { | ||
749 | wait_write_ptr = 1; | ||
750 | txq->need_update = 0; | ||
751 | } | ||
752 | |||
753 | /* Set up TFD's 2nd entry to point directly to remainder of skb, | ||
754 | * if any (802.11 null frames have no payload). */ | ||
755 | secondlen = skb->len - hdr_len; | ||
756 | if (secondlen > 0) { | ||
757 | phys_addr = dma_map_single(priv->bus.dev, skb->data + hdr_len, | ||
758 | secondlen, DMA_TO_DEVICE); | ||
759 | if (unlikely(dma_mapping_error(priv->bus.dev, phys_addr))) { | ||
760 | dma_unmap_single(priv->bus.dev, | ||
761 | dma_unmap_addr(out_meta, mapping), | ||
762 | dma_unmap_len(out_meta, len), | ||
763 | DMA_BIDIRECTIONAL); | ||
764 | goto drop_unlock_sta; | ||
765 | } | ||
766 | } | ||
767 | 690 | ||
768 | if (ieee80211_is_data_qos(fc)) { | 691 | if (ieee80211_is_data_qos(fc)) { |
769 | priv->stations[sta_id].tid[tid].tfds_in_queue++; | 692 | priv->stations[sta_id].tid[tid].tfds_in_queue++; |
@@ -772,55 +695,9 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
772 | } | 695 | } |
773 | 696 | ||
774 | spin_unlock(&priv->sta_lock); | 697 | spin_unlock(&priv->sta_lock); |
775 | |||
776 | /* Attach buffers to TFD */ | ||
777 | iwlagn_txq_attach_buf_to_tfd(priv, txq, txcmd_phys, firstlen, 1); | ||
778 | if (secondlen > 0) | ||
779 | iwlagn_txq_attach_buf_to_tfd(priv, txq, phys_addr, | ||
780 | secondlen, 0); | ||
781 | |||
782 | scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + | ||
783 | offsetof(struct iwl_tx_cmd, scratch); | ||
784 | |||
785 | /* take back ownership of DMA buffer to enable update */ | ||
786 | dma_sync_single_for_cpu(priv->bus.dev, txcmd_phys, firstlen, | ||
787 | DMA_BIDIRECTIONAL); | ||
788 | tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); | ||
789 | tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); | ||
790 | |||
791 | IWL_DEBUG_TX(priv, "sequence nr = 0X%x\n", | ||
792 | le16_to_cpu(out_cmd->hdr.sequence)); | ||
793 | IWL_DEBUG_TX(priv, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); | ||
794 | iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd)); | ||
795 | iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len); | ||
796 | |||
797 | /* Set up entry for this TFD in Tx byte-count array */ | ||
798 | if (info->flags & IEEE80211_TX_CTL_AMPDU) | ||
799 | iwlagn_txq_update_byte_cnt_tbl(priv, txq, | ||
800 | le16_to_cpu(tx_cmd->len)); | ||
801 | |||
802 | dma_sync_single_for_device(priv->bus.dev, txcmd_phys, firstlen, | ||
803 | DMA_BIDIRECTIONAL); | ||
804 | |||
805 | trace_iwlwifi_dev_tx(priv, | ||
806 | &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], | ||
807 | sizeof(struct iwl_tfd), | ||
808 | &out_cmd->hdr, firstlen, | ||
809 | skb->data + hdr_len, secondlen); | ||
810 | |||
811 | /* Tell device the write index *just past* this latest filled TFD */ | ||
812 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); | ||
813 | iwl_txq_update_write_ptr(priv, txq); | ||
814 | spin_unlock_irqrestore(&priv->lock, flags); | 698 | spin_unlock_irqrestore(&priv->lock, flags); |
815 | 699 | ||
816 | /* | 700 | /* |
817 | * At this point the frame is "transmitted" successfully | ||
818 | * and we will get a TX status notification eventually, | ||
819 | * regardless of the value of ret. "ret" only indicates | ||
820 | * whether or not we should update the write pointer. | ||
821 | */ | ||
822 | |||
823 | /* | ||
824 | * Avoid atomic ops if it isn't an associated client. | 701 | * Avoid atomic ops if it isn't an associated client. |
825 | * Also, if this is a packet for aggregation, don't | 702 | * Also, if this is a packet for aggregation, don't |
826 | * increase the counter because the ucode will stop | 703 | * increase the counter because the ucode will stop |
@@ -830,17 +707,6 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
830 | if (sta_priv && sta_priv->client && !is_agg) | 707 | if (sta_priv && sta_priv->client && !is_agg) |
831 | atomic_inc(&sta_priv->pending_frames); | 708 | atomic_inc(&sta_priv->pending_frames); |
832 | 709 | ||
833 | if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) { | ||
834 | if (wait_write_ptr) { | ||
835 | spin_lock_irqsave(&priv->lock, flags); | ||
836 | txq->need_update = 1; | ||
837 | iwl_txq_update_write_ptr(priv, txq); | ||
838 | spin_unlock_irqrestore(&priv->lock, flags); | ||
839 | } else { | ||
840 | iwl_stop_queue(priv, txq); | ||
841 | } | ||
842 | } | ||
843 | |||
844 | return 0; | 710 | return 0; |
845 | 711 | ||
846 | drop_unlock_sta: | 712 | drop_unlock_sta: |
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index 5f58b44bb2a..aed86c6cd93 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h | |||
@@ -142,6 +142,10 @@ void iwlagn_stop_device(struct iwl_priv *priv); | |||
142 | /* tx queue */ | 142 | /* tx queue */ |
143 | void iwlagn_set_wr_ptrs(struct iwl_priv *priv, | 143 | void iwlagn_set_wr_ptrs(struct iwl_priv *priv, |
144 | int txq_id, u32 index); | 144 | int txq_id, u32 index); |
145 | void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv, | ||
146 | struct iwl_tx_queue *txq, | ||
147 | u16 byte_cnt); | ||
148 | |||
145 | void iwlagn_tx_queue_set_status(struct iwl_priv *priv, | 149 | void iwlagn_tx_queue_set_status(struct iwl_priv *priv, |
146 | struct iwl_tx_queue *txq, | 150 | struct iwl_tx_queue *txq, |
147 | int tx_fifo_id, int scd_retry); | 151 | int tx_fifo_id, int scd_retry); |
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index a0cca02032e..173fad2ab24 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h | |||
@@ -1257,6 +1257,10 @@ struct iwl_trans_ops { | |||
1257 | 1257 | ||
1258 | int (*send_cmd_pdu)(struct iwl_priv *priv, u8 id, u32 flags, u16 len, | 1258 | int (*send_cmd_pdu)(struct iwl_priv *priv, u8 id, u32 flags, u16 len, |
1259 | const void *data); | 1259 | const void *data); |
1260 | struct iwl_tx_cmd * (*get_tx_cmd)(struct iwl_priv *priv, int txq_id); | ||
1261 | int (*tx)(struct iwl_priv *priv, struct sk_buff *skb, | ||
1262 | struct iwl_tx_cmd *tx_cmd, int txq_id, __le16 fc, bool ampdu, | ||
1263 | struct iwl_rxon_context *ctx); | ||
1260 | }; | 1264 | }; |
1261 | 1265 | ||
1262 | struct iwl_trans { | 1266 | struct iwl_trans { |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c index d760857c863..acd2a5feec9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans.c | |||
@@ -552,6 +552,159 @@ static int iwl_trans_tx_stop(struct iwl_priv *priv) | |||
552 | return 0; | 552 | return 0; |
553 | } | 553 | } |
554 | 554 | ||
555 | static struct iwl_tx_cmd *iwl_trans_get_tx_cmd(struct iwl_priv *priv, | ||
556 | int txq_id) | ||
557 | { | ||
558 | struct iwl_tx_queue *txq = &priv->txq[txq_id]; | ||
559 | struct iwl_queue *q = &txq->q; | ||
560 | struct iwl_device_cmd *dev_cmd; | ||
561 | |||
562 | if (unlikely(iwl_queue_space(q) < q->high_mark)) | ||
563 | return NULL; | ||
564 | |||
565 | /* | ||
566 | * Set up the Tx-command (not MAC!) header. | ||
567 | * Store the chosen Tx queue and TFD index within the sequence field; | ||
568 | * after Tx, uCode's Tx response will return this value so driver can | ||
569 | * locate the frame within the tx queue and do post-tx processing. | ||
570 | */ | ||
571 | dev_cmd = txq->cmd[q->write_ptr]; | ||
572 | memset(dev_cmd, 0, sizeof(*dev_cmd)); | ||
573 | dev_cmd->hdr.cmd = REPLY_TX; | ||
574 | dev_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | | ||
575 | INDEX_TO_SEQ(q->write_ptr))); | ||
576 | return &dev_cmd->cmd.tx; | ||
577 | } | ||
578 | |||
579 | static int iwl_trans_tx(struct iwl_priv *priv, struct sk_buff *skb, | ||
580 | struct iwl_tx_cmd *tx_cmd, int txq_id, __le16 fc, bool ampdu, | ||
581 | struct iwl_rxon_context *ctx) | ||
582 | { | ||
583 | struct iwl_tx_queue *txq = &priv->txq[txq_id]; | ||
584 | struct iwl_queue *q = &txq->q; | ||
585 | struct iwl_device_cmd *dev_cmd = txq->cmd[q->write_ptr]; | ||
586 | struct iwl_cmd_meta *out_meta; | ||
587 | |||
588 | dma_addr_t phys_addr = 0; | ||
589 | dma_addr_t txcmd_phys; | ||
590 | dma_addr_t scratch_phys; | ||
591 | u16 len, firstlen, secondlen; | ||
592 | u8 wait_write_ptr = 0; | ||
593 | u8 hdr_len = ieee80211_hdrlen(fc); | ||
594 | |||
595 | /* Set up driver data for this TFD */ | ||
596 | memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info)); | ||
597 | txq->txb[q->write_ptr].skb = skb; | ||
598 | txq->txb[q->write_ptr].ctx = ctx; | ||
599 | |||
600 | /* Set up first empty entry in queue's array of Tx/cmd buffers */ | ||
601 | out_meta = &txq->meta[q->write_ptr]; | ||
602 | |||
603 | /* | ||
604 | * Use the first empty entry in this queue's command buffer array | ||
605 | * to contain the Tx command and MAC header concatenated together | ||
606 | * (payload data will be in another buffer). | ||
607 | * Size of this varies, due to varying MAC header length. | ||
608 | * If end is not dword aligned, we'll have 2 extra bytes at the end | ||
609 | * of the MAC header (device reads on dword boundaries). | ||
610 | * We'll tell device about this padding later. | ||
611 | */ | ||
612 | len = sizeof(struct iwl_tx_cmd) + | ||
613 | sizeof(struct iwl_cmd_header) + hdr_len; | ||
614 | firstlen = (len + 3) & ~3; | ||
615 | |||
616 | /* Tell NIC about any 2-byte padding after MAC header */ | ||
617 | if (firstlen != len) | ||
618 | tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; | ||
619 | |||
620 | /* Physical address of this Tx command's header (not MAC header!), | ||
621 | * within command buffer array. */ | ||
622 | txcmd_phys = dma_map_single(priv->bus.dev, | ||
623 | &dev_cmd->hdr, firstlen, | ||
624 | DMA_BIDIRECTIONAL); | ||
625 | if (unlikely(dma_mapping_error(priv->bus.dev, txcmd_phys))) | ||
626 | return -1; | ||
627 | dma_unmap_addr_set(out_meta, mapping, txcmd_phys); | ||
628 | dma_unmap_len_set(out_meta, len, firstlen); | ||
629 | |||
630 | if (!ieee80211_has_morefrags(fc)) { | ||
631 | txq->need_update = 1; | ||
632 | } else { | ||
633 | wait_write_ptr = 1; | ||
634 | txq->need_update = 0; | ||
635 | } | ||
636 | |||
637 | /* Set up TFD's 2nd entry to point directly to remainder of skb, | ||
638 | * if any (802.11 null frames have no payload). */ | ||
639 | secondlen = skb->len - hdr_len; | ||
640 | if (secondlen > 0) { | ||
641 | phys_addr = dma_map_single(priv->bus.dev, skb->data + hdr_len, | ||
642 | secondlen, DMA_TO_DEVICE); | ||
643 | if (unlikely(dma_mapping_error(priv->bus.dev, phys_addr))) { | ||
644 | dma_unmap_single(priv->bus.dev, | ||
645 | dma_unmap_addr(out_meta, mapping), | ||
646 | dma_unmap_len(out_meta, len), | ||
647 | DMA_BIDIRECTIONAL); | ||
648 | return -1; | ||
649 | } | ||
650 | } | ||
651 | |||
652 | /* Attach buffers to TFD */ | ||
653 | iwlagn_txq_attach_buf_to_tfd(priv, txq, txcmd_phys, firstlen, 1); | ||
654 | if (secondlen > 0) | ||
655 | iwlagn_txq_attach_buf_to_tfd(priv, txq, phys_addr, | ||
656 | secondlen, 0); | ||
657 | |||
658 | scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + | ||
659 | offsetof(struct iwl_tx_cmd, scratch); | ||
660 | |||
661 | /* take back ownership of DMA buffer to enable update */ | ||
662 | dma_sync_single_for_cpu(priv->bus.dev, txcmd_phys, firstlen, | ||
663 | DMA_BIDIRECTIONAL); | ||
664 | tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); | ||
665 | tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); | ||
666 | |||
667 | IWL_DEBUG_TX(priv, "sequence nr = 0X%x\n", | ||
668 | le16_to_cpu(dev_cmd->hdr.sequence)); | ||
669 | IWL_DEBUG_TX(priv, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); | ||
670 | iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd)); | ||
671 | iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len); | ||
672 | |||
673 | /* Set up entry for this TFD in Tx byte-count array */ | ||
674 | if (ampdu) | ||
675 | iwlagn_txq_update_byte_cnt_tbl(priv, txq, | ||
676 | le16_to_cpu(tx_cmd->len)); | ||
677 | |||
678 | dma_sync_single_for_device(priv->bus.dev, txcmd_phys, firstlen, | ||
679 | DMA_BIDIRECTIONAL); | ||
680 | |||
681 | trace_iwlwifi_dev_tx(priv, | ||
682 | &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], | ||
683 | sizeof(struct iwl_tfd), | ||
684 | &dev_cmd->hdr, firstlen, | ||
685 | skb->data + hdr_len, secondlen); | ||
686 | |||
687 | /* Tell device the write index *just past* this latest filled TFD */ | ||
688 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); | ||
689 | iwl_txq_update_write_ptr(priv, txq); | ||
690 | |||
691 | /* | ||
692 | * At this point the frame is "transmitted" successfully | ||
693 | * and we will get a TX status notification eventually, | ||
694 | * regardless of the value of ret. "ret" only indicates | ||
695 | * whether or not we should update the write pointer. | ||
696 | */ | ||
697 | if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) { | ||
698 | if (wait_write_ptr) { | ||
699 | txq->need_update = 1; | ||
700 | iwl_txq_update_write_ptr(priv, txq); | ||
701 | } else { | ||
702 | iwl_stop_queue(priv, txq); | ||
703 | } | ||
704 | } | ||
705 | return 0; | ||
706 | } | ||
707 | |||
555 | static const struct iwl_trans_ops trans_ops = { | 708 | static const struct iwl_trans_ops trans_ops = { |
556 | .rx_init = iwl_trans_rx_init, | 709 | .rx_init = iwl_trans_rx_init, |
557 | .rx_stop = iwl_trans_rx_stop, | 710 | .rx_stop = iwl_trans_rx_stop, |
@@ -563,6 +716,9 @@ static const struct iwl_trans_ops trans_ops = { | |||
563 | 716 | ||
564 | .send_cmd = iwl_send_cmd, | 717 | .send_cmd = iwl_send_cmd, |
565 | .send_cmd_pdu = iwl_send_cmd_pdu, | 718 | .send_cmd_pdu = iwl_send_cmd_pdu, |
719 | |||
720 | .get_tx_cmd = iwl_trans_get_tx_cmd, | ||
721 | .tx = iwl_trans_tx, | ||
566 | }; | 722 | }; |
567 | 723 | ||
568 | void iwl_trans_register(struct iwl_trans *trans) | 724 | void iwl_trans_register(struct iwl_trans *trans) |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 111acca07d7..f10bee8c1f8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h | |||
@@ -102,4 +102,17 @@ static inline int trans_send_cmd_pdu(struct iwl_priv *priv, u8 id, u32 flags, | |||
102 | return priv->trans.ops->send_cmd_pdu(priv, id, flags, len, data); | 102 | return priv->trans.ops->send_cmd_pdu(priv, id, flags, len, data); |
103 | } | 103 | } |
104 | 104 | ||
105 | static inline struct iwl_tx_cmd *trans_get_tx_cmd(struct iwl_priv *priv, | ||
106 | int txq_id) | ||
107 | { | ||
108 | return priv->trans.ops->get_tx_cmd(priv, txq_id); | ||
109 | } | ||
110 | |||
111 | static inline int trans_tx(struct iwl_priv *priv, struct sk_buff *skb, | ||
112 | struct iwl_tx_cmd *tx_cmd, int txq_id, __le16 fc, bool ampdu, | ||
113 | struct iwl_rxon_context *ctx) | ||
114 | { | ||
115 | return priv->trans.ops->tx(priv, skb, tx_cmd, txq_id, fc, ampdu, ctx); | ||
116 | } | ||
117 | |||
105 | void iwl_trans_register(struct iwl_trans *trans); | 118 | void iwl_trans_register(struct iwl_trans *trans); |