* @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),
        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
  * @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.
        unsigned long status;
 
        struct device *dev;
+       u32 max_skb_frags;
        u32 hw_rev;
        u32 hw_id;
        char hw_id_str[52];
 
 
        /* 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;
 }
 
 
        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,
                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);
                      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;
        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;
                      "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
 
        /* 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
 
        /*
         * 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,
                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));
 
                             &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);