diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/pcie')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 12 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 135 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 172 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 5 |
5 files changed, 275 insertions, 52 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index e51760e752d4..2d92d3708619 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -538,7 +538,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* a000 Series */ {IWL_PCI_DEVICE(0x2720, 0x0A10, iwla000_2ac_cfg_hr_cdb)}, - {IWL_PCI_DEVICE(0x2722, 0x0A10, iwla000_2ac_cfg_hr)}, + {IWL_PCI_DEVICE(0x34F0, 0x0310, iwla000_2ac_cfg_jf)}, #endif /* CONFIG_IWLMVM */ {0} @@ -672,10 +672,12 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_trans->cfg = cfg_7265d; } - if (iwl_trans->cfg->rf_id && - (cfg == &iwla000_2ac_cfg_hr || cfg == &iwla000_2ac_cfg_hr_cdb) && - iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_JF) { - cfg = &iwla000_2ac_cfg_jf; + if (iwl_trans->cfg->rf_id && cfg == &iwla000_2ac_cfg_hr_cdb) { + if (iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_JF) + cfg = &iwla000_2ac_cfg_jf; + else if (iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_HR) + cfg = &iwla000_2ac_cfg_hr; + iwl_trans->cfg = cfg; } #endif diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index fd4faaaa1484..4b7be7ba9780 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -779,6 +779,9 @@ int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr, size_t size); void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr); void iwl_pcie_apply_destination(struct iwl_trans *trans); +#ifdef CONFIG_INET +struct iwl_tso_hdr_page *get_page_hdr(struct iwl_trans *trans, size_t len); +#endif /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 93cbc7a69bcd..a3c5d3b195ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -245,7 +245,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) */ /* Disable L0S exit timer (platform NMI Work/Around) */ - if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + if (trans->cfg->device_family < IWL_DEVICE_FAMILY_8000) iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); @@ -478,7 +478,7 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_WAKE_ME); - else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + else if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) { iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, CSR_RESET_LINK_PWR_MGMT_DISABLED); iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, @@ -892,7 +892,7 @@ monitor: if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) { iwl_write_prph(trans, le32_to_cpu(dest->base_reg), trans_pcie->fw_mon_phys >> dest->base_shift); - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) iwl_write_prph(trans, le32_to_cpu(dest->end_reg), (trans_pcie->fw_mon_phys + trans_pcie->fw_mon_size - 256) >> @@ -1318,7 +1318,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); /* Load the given image to the HW */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) ret = iwl_pcie_load_given_ucode_8000(trans, fw); else ret = iwl_pcie_load_given_ucode(trans, fw); @@ -1435,7 +1435,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) udelay(2); ret = iwl_poll_bit(trans, CSR_GP_CNTRL, @@ -1822,7 +1822,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, /* this bit wakes up the NIC */ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) udelay(2); /* @@ -2045,17 +2045,52 @@ void iwl_trans_pcie_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq) iwl_read_direct32(trans, FH_TX_TRB_REG(fifo))); } -static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) +static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq; - int cnt; unsigned long now = jiffies; + u8 wr_ptr; + + if (!test_bit(txq_idx, trans_pcie->queue_used)) + return -EINVAL; + + IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", txq_idx); + txq = trans_pcie->txq[txq_idx]; + wr_ptr = ACCESS_ONCE(txq->write_ptr); + + while (txq->read_ptr != ACCESS_ONCE(txq->write_ptr) && + !time_after(jiffies, + now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) { + u8 write_ptr = ACCESS_ONCE(txq->write_ptr); + + if (WARN_ONCE(wr_ptr != write_ptr, + "WR pointer moved while flushing %d -> %d\n", + wr_ptr, write_ptr)) + return -ETIMEDOUT; + usleep_range(1000, 2000); + } + + if (txq->read_ptr != txq->write_ptr) { + IWL_ERR(trans, + "fail to flush all tx fifo queues Q %d\n", txq_idx); + iwl_trans_pcie_log_scd_error(trans, txq); + return -ETIMEDOUT; + } + + IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", txq_idx); + + return 0; +} + +static int iwl_trans_pcie_wait_txqs_empty(struct iwl_trans *trans, u32 txq_bm) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int cnt; int ret = 0; /* waiting for all the tx frames complete might take a while */ for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { - u8 wr_ptr; if (cnt == trans_pcie->cmd_queue) continue; @@ -2064,34 +2099,11 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) if (!(BIT(cnt) & txq_bm)) continue; - IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt); - txq = trans_pcie->txq[cnt]; - wr_ptr = ACCESS_ONCE(txq->write_ptr); - - while (txq->read_ptr != ACCESS_ONCE(txq->write_ptr) && - !time_after(jiffies, - now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) { - u8 write_ptr = ACCESS_ONCE(txq->write_ptr); - - if (WARN_ONCE(wr_ptr != write_ptr, - "WR pointer moved while flushing %d -> %d\n", - wr_ptr, write_ptr)) - return -ETIMEDOUT; - usleep_range(1000, 2000); - } - - if (txq->read_ptr != txq->write_ptr) { - IWL_ERR(trans, - "fail to flush all tx fifo queues Q %d\n", cnt); - ret = -ETIMEDOUT; + ret = iwl_trans_pcie_wait_txq_empty(trans, cnt); + if (ret) break; - } - IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt); } - if (ret) - iwl_trans_pcie_log_scd_error(trans, txq); - return ret; } @@ -2563,8 +2575,15 @@ static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans, (*data)->len = cpu_to_le32(fh_regs_len); val = (void *)(*data)->data; - for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32)) - *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); + if (!trans->cfg->gen2) + for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; + i += sizeof(u32)) + *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); + else + for (i = FH_MEM_LOWER_BOUND_GEN2; i < FH_MEM_UPPER_BOUND_GEN2; + i += sizeof(u32)) + *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans, + i)); iwl_trans_release_nic_access(trans, &flags); @@ -2714,7 +2733,7 @@ static struct iwl_trans_dump_data trans->dbg_dest_tlv->end_shift; /* Make "end" point to the actual end */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 || + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000 || trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) end += (1 << trans->dbg_dest_tlv->end_shift); monitor_len = end - base; @@ -2740,7 +2759,12 @@ static struct iwl_trans_dump_data len += sizeof(*data) + IWL_CSR_TO_DUMP; /* FH registers */ - len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND); + if (trans->cfg->gen2) + len += sizeof(*data) + + (FH_MEM_UPPER_BOUND_GEN2 - FH_MEM_LOWER_BOUND_GEN2); + else + len += sizeof(*data) + + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND); if (dump_rbs) { /* Dump RBs is supported only for pre-9000 devices (1 queue) */ @@ -2754,6 +2778,13 @@ static struct iwl_trans_dump_data (PAGE_SIZE << trans_pcie->rx_page_order)); } + /* Paged memory for gen2 HW */ + if (trans->cfg->gen2) + for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) + len += sizeof(*data) + + sizeof(struct iwl_fw_error_dump_paging) + + trans_pcie->init_dram.paging[i].size; + dump_data = vzalloc(len); if (!dump_data) return NULL; @@ -2793,6 +2824,28 @@ static struct iwl_trans_dump_data if (dump_rbs) len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs); + /* Paged memory for gen2 HW */ + if (trans->cfg->gen2) { + for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) { + struct iwl_fw_error_dump_paging *paging; + dma_addr_t addr = + trans_pcie->init_dram.paging[i].physical; + u32 page_len = trans_pcie->init_dram.paging[i].size; + + data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); + data->len = cpu_to_le32(sizeof(*paging) + page_len); + paging = (void *)data->data; + paging->index = cpu_to_le32(i); + dma_sync_single_for_cpu(trans->dev, addr, page_len, + DMA_BIDIRECTIONAL); + memcpy(paging->data, + trans_pcie->init_dram.paging[i].block, page_len); + data = iwl_fw_error_next_data(data); + + len += sizeof(*data) + sizeof(*paging) + page_len; + } + } + len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len); dump_data->len = len; @@ -2835,7 +2888,6 @@ static void iwl_trans_pcie_resume(struct iwl_trans *trans) .ref = iwl_trans_pcie_ref, \ .unref = iwl_trans_pcie_unref, \ .dump_data = iwl_trans_pcie_dump_data, \ - .wait_tx_queues_empty = iwl_trans_pcie_wait_txq_empty, \ .d3_suspend = iwl_trans_pcie_d3_suspend, \ .d3_resume = iwl_trans_pcie_d3_resume @@ -2865,6 +2917,8 @@ static const struct iwl_trans_ops trans_ops_pcie = { .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode, + .wait_tx_queues_empty = iwl_trans_pcie_wait_txqs_empty, + .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, }; @@ -2884,6 +2938,7 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { .txq_alloc = iwl_trans_pcie_dyn_txq_alloc, .txq_free = iwl_trans_pcie_dyn_txq_free, + .wait_txq_empty = iwl_trans_pcie_wait_txq_empty, }; struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, @@ -2988,7 +3043,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, * "dash" value). To keep hw_rev backwards compatible - we'll store it * in the old format. */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) { unsigned long flags; trans->hw_rev = (trans->hw_rev & 0xfff0) | diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 9c9bfbbabdf1..6e186501f43c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -49,6 +49,7 @@ * *****************************************************************************/ #include <linux/pm_runtime.h> +#include <net/tso.h> #include "iwl-debug.h" #include "iwl-csr.h" @@ -226,6 +227,143 @@ static int iwl_pcie_gen2_set_tb(struct iwl_trans *trans, return idx; } +static int iwl_pcie_gen2_build_amsdu(struct iwl_trans *trans, + struct sk_buff *skb, + struct iwl_tfh_tfd *tfd, int start_len, + u8 hdr_len, struct iwl_device_cmd *dev_cmd) +{ +#ifdef CONFIG_INET + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; + struct ieee80211_hdr *hdr = (void *)skb->data; + unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room; + unsigned int mss = skb_shinfo(skb)->gso_size; + u16 length, iv_len, amsdu_pad; + u8 *start_hdr; + struct iwl_tso_hdr_page *hdr_page; + struct page **page_ptr; + struct tso_t tso; + + /* if the packet is protected, then it must be CCMP or GCMP */ + iv_len = ieee80211_has_protected(hdr->frame_control) ? + IEEE80211_CCMP_HDR_LEN : 0; + + trace_iwlwifi_dev_tx(trans->dev, skb, tfd, sizeof(*tfd), + &dev_cmd->hdr, start_len, NULL, 0); + + ip_hdrlen = skb_transport_header(skb) - skb_network_header(skb); + snap_ip_tcp_hdrlen = 8 + ip_hdrlen + tcp_hdrlen(skb); + total_len = skb->len - snap_ip_tcp_hdrlen - hdr_len - iv_len; + amsdu_pad = 0; + + /* total amount of header we may need for this A-MSDU */ + hdr_room = DIV_ROUND_UP(total_len, mss) * + (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)) + iv_len; + + /* Our device supports 9 segments at most, it will fit in 1 page */ + hdr_page = get_page_hdr(trans, hdr_room); + if (!hdr_page) + return -ENOMEM; + + get_page(hdr_page->page); + start_hdr = hdr_page->pos; + page_ptr = (void *)((u8 *)skb->cb + trans_pcie->page_offs); + *page_ptr = hdr_page->page; + memcpy(hdr_page->pos, skb->data + hdr_len, iv_len); + hdr_page->pos += iv_len; + + /* + * Pull the ieee80211 header + IV to be able to use TSO core, + * we will restore it for the tx_status flow. + */ + skb_pull(skb, hdr_len + iv_len); + + /* + * Remove the length of all the headers that we don't actually + * have in the MPDU by themselves, but that we duplicate into + * all the different MSDUs inside the A-MSDU. + */ + le16_add_cpu(&tx_cmd->len, -snap_ip_tcp_hdrlen); + + tso_start(skb, &tso); + + while (total_len) { + /* this is the data left for this subframe */ + unsigned int data_left = min_t(unsigned int, mss, total_len); + struct sk_buff *csum_skb = NULL; + unsigned int tb_len; + dma_addr_t tb_phys; + struct tcphdr *tcph; + u8 *iph, *subf_hdrs_start = hdr_page->pos; + + total_len -= data_left; + + memset(hdr_page->pos, 0, amsdu_pad); + hdr_page->pos += amsdu_pad; + amsdu_pad = (4 - (sizeof(struct ethhdr) + snap_ip_tcp_hdrlen + + data_left)) & 0x3; + ether_addr_copy(hdr_page->pos, ieee80211_get_DA(hdr)); + hdr_page->pos += ETH_ALEN; + ether_addr_copy(hdr_page->pos, ieee80211_get_SA(hdr)); + hdr_page->pos += ETH_ALEN; + + length = snap_ip_tcp_hdrlen + data_left; + *((__be16 *)hdr_page->pos) = cpu_to_be16(length); + hdr_page->pos += sizeof(length); + + /* + * This will copy the SNAP as well which will be considered + * as MAC header. + */ + tso_build_hdr(skb, hdr_page->pos, &tso, data_left, !total_len); + iph = hdr_page->pos + 8; + tcph = (void *)(iph + ip_hdrlen); + + hdr_page->pos += snap_ip_tcp_hdrlen; + + tb_len = hdr_page->pos - start_hdr; + tb_phys = dma_map_single(trans->dev, start_hdr, + tb_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { + dev_kfree_skb(csum_skb); + goto out_err; + } + iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, tb_len); + trace_iwlwifi_dev_tx_tso_chunk(trans->dev, start_hdr, tb_len); + /* add this subframe's headers' length to the tx_cmd */ + le16_add_cpu(&tx_cmd->len, hdr_page->pos - subf_hdrs_start); + + /* prepare the start_hdr for the next subframe */ + start_hdr = hdr_page->pos; + + /* put the payload */ + while (data_left) { + tb_len = min_t(unsigned int, tso.size, data_left); + tb_phys = dma_map_single(trans->dev, tso.data, + tb_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { + dev_kfree_skb(csum_skb); + goto out_err; + } + iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, tb_len); + trace_iwlwifi_dev_tx_tso_chunk(trans->dev, tso.data, + tb_len); + + data_left -= tb_len; + tso_build_data(skb, &tso, tb_len); + } + } + + /* re -add the WiFi header and IV */ + skb_push(skb, hdr_len + iv_len); + + return 0; + +out_err: +#endif + return -EINVAL; +} + static struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, @@ -238,15 +376,21 @@ struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, struct iwl_tfh_tfd *tfd = iwl_pcie_get_tfd(trans_pcie, txq, txq->write_ptr); dma_addr_t tb_phys; + bool amsdu; int i, len, tb1_len, tb2_len, hdr_len; void *tb1_addr; memset(tfd, 0, sizeof(*tfd)); + amsdu = ieee80211_is_data_qos(hdr->frame_control) && + (*ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_A_MSDU_PRESENT); + tb_phys = iwl_pcie_get_first_tb_dma(txq, txq->write_ptr); /* The first TB points to bi-directional DMA data */ - memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr, - IWL_FIRST_TB_SIZE); + if (!amsdu) + memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr, + IWL_FIRST_TB_SIZE); iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, IWL_FIRST_TB_SIZE); @@ -262,7 +406,11 @@ struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, len = sizeof(struct iwl_tx_cmd_gen2) + sizeof(struct iwl_cmd_header) + ieee80211_hdrlen(hdr->frame_control) - IWL_FIRST_TB_SIZE; - tb1_len = ALIGN(len, 4); + /* do not align A-MSDU to dword as the subframe header aligns it */ + if (amsdu) + tb1_len = len; + else + tb1_len = ALIGN(len, 4); /* map the data for TB1 */ tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_FIRST_TB_SIZE; @@ -271,8 +419,24 @@ struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, goto out_err; iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, tb1_len); - /* set up TFD's third entry to point to remainder of skb's head */ hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (amsdu) { + if (!iwl_pcie_gen2_build_amsdu(trans, skb, tfd, + tb1_len + IWL_FIRST_TB_SIZE, + hdr_len, dev_cmd)) + goto out_err; + + /* + * building the A-MSDU might have changed this data, so memcpy + * it now + */ + memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr, + IWL_FIRST_TB_SIZE); + return tfd; + } + + /* set up TFD's third entry to point to remainder of skb's head */ tb2_len = skb_headlen(skb) - hdr_len; if (tb2_len > 0) { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 386950a2d616..61b171ee32ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -762,7 +762,7 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); /* Enable L1-Active */ - if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + if (trans->cfg->device_family < IWL_DEVICE_FAMILY_8000) iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_L1_ACT_DIS); } @@ -1987,8 +1987,7 @@ static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, } #ifdef CONFIG_INET -static struct iwl_tso_hdr_page * -get_page_hdr(struct iwl_trans *trans, size_t len) +struct iwl_tso_hdr_page *get_page_hdr(struct iwl_trans *trans, size_t len) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tso_hdr_page *p = this_cpu_ptr(trans_pcie->tso_hdr_page); |