diff options
Diffstat (limited to 'drivers/net/wireless')
225 files changed, 9933 insertions, 4970 deletions
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index d0adbaf..7fe1964 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -2693,7 +2693,7 @@ static struct net_device *init_wifidev(struct airo_info *ai, dev->base_addr = ethdev->base_addr; dev->wireless_data = ethdev->wireless_data; SET_NETDEV_DEV(dev, ethdev->dev.parent); - memcpy(dev->dev_addr, ethdev->dev_addr, dev->addr_len); + eth_hw_addr_inherit(dev, ethdev); err = register_netdev(dev); if (err<0) { free_netdev(dev); diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index daeafef..e0ba7cd 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -159,7 +159,7 @@ struct ath_common { bool btcoex_enabled; bool disable_ani; - bool antenna_diversity; + bool bt_ant_diversity; }; struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 1a2ef51..744da6d 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -20,6 +20,12 @@ #include "debug.h" #include "htc.h" +void ath10k_bmi_start(struct ath10k *ar) +{ + ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n"); + ar->bmi.done_sent = false; +} + int ath10k_bmi_done(struct ath10k *ar) { struct bmi_cmd cmd; @@ -105,7 +111,8 @@ int ath10k_bmi_read_memory(struct ath10k *ar, ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &rxlen); if (ret) { - ath10k_warn("unable to read from the device\n"); + ath10k_warn("unable to read from the device (%d)\n", + ret); return ret; } @@ -149,7 +156,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar, ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, NULL, NULL); if (ret) { - ath10k_warn("unable to write to the device\n"); + ath10k_warn("unable to write to the device (%d)\n", + ret); return ret; } diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index 32c56aa..8d81ce1 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -184,6 +184,7 @@ struct bmi_target_info { #define BMI_CE_NUM_TO_TARG 0 #define BMI_CE_NUM_TO_HOST 1 +void ath10k_bmi_start(struct ath10k *ar); int ath10k_bmi_done(struct ath10k *ar); int ath10k_bmi_get_target_info(struct ath10k *ar, struct bmi_target_info *target_info); diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 61a8ac7..f8b969f 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -79,7 +79,7 @@ static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar, struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); void __iomem *indicator_addr; - if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) { + if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) { ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); return; } @@ -637,6 +637,7 @@ static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state, ath10k_pci_wake(ar); src_ring->hw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); + src_ring->hw_index &= nentries_mask; ath10k_pci_sleep(ar); } read_index = src_ring->hw_index; @@ -950,10 +951,12 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, ath10k_pci_wake(ar); src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); + src_ring->sw_index &= src_ring->nentries_mask; src_ring->hw_index = src_ring->sw_index; src_ring->write_index = ath10k_ce_src_ring_write_index_get(ar, ctrl_addr); + src_ring->write_index &= src_ring->nentries_mask; ath10k_pci_sleep(ar); src_ring->per_transfer_context = (void **)ptr; @@ -1035,8 +1038,10 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, ath10k_pci_wake(ar); dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr); + dest_ring->sw_index &= dest_ring->nentries_mask; dest_ring->write_index = ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr); + dest_ring->write_index &= dest_ring->nentries_mask; ath10k_pci_sleep(ar); dest_ring->per_transfer_context = (void **)ptr; diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 2b3426b..7226c23 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -100,7 +100,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar) goto conn_fail; /* Start HTC */ - status = ath10k_htc_start(ar->htc); + status = ath10k_htc_start(&ar->htc); if (status) goto conn_fail; @@ -116,7 +116,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar) return 0; timeout: - ath10k_htc_stop(ar->htc); + ath10k_htc_stop(&ar->htc); conn_fail: return status; } @@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, static int ath10k_download_board_data(struct ath10k *ar) { + const struct firmware *fw = ar->board_data; u32 board_data_size = QCA988X_BOARD_DATA_SZ; u32 address; - const struct firmware *fw; int ret; - fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, - ar->hw_params.fw.board); - if (IS_ERR(fw)) { - ath10k_err("could not fetch board data fw file (%ld)\n", - PTR_ERR(fw)); - return PTR_ERR(fw); - } - ret = ath10k_push_board_ext_data(ar, fw); if (ret) { ath10k_err("could not push board ext data (%d)\n", ret); @@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar) } exit: - release_firmware(fw); return ret; } static int ath10k_download_and_run_otp(struct ath10k *ar) { - const struct firmware *fw; - u32 address; + const struct firmware *fw = ar->otp; + u32 address = ar->hw_params.patch_load_addr; u32 exec_param; int ret; /* OTP is optional */ - if (ar->hw_params.fw.otp == NULL) { - ath10k_info("otp file not defined\n"); - return 0; - } - - address = ar->hw_params.patch_load_addr; - - fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, - ar->hw_params.fw.otp); - if (IS_ERR(fw)) { - ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw)); + if (!ar->otp) return 0; - } ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); if (ret) { @@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) } exit: - release_firmware(fw); return ret; } static int ath10k_download_fw(struct ath10k *ar) { - const struct firmware *fw; + const struct firmware *fw = ar->firmware; u32 address; int ret; - if (ar->hw_params.fw.fw == NULL) - return -EINVAL; - address = ar->hw_params.patch_load_addr; - fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, - ar->hw_params.fw.fw); - if (IS_ERR(fw)) { - ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw)); - return PTR_ERR(fw); - } - ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); if (ret) { ath10k_err("could not write fw (%d)\n", ret); @@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar) } exit: - release_firmware(fw); + return ret; +} + +static void ath10k_core_free_firmware_files(struct ath10k *ar) +{ + if (ar->board_data && !IS_ERR(ar->board_data)) + release_firmware(ar->board_data); + + if (ar->otp && !IS_ERR(ar->otp)) + release_firmware(ar->otp); + + if (ar->firmware && !IS_ERR(ar->firmware)) + release_firmware(ar->firmware); + + ar->board_data = NULL; + ar->otp = NULL; + ar->firmware = NULL; +} + +static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +{ + int ret = 0; + + if (ar->hw_params.fw.fw == NULL) { + ath10k_err("firmware file not defined\n"); + return -EINVAL; + } + + if (ar->hw_params.fw.board == NULL) { + ath10k_err("board data file not defined"); + return -EINVAL; + } + + ar->board_data = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(ar->board_data)) { + ret = PTR_ERR(ar->board_data); + ath10k_err("could not fetch board data (%d)\n", ret); + goto err; + } + + ar->firmware = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.fw); + if (IS_ERR(ar->firmware)) { + ret = PTR_ERR(ar->firmware); + ath10k_err("could not fetch firmware (%d)\n", ret); + goto err; + } + + /* OTP may be undefined. If so, don't fetch it at all */ + if (ar->hw_params.fw.otp == NULL) + return 0; + + ar->otp = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.otp); + if (IS_ERR(ar->otp)) { + ret = PTR_ERR(ar->otp); + ath10k_err("could not fetch otp (%d)\n", ret); + goto err; + } + + return 0; + +err: + ath10k_core_free_firmware_files(ar); return ret; } @@ -440,8 +476,35 @@ static int ath10k_init_hw_params(struct ath10k *ar) return 0; } +static void ath10k_core_restart(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, restart_work); + + mutex_lock(&ar->conf_mutex); + + switch (ar->state) { + case ATH10K_STATE_ON: + ath10k_halt(ar); + ar->state = ATH10K_STATE_RESTARTING; + ieee80211_restart_hw(ar->hw); + break; + case ATH10K_STATE_OFF: + /* this can happen if driver is being unloaded */ + ath10k_warn("cannot restart a device that hasn't been started\n"); + break; + case ATH10K_STATE_RESTARTING: + case ATH10K_STATE_RESTARTED: + ar->state = ATH10K_STATE_WEDGED; + /* fall through */ + case ATH10K_STATE_WEDGED: + ath10k_warn("device is wedged, will not restart\n"); + break; + } + + mutex_unlock(&ar->conf_mutex); +} + struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, - enum ath10k_bus bus, const struct ath10k_hif_ops *hif_ops) { struct ath10k *ar; @@ -458,9 +521,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, ar->hif.priv = hif_priv; ar->hif.ops = hif_ops; - ar->hif.bus = bus; - - ar->free_vdev_map = 0xFF; /* 8 vdevs */ init_completion(&ar->scan.started); init_completion(&ar->scan.completed); @@ -487,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, init_waitqueue_head(&ar->event_queue); + INIT_WORK(&ar->restart_work, ath10k_core_restart); + return ar; err_wq: @@ -504,24 +566,11 @@ void ath10k_core_destroy(struct ath10k *ar) } EXPORT_SYMBOL(ath10k_core_destroy); - -int ath10k_core_register(struct ath10k *ar) +int ath10k_core_start(struct ath10k *ar) { - struct ath10k_htc_ops htc_ops; - struct bmi_target_info target_info; int status; - memset(&target_info, 0, sizeof(target_info)); - status = ath10k_bmi_get_target_info(ar, &target_info); - if (status) - goto err; - - ar->target_version = target_info.version; - ar->hw->wiphy->hw_version = target_info.version; - - status = ath10k_init_hw_params(ar); - if (status) - goto err; + ath10k_bmi_start(ar); if (ath10k_init_configure_target(ar)) { status = -EINVAL; @@ -536,32 +585,32 @@ int ath10k_core_register(struct ath10k *ar) if (status) goto err; - htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete; + ar->htc.htc_ops.target_send_suspend_complete = + ath10k_send_suspend_complete; - ar->htc = ath10k_htc_create(ar, &htc_ops); - if (IS_ERR(ar->htc)) { - status = PTR_ERR(ar->htc); - ath10k_err("could not create HTC (%d)\n", status); + status = ath10k_htc_init(ar); + if (status) { + ath10k_err("could not init HTC (%d)\n", status); goto err; } status = ath10k_bmi_done(ar); if (status) - goto err_htc_destroy; + goto err; status = ath10k_wmi_attach(ar); if (status) { ath10k_err("WMI attach failed: %d\n", status); - goto err_htc_destroy; + goto err; } - status = ath10k_htc_wait_target(ar->htc); + status = ath10k_htc_wait_target(&ar->htc); if (status) goto err_wmi_detach; - ar->htt = ath10k_htt_attach(ar); - if (!ar->htt) { - status = -ENOMEM; + status = ath10k_htt_attach(ar); + if (status) { + ath10k_err("could not attach htt (%d)\n", status); goto err_wmi_detach; } @@ -588,77 +637,127 @@ int ath10k_core_register(struct ath10k *ar) goto err_disconnect_htc; } - status = ath10k_htt_attach_target(ar->htt); - if (status) - goto err_disconnect_htc; - - status = ath10k_mac_register(ar); + status = ath10k_htt_attach_target(&ar->htt); if (status) goto err_disconnect_htc; - status = ath10k_debug_create(ar); - if (status) { - ath10k_err("unable to initialize debugfs\n"); - goto err_unregister_mac; - } + ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; return 0; -err_unregister_mac: - ath10k_mac_unregister(ar); err_disconnect_htc: - ath10k_htc_stop(ar->htc); + ath10k_htc_stop(&ar->htc); err_htt_detach: - ath10k_htt_detach(ar->htt); + ath10k_htt_detach(&ar->htt); err_wmi_detach: ath10k_wmi_detach(ar); -err_htc_destroy: - ath10k_htc_destroy(ar->htc); err: return status; } -EXPORT_SYMBOL(ath10k_core_register); +EXPORT_SYMBOL(ath10k_core_start); -void ath10k_core_unregister(struct ath10k *ar) +void ath10k_core_stop(struct ath10k *ar) { - /* We must unregister from mac80211 before we stop HTC and HIF. - * Otherwise we will fail to submit commands to FW and mac80211 will be - * unhappy about callback failures. */ - ath10k_mac_unregister(ar); - ath10k_htc_stop(ar->htc); - ath10k_htt_detach(ar->htt); + ath10k_htc_stop(&ar->htc); + ath10k_htt_detach(&ar->htt); ath10k_wmi_detach(ar); - ath10k_htc_destroy(ar->htc); } -EXPORT_SYMBOL(ath10k_core_unregister); +EXPORT_SYMBOL(ath10k_core_stop); -int ath10k_core_target_suspend(struct ath10k *ar) +/* mac80211 manages fw/hw initialization through start/stop hooks. However in + * order to know what hw capabilities should be advertised to mac80211 it is + * necessary to load the firmware (and tear it down immediately since start + * hook will try to init it again) before registering */ +static int ath10k_core_probe_fw(struct ath10k *ar) { - int ret; + struct bmi_target_info target_info; + int ret = 0; + + ret = ath10k_hif_power_up(ar); + if (ret) { + ath10k_err("could not start pci hif (%d)\n", ret); + return ret; + } - ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__); + memset(&target_info, 0, sizeof(target_info)); + ret = ath10k_bmi_get_target_info(ar, &target_info); + if (ret) { + ath10k_err("could not get target info (%d)\n", ret); + ath10k_hif_power_down(ar); + return ret; + } - ret = ath10k_wmi_pdev_suspend_target(ar); - if (ret) - ath10k_warn("could not suspend target (%d)\n", ret); + ar->target_version = target_info.version; + ar->hw->wiphy->hw_version = target_info.version; - return ret; + ret = ath10k_init_hw_params(ar); + if (ret) { + ath10k_err("could not get hw params (%d)\n", ret); + ath10k_hif_power_down(ar); + return ret; + } + + ret = ath10k_core_fetch_firmware_files(ar); + if (ret) { + ath10k_err("could not fetch firmware files (%d)\n", ret); + ath10k_hif_power_down(ar); + return ret; + } + + ret = ath10k_core_start(ar); + if (ret) { + ath10k_err("could not init core (%d)\n", ret); + ath10k_core_free_firmware_files(ar); + ath10k_hif_power_down(ar); + return ret; + } + + ath10k_core_stop(ar); + ath10k_hif_power_down(ar); + return 0; } -EXPORT_SYMBOL(ath10k_core_target_suspend); -int ath10k_core_target_resume(struct ath10k *ar) +int ath10k_core_register(struct ath10k *ar) { - int ret; + int status; - ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__); + status = ath10k_core_probe_fw(ar); + if (status) { + ath10k_err("could not probe fw (%d)\n", status); + return status; + } - ret = ath10k_wmi_pdev_resume_target(ar); - if (ret) - ath10k_warn("could not resume target (%d)\n", ret); + status = ath10k_mac_register(ar); + if (status) { + ath10k_err("could not register to mac80211 (%d)\n", status); + goto err_release_fw; + } - return ret; + status = ath10k_debug_create(ar); + if (status) { + ath10k_err("unable to initialize debugfs\n"); + goto err_unregister_mac; + } + + return 0; + +err_unregister_mac: + ath10k_mac_unregister(ar); +err_release_fw: + ath10k_core_free_firmware_files(ar); + return status; +} +EXPORT_SYMBOL(ath10k_core_register); + +void ath10k_core_unregister(struct ath10k *ar) +{ + /* We must unregister from mac80211 before we stop HTC and HIF. + * Otherwise we will fail to submit commands to FW and mac80211 will be + * unhappy about callback failures. */ + ath10k_mac_unregister(ar); + ath10k_core_free_firmware_files(ar); } -EXPORT_SYMBOL(ath10k_core_target_resume); +EXPORT_SYMBOL(ath10k_core_unregister); MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Core module for QCA988X PCIe devices."); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 539336d..e4bba56 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -23,6 +23,7 @@ #include <linux/types.h> #include <linux/pci.h> +#include "htt.h" #include "htc.h" #include "hw.h" #include "targaddrs.h" @@ -37,16 +38,13 @@ #define ATH10K_SCAN_ID 0 #define WMI_READY_TIMEOUT (5 * HZ) #define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ) +#define ATH10K_NUM_CHANS 38 /* Antenna noise floor */ #define ATH10K_DEFAULT_NOISE_FLOOR -95 struct ath10k; -enum ath10k_bus { - ATH10K_BUS_PCI, -}; - struct ath10k_skb_cb { dma_addr_t paddr; bool is_mapped; @@ -250,6 +248,28 @@ struct ath10k_debug { struct completion event_stats_compl; }; +enum ath10k_state { + ATH10K_STATE_OFF = 0, + ATH10K_STATE_ON, + + /* When doing firmware recovery the device is first powered down. + * mac80211 is supposed to call in to start() hook later on. It is + * however possible that driver unloading and firmware crash overlap. + * mac80211 can wait on conf_mutex in stop() while the device is + * stopped in ath10k_core_restart() work holding conf_mutex. The state + * RESTARTED means that the device is up and mac80211 has started hw + * reconfiguration. Once mac80211 is done with the reconfiguration we + * set the state to STATE_ON in restart_complete(). */ + ATH10K_STATE_RESTARTING, + ATH10K_STATE_RESTARTED, + + /* The device has crashed while restarting hw. This state is like ON + * but commands are blocked in HTC and -ECOMM response is given. This + * prevents completion timeouts and makes the driver more responsive to + * userspace commands. This is also prevents recursive recovery. */ + ATH10K_STATE_WEDGED, +}; + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; @@ -266,6 +286,7 @@ struct ath10k { u32 hw_max_tx_power; u32 ht_cap_info; u32 vht_cap_info; + u32 num_rf_chains; struct targetdef *targetdef; struct hostdef *hostdef; @@ -274,19 +295,16 @@ struct ath10k { struct { void *priv; - enum ath10k_bus bus; const struct ath10k_hif_ops *ops; } hif; - struct ath10k_wmi wmi; - wait_queue_head_t event_queue; bool is_target_paused; struct ath10k_bmi bmi; - - struct ath10k_htc *htc; - struct ath10k_htt *htt; + struct ath10k_wmi wmi; + struct ath10k_htc htc; + struct ath10k_htt htt; struct ath10k_hw_params { u32 id; @@ -301,6 +319,10 @@ struct ath10k { } fw; } hw_params; + const struct firmware *board_data; + const struct firmware *otp; + const struct firmware *firmware; + struct { struct completion started; struct completion completed; @@ -350,20 +372,28 @@ struct ath10k { struct completion offchan_tx_completed; struct sk_buff *offchan_tx_skb; + enum ath10k_state state; + + struct work_struct restart_work; + + /* cycle count is reported twice for each visited channel during scan. + * access protected by data_lock */ + u32 survey_last_rx_clear_count; + u32 survey_last_cycle_count; + struct survey_info survey[ATH10K_NUM_CHANS]; + #ifdef CONFIG_ATH10K_DEBUGFS struct ath10k_debug debug; #endif }; struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, - enum ath10k_bus bus, const struct ath10k_hif_ops *hif_ops); void ath10k_core_destroy(struct ath10k *ar); +int ath10k_core_start(struct ath10k *ar); +void ath10k_core_stop(struct ath10k *ar); int ath10k_core_register(struct ath10k *ar); void ath10k_core_unregister(struct ath10k *ar); -int ath10k_core_target_suspend(struct ath10k *ar); -int ath10k_core_target_resume(struct ath10k *ar); - #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 499034b..3d65594 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, struct wmi_pdev_stats *ps; int i; - mutex_lock(&ar->conf_mutex); + spin_lock_bh(&ar->data_lock); stats = &ar->debug.target_stats; @@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, } } + spin_unlock_bh(&ar->data_lock); mutex_unlock(&ar->conf_mutex); complete(&ar->debug.event_stats_compl); } @@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, { struct ath10k *ar = file->private_data; struct ath10k_target_stats *fw_stats; - char *buf; + char *buf = NULL; unsigned int len = 0, buf_len = 2500; - ssize_t ret_cnt; + ssize_t ret_cnt = 0; long left; int i; int ret; fw_stats = &ar->debug.target_stats; + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH10K_STATE_ON) + goto exit; + buf = kzalloc(buf_len, GFP_KERNEL); if (!buf) - return -ENOMEM; + goto exit; ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT); if (ret) { ath10k_warn("could not request stats (%d)\n", ret); - kfree(buf); - return -EIO; + goto exit; } left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ); + if (left <= 0) + goto exit; - if (left <= 0) { - kfree(buf); - return -ETIMEDOUT; - } - - mutex_lock(&ar->conf_mutex); - + spin_lock_bh(&ar->data_lock); len += scnprintf(buf + len, buf_len - len, "\n"); len += scnprintf(buf + len, buf_len - len, "%30s\n", "ath10k PDEV stats"); @@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, fw_stats->peer_stat[i].peer_tx_rate); len += scnprintf(buf + len, buf_len - len, "\n"); } + spin_unlock_bh(&ar->data_lock); if (len > buf_len) len = buf_len; ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); +exit: mutex_unlock(&ar->conf_mutex); - kfree(buf); return ret_cnt; } @@ -443,6 +445,60 @@ static const struct file_operations fops_fw_stats = { .llseek = default_llseek, }; +static ssize_t ath10k_read_simulate_fw_crash(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char buf[] = "To simulate firmware crash write the keyword" + " `crash` to this file.\nThis will force firmware" + " to report a crash to the host system.\n"; + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +static ssize_t ath10k_write_simulate_fw_crash(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32] = {}; + int ret; + + mutex_lock(&ar->conf_mutex); + + simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) { + ret = -EINVAL; + goto exit; + } + + if (ar->state != ATH10K_STATE_ON && + ar->state != ATH10K_STATE_RESTARTED) { + ret = -ENETDOWN; + goto exit; + } + + ath10k_info("simulating firmware crash\n"); + + ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0); + if (ret) + ath10k_warn("failed to force fw hang (%d)\n", ret); + + if (ret == 0) + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_simulate_fw_crash = { + .read = ath10k_read_simulate_fw_crash, + .write = ath10k_write_simulate_fw_crash, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_create(struct ath10k *ar) { ar->debug.debugfs_phy = debugfs_create_dir("ath10k", @@ -459,6 +515,9 @@ int ath10k_debug_create(struct ath10k *ar) debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_wmi_services); + debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_simulate_fw_crash); + return 0; } #endif /* CONFIG_ATH10K_DEBUGFS */ diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h index 73a24d4..dcdea68 100644 --- a/drivers/net/wireless/ath/ath10k/hif.h +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -46,8 +46,11 @@ struct ath10k_hif_ops { void *request, u32 request_len, void *response, u32 *response_len); + /* Post BMI phase, after FW is loaded. Starts regular operation */ int (*start)(struct ath10k *ar); + /* Clean up what start() did. This does not revert to BMI phase. If + * desired so, call power_down() and power_up() */ void (*stop)(struct ath10k *ar); int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id, @@ -66,10 +69,20 @@ struct ath10k_hif_ops { */ void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force); - void (*init)(struct ath10k *ar, - struct ath10k_hif_cb *callbacks); + void (*set_callbacks)(struct ath10k *ar, + struct ath10k_hif_cb *callbacks); u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id); + + /* Power up the device and enter BMI transfer mode for FW download */ + int (*power_up)(struct ath10k *ar); + + /* Power down the device and free up resources. stop() must be called + * before this if start() was called earlier */ + void (*power_down)(struct ath10k *ar); + + int (*suspend)(struct ath10k *ar); + int (*resume)(struct ath10k *ar); }; @@ -122,10 +135,10 @@ static inline void ath10k_hif_send_complete_check(struct ath10k *ar, ar->hif.ops->send_complete_check(ar, pipe_id, force); } -static inline void ath10k_hif_init(struct ath10k *ar, - struct ath10k_hif_cb *callbacks) +static inline void ath10k_hif_set_callbacks(struct ath10k *ar, + struct ath10k_hif_cb *callbacks) { - ar->hif.ops->init(ar, callbacks); + ar->hif.ops->set_callbacks(ar, callbacks); } static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar, @@ -134,4 +147,30 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar, return ar->hif.ops->get_free_queue_number(ar, pipe_id); } +static inline int ath10k_hif_power_up(struct ath10k *ar) +{ + return ar->hif.ops->power_up(ar); +} + +static inline void ath10k_hif_power_down(struct ath10k *ar) +{ + ar->hif.ops->power_down(ar); +} + +static inline int ath10k_hif_suspend(struct ath10k *ar) +{ + if (!ar->hif.ops->suspend) + return -EOPNOTSUPP; + + return ar->hif.ops->suspend(ar); +} + +static inline int ath10k_hif_resume(struct ath10k *ar) +{ + if (!ar->hif.ops->resume) + return -EOPNOTSUPP; + + return ar->hif.ops->resume(ar); +} + #endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 74363c9..ef3329e 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -246,15 +246,22 @@ int ath10k_htc_send(struct ath10k_htc *htc, { struct ath10k_htc_ep *ep = &htc->endpoint[eid]; + if (htc->ar->state == ATH10K_STATE_WEDGED) + return -ECOMM; + if (eid >= ATH10K_HTC_EP_COUNT) { ath10k_warn("Invalid endpoint id: %d\n", eid); return -ENOENT; } - skb_push(skb, sizeof(struct ath10k_htc_hdr)); - spin_lock_bh(&htc->tx_lock); + if (htc->stopped) { + spin_unlock_bh(&htc->tx_lock); + return -ESHUTDOWN; + } + __skb_queue_tail(&ep->tx_queue, skb); + skb_push(skb, sizeof(struct ath10k_htc_hdr)); spin_unlock_bh(&htc->tx_lock); queue_work(htc->ar->workqueue, &ep->send_work); @@ -265,25 +272,19 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb, unsigned int eid) { - struct ath10k_htc *htc = ar->htc; + struct ath10k_htc *htc = &ar->htc; struct ath10k_htc_ep *ep = &htc->endpoint[eid]; - bool stopping; ath10k_htc_notify_tx_completion(ep, skb); /* the skb now belongs to the completion handler */ + /* note: when using TX credit flow, the re-checking of queues happens + * when credits flow back from the target. in the non-TX credit case, + * we recheck after the packet completes */ spin_lock_bh(&htc->tx_lock); - stopping = htc->stopping; - spin_unlock_bh(&htc->tx_lock); - - if (!ep->tx_credit_flow_enabled && !stopping) - /* - * note: when using TX credit flow, the re-checking of - * queues happens when credits flow back from the target. - * in the non-TX credit case, we recheck after the packet - * completes - */ + if (!ep->tx_credit_flow_enabled && !htc->stopped) queue_work(ar->workqueue, &ep->send_work); + spin_unlock_bh(&htc->tx_lock); return 0; } @@ -414,7 +415,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, u8 pipe_id) { int status = 0; - struct ath10k_htc *htc = ar->htc; + struct ath10k_htc *htc = &ar->htc; struct ath10k_htc_hdr *hdr; struct ath10k_htc_ep *ep; u16 payload_len; @@ -751,8 +752,9 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, tx_alloc = ath10k_htc_get_credit_allocation(htc, conn_req->service_id); if (!tx_alloc) - ath10k_warn("HTC Service %s does not allocate target credits\n", - htc_service_name(conn_req->service_id)); + ath10k_dbg(ATH10K_DBG_HTC, + "HTC Service %s does not allocate target credits\n", + htc_service_name(conn_req->service_id)); skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); if (!skb) { @@ -947,7 +949,7 @@ void ath10k_htc_stop(struct ath10k_htc *htc) struct ath10k_htc_ep *ep; spin_lock_bh(&htc->tx_lock); - htc->stopping = true; + htc->stopped = true; spin_unlock_bh(&htc->tx_lock); for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) { @@ -956,26 +958,18 @@ void ath10k_htc_stop(struct ath10k_htc *htc) } ath10k_hif_stop(htc->ar); - ath10k_htc_reset_endpoint_states(htc); } /* registered target arrival callback from the HIF layer */ -struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, - struct ath10k_htc_ops *htc_ops) +int ath10k_htc_init(struct ath10k *ar) { struct ath10k_hif_cb htc_callbacks; struct ath10k_htc_ep *ep = NULL; - struct ath10k_htc *htc = NULL; - - /* FIXME: use struct ath10k instead */ - htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL); - if (!htc) - return ERR_PTR(-ENOMEM); + struct ath10k_htc *htc = &ar->htc; spin_lock_init(&htc->tx_lock); - memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops)); - + htc->stopped = false; ath10k_htc_reset_endpoint_states(htc); /* setup HIF layer callbacks */ @@ -986,15 +980,10 @@ struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, /* Get HIF default pipe for HTC message exchange */ ep = &htc->endpoint[ATH10K_HTC_EP_0]; - ath10k_hif_init(ar, &htc_callbacks); + ath10k_hif_set_callbacks(ar, &htc_callbacks); ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id); init_completion(&htc->ctl_resp); - return htc; -} - -void ath10k_htc_destroy(struct ath10k_htc *htc) -{ - kfree(htc); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index fa45844..e1dd8c7 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -335,7 +335,7 @@ struct ath10k_htc { struct ath10k *ar; struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT]; - /* protects endpoint and stopping fields */ + /* protects endpoint and stopped fields */ spinlock_t tx_lock; struct ath10k_htc_ops htc_ops; @@ -349,11 +349,10 @@ struct ath10k_htc { struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT]; int target_credit_size; - bool stopping; + bool stopped; }; -struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, - struct ath10k_htc_ops *htc_ops); +int ath10k_htc_init(struct ath10k *ar); int ath10k_htc_wait_target(struct ath10k_htc *htc); int ath10k_htc_start(struct ath10k_htc *htc); int ath10k_htc_connect_service(struct ath10k_htc *htc, @@ -362,7 +361,6 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, struct sk_buff *packet); void ath10k_htc_stop(struct ath10k_htc *htc); -void ath10k_htc_destroy(struct ath10k_htc *htc); struct sk_buff *ath10k_htc_alloc_skb(int size); #endif diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 185a546..39342c5 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -16,6 +16,7 @@ */ #include <linux/slab.h> +#include <linux/if_ether.h> #include "htt.h" #include "core.h" @@ -36,7 +37,7 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt) /* connect to control service */ conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG; - status = ath10k_htc_connect_service(htt->ar->htc, &conn_req, + status = ath10k_htc_connect_service(&htt->ar->htc, &conn_req, &conn_resp); if (status) @@ -47,15 +48,11 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt) return 0; } -struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) +int ath10k_htt_attach(struct ath10k *ar) { - struct ath10k_htt *htt; + struct ath10k_htt *htt = &ar->htt; int ret; - htt = kzalloc(sizeof(*htt), GFP_KERNEL); - if (!htt) - return NULL; - htt->ar = ar; htt->max_throughput_mbps = 800; @@ -65,8 +62,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) * since ath10k_htt_rx_attach involves sending a rx ring configure * message to the target. */ - if (ath10k_htt_htc_attach(htt)) + ret = ath10k_htt_htc_attach(htt); + if (ret) { + ath10k_err("could not attach htt htc (%d)\n", ret); goto err_htc_attach; + } ret = ath10k_htt_tx_attach(htt); if (ret) { @@ -74,8 +74,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) goto err_htc_attach; } - if (ath10k_htt_rx_attach(htt)) + ret = ath10k_htt_rx_attach(htt); + if (ret) { + ath10k_err("could not attach htt rx (%d)\n", ret); goto err_rx_attach; + } /* * Prefetch enough data to satisfy target @@ -89,13 +92,12 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) 8 + /* llc snap */ 2; /* ip4 dscp or ip6 priority */ - return htt; + return 0; err_rx_attach: ath10k_htt_tx_detach(htt); err_htc_attach: - kfree(htt); - return NULL; + return ret; } #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ) @@ -148,5 +150,4 @@ void ath10k_htt_detach(struct ath10k_htt *htt) { ath10k_htt_rx_detach(htt); ath10k_htt_tx_detach(htt); - kfree(htt); } diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index a7a7aa0..318be46 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -20,7 +20,6 @@ #include <linux/bug.h> -#include "core.h" #include "htc.h" #include "rx_desc.h" @@ -1317,7 +1316,7 @@ struct htt_rx_desc { #define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */ #define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1) -struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar); +int ath10k_htt_attach(struct ath10k *ar); int ath10k_htt_attach_target(struct ath10k_htt *htt); void ath10k_htt_detach(struct ath10k_htt *htt); diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index de058d7..e784c40 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -15,6 +15,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "core.h" #include "htc.h" #include "htt.h" #include "txrx.h" @@ -803,6 +804,37 @@ static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb) return false; } +static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + u32 flags, info; + bool is_ip4, is_ip6; + bool is_tcp, is_udp; + bool ip_csum_ok, tcpudp_csum_ok; + + rxd = (void *)skb->data - sizeof(*rxd); + flags = __le32_to_cpu(rxd->attention.flags); + info = __le32_to_cpu(rxd->msdu_start.info1); + + is_ip4 = !!(info & RX_MSDU_START_INFO1_IPV4_PROTO); + is_ip6 = !!(info & RX_MSDU_START_INFO1_IPV6_PROTO); + is_tcp = !!(info & RX_MSDU_START_INFO1_TCP_PROTO); + is_udp = !!(info & RX_MSDU_START_INFO1_UDP_PROTO); + ip_csum_ok = !(flags & RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL); + tcpudp_csum_ok = !(flags & RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL); + + if (!is_ip4 && !is_ip6) + return CHECKSUM_NONE; + if (!is_tcp && !is_udp) + return CHECKSUM_NONE; + if (!ip_csum_ok) + return CHECKSUM_NONE; + if (!tcpudp_csum_ok) + return CHECKSUM_NONE; + + return CHECKSUM_UNNECESSARY; +} + static void ath10k_htt_rx_handler(struct ath10k_htt *htt, struct htt_rx_indication *rx) { @@ -814,6 +846,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, u8 *fw_desc; int i, j; int ret; + int ip_summed; memset(&info, 0, sizeof(info)); @@ -888,6 +921,11 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, continue; } + /* The skb is not yet processed and it may be + * reallocated. Since the offload is in the original + * skb extract the checksum now and assign it later */ + ip_summed = ath10k_htt_rx_get_csum_state(msdu_head); + info.skb = msdu_head; info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head); info.signal = ATH10K_DEFAULT_NOISE_FLOOR; @@ -913,6 +951,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data)) ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n"); + info.skb->ip_summed = ip_summed; + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ", info.skb->data, info.skb->len); ath10k_process_rx(htt->ar, &info); @@ -979,6 +1019,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, info.status = HTT_RX_IND_MPDU_STATUS_OK; info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); + info.skb->ip_summed = ath10k_htt_rx_get_csum_state(info.skb); if (tkip_mic_err) { ath10k_warn("tkip mic error\n"); @@ -1036,7 +1077,7 @@ end: void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) { - struct ath10k_htt *htt = ar->htt; + struct ath10k_htt *htt = &ar->htt; struct htt_resp *resp = (struct htt_resp *)skb->data; /* confirm alignment */ diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index ef79106..656c254 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -92,7 +92,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt) /* At the beginning free queue number should hint us the maximum * queue length */ - pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id; + pipe = htt->ar->htc.endpoint[htt->eid].ul_pipe_id; htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar, pipe); @@ -153,7 +153,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt) void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) { struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); - struct ath10k_htt *htt = ar->htt; + struct ath10k_htt *htt = &ar->htt; if (skb_cb->htt.is_conf) { dev_kfree_skb_any(skb); @@ -194,7 +194,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) ATH10K_SKB_CB(skb)->htt.is_conf = true; - ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb); + ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); return ret; @@ -281,7 +281,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) ATH10K_SKB_CB(skb)->htt.is_conf = true; - ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb); + ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); return ret; @@ -346,7 +346,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) skb_cb->htt.refcount = 2; skb_cb->htt.msdu = msdu; - res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc); + res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); if (res) goto err; @@ -465,6 +465,8 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) flags1 = 0; flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID); flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID); + flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD; + flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD; frags_paddr = ATH10K_SKB_CB(txfrag)->paddr; @@ -486,7 +488,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) skb_cb->htt.txfrag = txfrag; skb_cb->htt.msdu = msdu; - res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc); + res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); if (res) goto err; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index da5c333..cf2ba4d 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -20,6 +20,7 @@ #include <net/mac80211.h> #include <linux/etherdevice.h> +#include "hif.h" #include "core.h" #include "debug.h" #include "wmi.h" @@ -43,6 +44,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif, .macaddr = macaddr, }; + lockdep_assert_held(&arvif->ar->conf_mutex); + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) arg.key_flags = WMI_KEY_PAIRWISE; else @@ -87,6 +90,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif, struct ath10k *ar = arvif->ar; int ret; + lockdep_assert_held(&ar->conf_mutex); + INIT_COMPLETION(ar->install_key_done); ret = ath10k_send_key(arvif, key, cmd, macaddr); @@ -327,6 +332,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) return 0; } +static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value) +{ + if (value != 0xFFFFFFFF) + value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold, + ATH10K_RTS_MAX); + + return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, + WMI_VDEV_PARAM_RTS_THRESHOLD, + value); +} + +static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value) +{ + if (value != 0xFFFFFFFF) + value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold, + ATH10K_FRAGMT_THRESHOLD_MIN, + ATH10K_FRAGMT_THRESHOLD_MAX); + + return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + value); +} + static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) { int ret; @@ -364,6 +392,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) spin_unlock_bh(&ar->data_lock); } +static void ath10k_peer_cleanup_all(struct ath10k *ar) +{ + struct ath10k_peer *peer, *tmp; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(peer, tmp, &ar->peers, list) { + list_del(&peer->list); + kfree(peer); + } + spin_unlock_bh(&ar->data_lock); +} + /************************/ /* Interface management */ /************************/ @@ -372,6 +414,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar) { int ret; + lockdep_assert_held(&ar->conf_mutex); + ret = wait_for_completion_timeout(&ar->vdev_setup_done, ATH10K_VDEV_SETUP_TIMEOUT_HZ); if (ret == 0) @@ -605,6 +649,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, { int ret = 0; + lockdep_assert_held(&arvif->ar->conf_mutex); + if (!info->enable_beacon) { ath10k_vdev_stop(arvif); return; @@ -631,6 +677,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, { int ret = 0; + lockdep_assert_held(&arvif->ar->conf_mutex); + if (!info->ibss_joined) { ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer); if (ret) @@ -680,6 +728,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) enum wmi_sta_ps_mode psmode; int ret; + lockdep_assert_held(&arvif->ar->conf_mutex); + if (vif->type != NL80211_IFTYPE_STATION) return; @@ -722,6 +772,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar, struct ieee80211_bss_conf *bss_conf, struct wmi_peer_assoc_complete_arg *arg) { + lockdep_assert_held(&ar->conf_mutex); + memcpy(arg->addr, sta->addr, ETH_ALEN); arg->vdev_id = arvif->vdev_id; arg->peer_aid = sta->aid; @@ -764,6 +816,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, const u8 *rsnie = NULL; const u8 *wpaie = NULL; + lockdep_assert_held(&ar->conf_mutex); + bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan, info->bssid, NULL, 0, 0, 0); if (bss) { @@ -804,6 +858,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar, u32 ratemask; int i; + lockdep_assert_held(&ar->conf_mutex); + sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band]; ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band]; rates = sband->bitrates; @@ -827,6 +883,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, int smps; int i, n; + lockdep_assert_held(&ar->conf_mutex); + if (!ht_cap->ht_supported) return; @@ -905,6 +963,8 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, u32 uapsd = 0; u32 max_sp = 0; + lockdep_assert_held(&ar->conf_mutex); + if (sta->wme) arg->peer_flags |= WMI_PEER_QOS; @@ -1056,6 +1116,8 @@ static int ath10k_peer_assoc(struct ath10k *ar, { struct wmi_peer_assoc_complete_arg arg; + lockdep_assert_held(&ar->conf_mutex); + memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg)); ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg); @@ -1079,6 +1141,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, struct ieee80211_sta *ap_sta; int ret; + lockdep_assert_held(&ar->conf_mutex); + rcu_read_lock(); ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); @@ -1119,6 +1183,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); int ret; + lockdep_assert_held(&ar->conf_mutex); + /* * For some reason, calling VDEV-DOWN before VDEV-STOP * makes the FW to send frames via HTT after disassociation. @@ -1152,6 +1218,8 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, { int ret = 0; + lockdep_assert_held(&ar->conf_mutex); + ret = ath10k_peer_assoc(ar, arvif, sta, NULL); if (ret) { ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr); @@ -1172,6 +1240,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif, { int ret = 0; + lockdep_assert_held(&ar->conf_mutex); + ret = ath10k_clear_peer_keys(arvif, sta->addr); if (ret) { ath10k_warn("could not clear all peer wep keys (%d)\n", ret); @@ -1198,6 +1268,8 @@ static int ath10k_update_channel_list(struct ath10k *ar) int ret; int i; + lockdep_assert_held(&ar->conf_mutex); + bands = hw->wiphy->bands; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!bands[band]) @@ -1276,21 +1348,19 @@ static int ath10k_update_channel_list(struct ath10k *ar) return ret; } -static void ath10k_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) +static void ath10k_regd_update(struct ath10k *ar) { - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct reg_dmn_pair_mapping *regpair; - struct ath10k *ar = hw->priv; int ret; - ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); + lockdep_assert_held(&ar->conf_mutex); ret = ath10k_update_channel_list(ar); if (ret) ath10k_warn("could not update channel list (%d)\n", ret); regpair = ar->ath_common.regulatory.regpair; + /* Target allows setting up per-band regdomain but ath_common provides * a combined one only */ ret = ath10k_wmi_pdev_set_regdomain(ar, @@ -1303,6 +1373,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, ath10k_warn("could not set pdev regdomain (%d)\n", ret); } +static void ath10k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath10k *ar = hw->priv; + + ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); + + mutex_lock(&ar->conf_mutex); + if (ar->state == ATH10K_STATE_ON) + ath10k_regd_update(ar); + mutex_unlock(&ar->conf_mutex); +} + /***************/ /* TX handlers */ /***************/ @@ -1322,9 +1406,9 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw, return; qos_ctl = ieee80211_get_qos_ctl(hdr); - memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN, - skb->len - ieee80211_hdrlen(hdr->frame_control)); - skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN); + memmove(skb->data + IEEE80211_QOS_CTL_LEN, + skb->data, (void *)qos_ctl - (void *)skb->data); + skb_pull(skb, IEEE80211_QOS_CTL_LEN); } static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) @@ -1397,15 +1481,15 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) int ret; if (ieee80211_is_mgmt(hdr->frame_control)) - ret = ath10k_htt_mgmt_tx(ar->htt, skb); + ret = ath10k_htt_mgmt_tx(&ar->htt, skb); else if (ieee80211_is_nullfunc(hdr->frame_control)) /* FW does not report tx status properly for NullFunc frames * unless they are sent through mgmt tx path. mac80211 sends * those frames when it detects link/beacon loss and depends on * the tx status to be correct. */ - ret = ath10k_htt_mgmt_tx(ar->htt, skb); + ret = ath10k_htt_mgmt_tx(&ar->htt, skb); else - ret = ath10k_htt_tx(ar->htt, skb); + ret = ath10k_htt_tx(&ar->htt, skb); if (ret) { ath10k_warn("tx failed (%d). dropping packet.\n", ret); @@ -1552,6 +1636,10 @@ static int ath10k_abort_scan(struct ath10k *ar) ret = ath10k_wmi_stop_scan(ar, &arg); if (ret) { ath10k_warn("could not submit wmi stop scan (%d)\n", ret); + spin_lock_bh(&ar->data_lock); + ar->scan.in_progress = false; + ath10k_offchan_tx_purge(ar); + spin_unlock_bh(&ar->data_lock); return -EIO; } @@ -1645,10 +1733,14 @@ static void ath10k_tx(struct ieee80211_hw *hw, tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; } - ath10k_tx_h_qos_workaround(hw, control, skb); - ath10k_tx_h_update_wep_key(skb); - ath10k_tx_h_add_p2p_noa_ie(ar, skb); - ath10k_tx_h_seq_no(skb); + /* it makes no sense to process injected frames like that */ + if (info->control.vif && + info->control.vif->type != NL80211_IFTYPE_MONITOR) { + ath10k_tx_h_qos_workaround(hw, control, skb); + ath10k_tx_h_update_wep_key(skb); + ath10k_tx_h_add_p2p_noa_ie(ar, skb); + ath10k_tx_h_seq_no(skb); + } memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb))); ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id; @@ -1673,10 +1765,57 @@ static void ath10k_tx(struct ieee80211_hw *hw, /* * Initialize various parameters with default vaules. */ +void ath10k_halt(struct ath10k *ar) +{ + lockdep_assert_held(&ar->conf_mutex); + + del_timer_sync(&ar->scan.timeout); + ath10k_offchan_tx_purge(ar); + ath10k_peer_cleanup_all(ar); + ath10k_core_stop(ar); + ath10k_hif_power_down(ar); + + spin_lock_bh(&ar->data_lock); + if (ar->scan.in_progress) { + del_timer(&ar->scan.timeout); + ar->scan.in_progress = false; + ieee80211_scan_completed(ar->hw, true); + } + spin_unlock_bh(&ar->data_lock); +} + static int ath10k_start(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; - int ret; + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH10K_STATE_OFF && + ar->state != ATH10K_STATE_RESTARTING) { + ret = -EINVAL; + goto exit; + } + + ret = ath10k_hif_power_up(ar); + if (ret) { + ath10k_err("could not init hif (%d)\n", ret); + ar->state = ATH10K_STATE_OFF; + goto exit; + } + + ret = ath10k_core_start(ar); + if (ret) { + ath10k_err("could not init core (%d)\n", ret); + ath10k_hif_power_down(ar); + ar->state = ATH10K_STATE_OFF; + goto exit; + } + + if (ar->state == ATH10K_STATE_OFF) + ar->state = ATH10K_STATE_ON; + else if (ar->state == ATH10K_STATE_RESTARTING) + ar->state = ATH10K_STATE_RESTARTED; ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); if (ret) @@ -1688,6 +1827,10 @@ static int ath10k_start(struct ieee80211_hw *hw) ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", ret); + ath10k_regd_update(ar); + +exit: + mutex_unlock(&ar->conf_mutex); return 0; } @@ -1695,18 +1838,48 @@ static void ath10k_stop(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; - /* avoid leaks in case FW never confirms scan for offchannel */ + mutex_lock(&ar->conf_mutex); + if (ar->state == ATH10K_STATE_ON || + ar->state == ATH10K_STATE_RESTARTED || + ar->state == ATH10K_STATE_WEDGED) + ath10k_halt(ar); + + ar->state = ATH10K_STATE_OFF; + mutex_unlock(&ar->conf_mutex); + cancel_work_sync(&ar->offchan_tx_work); - ath10k_offchan_tx_purge(ar); + cancel_work_sync(&ar->restart_work); } -static int ath10k_config(struct ieee80211_hw *hw, u32 changed) +static void ath10k_config_ps(struct ath10k *ar) { struct ath10k_generic_iter ar_iter; + + lockdep_assert_held(&ar->conf_mutex); + + /* During HW reconfiguration mac80211 reports all interfaces that were + * running until reconfiguration was started. Since FW doesn't have any + * vdevs at this point we must not iterate over this interface list. + * This setting will be updated upon add_interface(). */ + if (ar->state == ATH10K_STATE_RESTARTED) + return; + + memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); + ar_iter.ar = ar; + + ieee80211_iterate_active_interfaces_atomic( + ar->hw, IEEE80211_IFACE_ITER_NORMAL, + ath10k_ps_iter, &ar_iter); + + if (ar_iter.ret) + ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret); +} + +static int ath10k_config(struct ieee80211_hw *hw, u32 changed) +{ struct ath10k *ar = hw->priv; struct ieee80211_conf *conf = &hw->conf; int ret = 0; - u32 flags; mutex_lock(&ar->conf_mutex); @@ -1718,18 +1891,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) spin_unlock_bh(&ar->data_lock); } - if (changed & IEEE80211_CONF_CHANGE_PS) { - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; - flags = IEEE80211_IFACE_ITER_RESUME_ALL; - - ieee80211_iterate_active_interfaces_atomic(hw, - flags, - ath10k_ps_iter, - &ar_iter); - - ret = ar_iter.ret; - } + if (changed & IEEE80211_CONF_CHANGE_PS) + ath10k_config_ps(ar); if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if (conf->flags & IEEE80211_CONF_MONITOR) @@ -1738,6 +1901,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) ret = ath10k_monitor_destroy(ar); } + ath10k_wmi_flush_tx(ar); mutex_unlock(&ar->conf_mutex); return ret; } @@ -1761,6 +1925,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); + memset(arvif, 0, sizeof(*arvif)); + arvif->ar = ar; arvif->vif = vif; @@ -1859,6 +2025,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ath10k_warn("Failed to set PSPOLL count: %d\n", ret); } + ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold); + if (ret) + ath10k_warn("failed to set rts threshold for vdev %d (%d)\n", + arvif->vdev_id, ret); + + ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold); + if (ret) + ath10k_warn("failed to set frag threshold for vdev %d (%d)\n", + arvif->vdev_id, ret); + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ar->monitor_present = true; @@ -2164,6 +2340,8 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, arg.ssids[i].len = req->ssids[i].ssid_len; arg.ssids[i].ssid = req->ssids[i].ssid; } + } else { + arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; } if (req->n_channels) { @@ -2363,6 +2541,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, u32 value = 0; int ret = 0; + lockdep_assert_held(&ar->conf_mutex); + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) return 0; @@ -2558,11 +2738,16 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); u32 rts = ar_iter->ar->hw->wiphy->rts_threshold; - rts = min_t(u32, rts, ATH10K_RTS_MAX); + lockdep_assert_held(&arvif->ar->conf_mutex); - ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, - WMI_VDEV_PARAM_RTS_THRESHOLD, - rts); + /* During HW reconfiguration mac80211 reports all interfaces that were + * running until reconfiguration was started. Since FW doesn't have any + * vdevs at this point we must not iterate over this interface list. + * This setting will be updated upon add_interface(). */ + if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) + return; + + ar_iter->ret = ath10k_mac_set_rts(arvif, rts); if (ar_iter->ret) ath10k_warn("Failed to set RTS threshold for VDEV: %d\n", arvif->vdev_id); @@ -2581,8 +2766,9 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) ar_iter.ar = ar; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath10k_set_rts_iter, &ar_iter); + ieee80211_iterate_active_interfaces_atomic( + hw, IEEE80211_IFACE_ITER_NORMAL, + ath10k_set_rts_iter, &ar_iter); mutex_unlock(&ar->conf_mutex); return ar_iter.ret; @@ -2593,17 +2779,17 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) struct ath10k_generic_iter *ar_iter = data; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); u32 frag = ar_iter->ar->hw->wiphy->frag_threshold; - int ret; - frag = clamp_t(u32, frag, - ATH10K_FRAGMT_THRESHOLD_MIN, - ATH10K_FRAGMT_THRESHOLD_MAX); + lockdep_assert_held(&arvif->ar->conf_mutex); - ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, - WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, - frag); + /* During HW reconfiguration mac80211 reports all interfaces that were + * running until reconfiguration was started. Since FW doesn't have any + * vdevs at this point we must not iterate over this interface list. + * This setting will be updated upon add_interface(). */ + if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) + return; - ar_iter->ret = ret; + ar_iter->ret = ath10k_mac_set_frag(arvif, frag); if (ar_iter->ret) ath10k_warn("Failed to set frag threshold for VDEV: %d\n", arvif->vdev_id); @@ -2622,8 +2808,9 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) ar_iter.ar = ar; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath10k_set_frag_iter, &ar_iter); + ieee80211_iterate_active_interfaces_atomic( + hw, IEEE80211_IFACE_ITER_NORMAL, + ath10k_set_frag_iter, &ar_iter); mutex_unlock(&ar->conf_mutex); return ar_iter.ret; @@ -2632,6 +2819,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct ath10k *ar = hw->priv; + bool skip; int ret; /* mac80211 doesn't care if we really xmit queued frames or not @@ -2639,16 +2827,29 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) if (drop) return; - ret = wait_event_timeout(ar->htt->empty_tx_wq, ({ + mutex_lock(&ar->conf_mutex); + + if (ar->state == ATH10K_STATE_WEDGED) + goto skip; + + ret = wait_event_timeout(ar->htt.empty_tx_wq, ({ bool empty; - spin_lock_bh(&ar->htt->tx_lock); - empty = bitmap_empty(ar->htt->used_msdu_ids, - ar->htt->max_num_pending_tx); - spin_unlock_bh(&ar->htt->tx_lock); - (empty); + + spin_lock_bh(&ar->htt.tx_lock); + empty = bitmap_empty(ar->htt.used_msdu_ids, + ar->htt.max_num_pending_tx); + spin_unlock_bh(&ar->htt.tx_lock); + + skip = (ar->state == ATH10K_STATE_WEDGED); + + (empty || skip); }), ATH10K_FLUSH_TIMEOUT_HZ); - if (ret <= 0) + + if (ret <= 0 || skip) ath10k_warn("tx not flushed\n"); + +skip: + mutex_unlock(&ar->conf_mutex); } /* TODO: Implement this function properly @@ -2660,6 +2861,118 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw) return 1; } +#ifdef CONFIG_PM +static int ath10k_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct ath10k *ar = hw->priv; + int ret; + + ar->is_target_paused = false; + + ret = ath10k_wmi_pdev_suspend_target(ar); + if (ret) { + ath10k_warn("could not suspend target (%d)\n", ret); + return 1; + } + + ret = wait_event_interruptible_timeout(ar->event_queue, + ar->is_target_paused == true, + 1 * HZ); + if (ret < 0) { + ath10k_warn("suspend interrupted (%d)\n", ret); + goto resume; + } else if (ret == 0) { + ath10k_warn("suspend timed out - target pause event never came\n"); + goto resume; + } + + ret = ath10k_hif_suspend(ar); + if (ret) { + ath10k_warn("could not suspend hif (%d)\n", ret); + goto resume; + } + + return 0; +resume: + ret = ath10k_wmi_pdev_resume_target(ar); + if (ret) + ath10k_warn("could not resume target (%d)\n", ret); + return 1; +} + +static int ath10k_resume(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + int ret; + + ret = ath10k_hif_resume(ar); + if (ret) { + ath10k_warn("could not resume hif (%d)\n", ret); + return 1; + } + + ret = ath10k_wmi_pdev_resume_target(ar); + if (ret) { + ath10k_warn("could not resume target (%d)\n", ret); + return 1; + } + + return 0; +} +#endif + +static void ath10k_restart_complete(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + /* If device failed to restart it will be in a different state, e.g. + * ATH10K_STATE_WEDGED */ + if (ar->state == ATH10K_STATE_RESTARTED) { + ath10k_info("device successfully recovered\n"); + ar->state = ATH10K_STATE_ON; + } + + mutex_unlock(&ar->conf_mutex); +} + +static int ath10k_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct ath10k *ar = hw->priv; + struct ieee80211_supported_band *sband; + struct survey_info *ar_survey = &ar->survey[idx]; + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } + + if (!sband) + sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; + + if (!sband || idx >= sband->n_channels) { + ret = -ENOENT; + goto exit; + } + + spin_lock_bh(&ar->data_lock); + memcpy(survey, ar_survey, sizeof(*survey)); + spin_unlock_bh(&ar->data_lock); + + survey->channel = &sband->channels[idx]; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + static const struct ieee80211_ops ath10k_ops = { .tx = ath10k_tx, .start = ath10k_start, @@ -2680,6 +2993,12 @@ static const struct ieee80211_ops ath10k_ops = { .set_frag_threshold = ath10k_set_frag_threshold, .flush = ath10k_flush, .tx_last_beacon = ath10k_tx_last_beacon, + .restart_complete = ath10k_restart_complete, + .get_survey = ath10k_get_survey, +#ifdef CONFIG_PM + .suspend = ath10k_suspend, + .resume = ath10k_resume, +#endif }; #define RATETAB_ENT(_rate, _rateid, _flags) { \ @@ -2797,9 +3116,15 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { .max = 8, .types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT) - | BIT(NL80211_IFTYPE_P2P_GO) - | BIT(NL80211_IFTYPE_AP) - } + }, + { + .max = 3, + .types = BIT(NL80211_IFTYPE_P2P_GO) + }, + { + .max = 7, + .types = BIT(NL80211_IFTYPE_AP) + }, }; static const struct ieee80211_iface_combination ath10k_if_comb = { @@ -2814,19 +3139,18 @@ static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) { struct ieee80211_sta_vht_cap vht_cap = {0}; u16 mcs_map; + int i; vht_cap.vht_supported = 1; vht_cap.cap = ar->vht_cap_info; - /* FIXME: check dynamically how many streams board supports */ - mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 14; + mcs_map = 0; + for (i = 0; i < 8; i++) { + if (i < ar->num_rf_chains) + mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i*2); + else + mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2); + } vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map); vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map); @@ -2889,7 +3213,7 @@ static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar) if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK) ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; - for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++) + for (i = 0; i < ar->num_rf_chains; i++) ht_cap.mcs.rx_mask[i] = 0xFF; ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; @@ -2948,8 +3272,10 @@ int ath10k_mac_register(struct ath10k *ar) channels = kmemdup(ath10k_2ghz_channels, sizeof(ath10k_2ghz_channels), GFP_KERNEL); - if (!channels) - return -ENOMEM; + if (!channels) { + ret = -ENOMEM; + goto err_free; + } band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels); @@ -2968,11 +3294,8 @@ int ath10k_mac_register(struct ath10k *ar) sizeof(ath10k_5ghz_channels), GFP_KERNEL); if (!channels) { - if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) { - band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; - kfree(band->channels); - } - return -ENOMEM; + ret = -ENOMEM; + goto err_free; } band = &ar->mac.sbands[IEEE80211_BAND_5GHZ]; @@ -3032,29 +3355,36 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->iface_combinations = &ath10k_if_comb; ar->hw->wiphy->n_iface_combinations = 1; + ar->hw->netdev_features = NETIF_F_HW_CSUM; + ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy, ath10k_reg_notifier); if (ret) { ath10k_err("Regulatory initialization failed\n"); - return ret; + goto err_free; } ret = ieee80211_register_hw(ar->hw); if (ret) { ath10k_err("ieee80211 registration failed: %d\n", ret); - return ret; + goto err_free; } if (!ath_is_world_regd(&ar->ath_common.regulatory)) { ret = regulatory_hint(ar->hw->wiphy, ar->ath_common.regulatory.alpha2); if (ret) - goto exit; + goto err_unregister; } return 0; -exit: + +err_unregister: ieee80211_unregister_hw(ar->hw); +err_free: + kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels); + return ret; } diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index 27fc92e..6fce9bf 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); void ath10k_reset_scan(unsigned long ptr); void ath10k_offchan_tx_purge(struct ath10k *ar); void ath10k_offchan_tx_work(struct work_struct *work); +void ath10k_halt(struct ath10k *ar); static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) { diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 33af467..e2f9ef5 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -32,7 +32,7 @@ #include "ce.h" #include "pci.h" -unsigned int ath10k_target_ps; +static unsigned int ath10k_target_ps; module_param(ath10k_target_ps, uint, 0644); MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option"); @@ -54,6 +54,10 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, int num); static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info); static void ath10k_pci_stop_ce(struct ath10k *ar); +static void ath10k_pci_device_reset(struct ath10k *ar); +static int ath10k_pci_reset_target(struct ath10k *ar); +static int ath10k_pci_start_intr(struct ath10k *ar); +static void ath10k_pci_stop_intr(struct ath10k *ar); static const struct ce_attr host_ce_config_wlan[] = { /* host->target HTC control and raw streams */ @@ -718,6 +722,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar) reg_dump_values[i + 1], reg_dump_values[i + 2], reg_dump_values[i + 3]); + + ieee80211_queue_work(ar->hw, &ar->restart_work); } static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, @@ -744,8 +750,8 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, ath10k_ce_per_engine_service(ar, pipe); } -static void ath10k_pci_hif_post_init(struct ath10k *ar, - struct ath10k_hif_cb *callbacks) +static void ath10k_pci_hif_set_callbacks(struct ath10k *ar, + struct ath10k_hif_cb *callbacks) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -1250,10 +1256,25 @@ static void ath10k_pci_ce_deinit(struct ath10k *ar) } } +static void ath10k_pci_disable_irqs(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int i; + + for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) + disable_irq(ar_pci->pdev->irq + i); +} + static void ath10k_pci_hif_stop(struct ath10k *ar) { + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + /* Irqs are never explicitly re-enabled. They are implicitly re-enabled + * by ath10k_pci_start_intr(). */ + ath10k_pci_disable_irqs(ar); + ath10k_pci_stop_ce(ar); /* At this point, asynchronous threads are stopped, the target should @@ -1263,7 +1284,8 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_pci_process_ce(ar); ath10k_pci_cleanup_ce(ar); ath10k_pci_buffer_cleanup(ar); - ath10k_pci_ce_deinit(ar); + + ar_pci->started = 0; } static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, @@ -1735,6 +1757,124 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) ath10k_pci_sleep(ar); } +static int ath10k_pci_hif_power_up(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ret = ath10k_pci_start_intr(ar); + if (ret) { + ath10k_err("could not start interrupt handling (%d)\n", ret); + goto err; + } + + /* + * Bring the target up cleanly. + * + * The target may be in an undefined state with an AUX-powered Target + * and a Host in WoW mode. If the Host crashes, loses power, or is + * restarted (without unloading the driver) then the Target is left + * (aux) powered and running. On a subsequent driver load, the Target + * is in an unexpected state. We try to catch that here in order to + * reset the Target and retry the probe. + */ + ath10k_pci_device_reset(ar); + + ret = ath10k_pci_reset_target(ar); + if (ret) + goto err_irq; + + if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) + /* Force AWAKE forever */ + ath10k_do_pci_wake(ar); + + ret = ath10k_pci_ce_init(ar); + if (ret) + goto err_ps; + + ret = ath10k_pci_init_config(ar); + if (ret) + goto err_ce; + + ret = ath10k_pci_wake_target_cpu(ar); + if (ret) { + ath10k_err("could not wake up target CPU (%d)\n", ret); + goto err_ce; + } + + return 0; + +err_ce: + ath10k_pci_ce_deinit(ar); +err_ps: + if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) + ath10k_do_pci_sleep(ar); +err_irq: + ath10k_pci_stop_intr(ar); +err: + return ret; +} + +static void ath10k_pci_hif_power_down(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + ath10k_pci_stop_intr(ar); + + ath10k_pci_ce_deinit(ar); + if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) + ath10k_do_pci_sleep(ar); +} + +#ifdef CONFIG_PM + +#define ATH10K_PCI_PM_CONTROL 0x44 + +static int ath10k_pci_hif_suspend(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct pci_dev *pdev = ar_pci->pdev; + u32 val; + + pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); + + if ((val & 0x000000ff) != 0x3) { + pci_save_state(pdev); + pci_disable_device(pdev); + pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, + (val & 0xffffff00) | 0x03); + } + + return 0; +} + +static int ath10k_pci_hif_resume(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct pci_dev *pdev = ar_pci->pdev; + u32 val; + + pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); + + if ((val & 0x000000ff) != 0) { + pci_restore_state(pdev); + pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, + val & 0xffffff00); + /* + * Suspend/Resume resets the PCI configuration space, + * so we have to re-disable the RETRY_TIMEOUT register (0x41) + * to keep PCI Tx retries from interfering with C3 CPU state + */ + pci_read_config_dword(pdev, 0x40, &val); + + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + } + + return 0; +} +#endif + static const struct ath10k_hif_ops ath10k_pci_hif_ops = { .send_head = ath10k_pci_hif_send_head, .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg, @@ -1743,8 +1883,14 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = { .map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe, .get_default_pipe = ath10k_pci_hif_get_default_pipe, .send_complete_check = ath10k_pci_hif_send_complete_check, - .init = ath10k_pci_hif_post_init, + .set_callbacks = ath10k_pci_hif_set_callbacks, .get_free_queue_number = ath10k_pci_hif_get_free_queue_number, + .power_up = ath10k_pci_hif_power_up, + .power_down = ath10k_pci_hif_power_down, +#ifdef CONFIG_PM + .suspend = ath10k_pci_hif_suspend, + .resume = ath10k_pci_hif_resume, +#endif }; static void ath10k_pci_ce_tasklet(unsigned long ptr) @@ -1872,8 +2018,13 @@ static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num) ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ath10k_pci_msi_fw_handler, IRQF_SHARED, "ath10k_pci", ar); - if (ret) + if (ret) { + ath10k_warn("request_irq(%d) failed %d\n", + ar_pci->pdev->irq + MSI_ASSIGN_FW, ret); + + pci_disable_msi(ar_pci->pdev); return ret; + } for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) { ret = request_irq(ar_pci->pdev->irq + i, @@ -2059,9 +2210,9 @@ static int ath10k_pci_reset_target(struct ath10k *ar) return 0; } -static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci) +static void ath10k_pci_device_reset(struct ath10k *ar) { - struct ath10k *ar = ar_pci->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); void __iomem *mem = ar_pci->mem; int i; u32 val; @@ -2118,9 +2269,12 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) case ATH10K_PCI_FEATURE_MSI_X: ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n"); break; - case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND: + case ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND: ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n"); break; + case ATH10K_PCI_FEATURE_SOC_POWER_SAVE: + ath10k_dbg(ATH10K_DBG_PCI, "QCA98XX SoC power save enabled\n"); + break; } } } @@ -2145,7 +2299,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, switch (pci_dev->device) { case QCA988X_1_0_DEVICE_ID: - set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features); + set_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features); break; case QCA988X_2_0_DEVICE_ID: set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features); @@ -2156,10 +2310,12 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_ar_pci; } + if (ath10k_target_ps) + set_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features); + ath10k_pci_dump_features(ar_pci); - ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI, - &ath10k_pci_hif_ops); + ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops); if (!ar) { ath10k_err("ath10k_core_create failed!\n"); ret = -EINVAL; @@ -2167,7 +2323,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, } /* Enable QCA988X_1.0 HW workarounds */ - if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) + if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) spin_lock_init(&ar_pci->hw_v1_workaround_lock); ar_pci->ar = ar; @@ -2241,62 +2397,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ar_pci->cacheline_sz = dma_get_cache_alignment(); - ret = ath10k_pci_start_intr(ar); - if (ret) { - ath10k_err("could not start interrupt handling (%d)\n", ret); - goto err_iomap; - } - - /* - * Bring the target up cleanly. - * - * The target may be in an undefined state with an AUX-powered Target - * and a Host in WoW mode. If the Host crashes, loses power, or is - * restarted (without unloading the driver) then the Target is left - * (aux) powered and running. On a subsequent driver load, the Target - * is in an unexpected state. We try to catch that here in order to - * reset the Target and retry the probe. - */ - ath10k_pci_device_reset(ar_pci); - - ret = ath10k_pci_reset_target(ar); - if (ret) - goto err_intr; - - if (ath10k_target_ps) { - ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n"); - } else { - /* Force AWAKE forever */ - ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n"); - ath10k_do_pci_wake(ar); - } - - ret = ath10k_pci_ce_init(ar); - if (ret) - goto err_intr; - - ret = ath10k_pci_init_config(ar); - if (ret) - goto err_ce; - - ret = ath10k_pci_wake_target_cpu(ar); - if (ret) { - ath10k_err("could not wake up target CPU (%d)\n", ret); - goto err_ce; - } - ret = ath10k_core_register(ar); if (ret) { ath10k_err("could not register driver core (%d)\n", ret); - goto err_ce; + goto err_iomap; } return 0; -err_ce: - ath10k_pci_ce_deinit(ar); -err_intr: - ath10k_pci_stop_intr(ar); err_iomap: pci_iounmap(pdev, mem); err_master: @@ -2333,7 +2441,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev) tasklet_kill(&ar_pci->msi_fw_err); ath10k_core_unregister(ar); - ath10k_pci_stop_intr(ar); pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, ar_pci->mem); @@ -2345,128 +2452,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev) kfree(ar_pci); } -#if defined(CONFIG_PM_SLEEP) - -#define ATH10K_PCI_PM_CONTROL 0x44 - -static int ath10k_pci_suspend(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct ath10k *ar = pci_get_drvdata(pdev); - struct ath10k_pci *ar_pci; - u32 val; - int ret, retval; - - ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); - - if (!ar) - return -ENODEV; - - ar_pci = ath10k_pci_priv(ar); - if (!ar_pci) - return -ENODEV; - - if (ath10k_core_target_suspend(ar)) - return -EBUSY; - - ret = wait_event_interruptible_timeout(ar->event_queue, - ar->is_target_paused == true, - 1 * HZ); - if (ret < 0) { - ath10k_warn("suspend interrupted (%d)\n", ret); - retval = ret; - goto resume; - } else if (ret == 0) { - ath10k_warn("suspend timed out - target pause event never came\n"); - retval = EIO; - goto resume; - } - - /* - * reset is_target_paused and host can check that in next time, - * or it will always be TRUE and host just skip the waiting - * condition, it causes target assert due to host already - * suspend - */ - ar->is_target_paused = false; - - pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); - - if ((val & 0x000000ff) != 0x3) { - pci_save_state(pdev); - pci_disable_device(pdev); - pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, - (val & 0xffffff00) | 0x03); - } - - return 0; -resume: - ret = ath10k_core_target_resume(ar); - if (ret) - ath10k_warn("could not resume (%d)\n", ret); - - return retval; -} - -static int ath10k_pci_resume(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct ath10k *ar = pci_get_drvdata(pdev); - struct ath10k_pci *ar_pci; - int ret; - u32 val; - - ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); - - if (!ar) - return -ENODEV; - ar_pci = ath10k_pci_priv(ar); - - if (!ar_pci) - return -ENODEV; - - ret = pci_enable_device(pdev); - if (ret) { - ath10k_warn("cannot enable PCI device: %d\n", ret); - return ret; - } - - pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); - - if ((val & 0x000000ff) != 0) { - pci_restore_state(pdev); - pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, - val & 0xffffff00); - /* - * Suspend/Resume resets the PCI configuration space, - * so we have to re-disable the RETRY_TIMEOUT register (0x41) - * to keep PCI Tx retries from interfering with C3 CPU state - */ - pci_read_config_dword(pdev, 0x40, &val); - - if ((val & 0x0000ff00) != 0) - pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); - } - - ret = ath10k_core_target_resume(ar); - if (ret) - ath10k_warn("target resume failed: %d\n", ret); - - return ret; -} - -static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops, - ath10k_pci_suspend, - ath10k_pci_resume); - -#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops) - -#else - -#define ATH10K_PCI_PM_OPS NULL - -#endif /* CONFIG_PM_SLEEP */ - MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table); static struct pci_driver ath10k_pci_driver = { @@ -2474,7 +2459,6 @@ static struct pci_driver ath10k_pci_driver = { .id_table = ath10k_pci_id_table, .probe = ath10k_pci_probe, .remove = ath10k_pci_remove, - .driver.pm = ATH10K_PCI_PM_OPS, }; static int __init ath10k_pci_init(void) diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index d2a055a..871bb33 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -152,7 +152,8 @@ struct service_to_pipe { enum ath10k_pci_features { ATH10K_PCI_FEATURE_MSI_X = 0, - ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND = 1, + ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND = 1, + ATH10K_PCI_FEATURE_SOC_POWER_SAVE = 2, /* keep last */ ATH10K_PCI_FEATURE_COUNT @@ -311,7 +312,7 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); void __iomem *addr = ar_pci->mem; - if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) { + if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) { unsigned long irq_flags; spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags); @@ -335,20 +336,22 @@ static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) return ioread32(ar_pci->mem + offset); } -extern unsigned int ath10k_target_ps; - void ath10k_do_pci_wake(struct ath10k *ar); void ath10k_do_pci_sleep(struct ath10k *ar); static inline void ath10k_pci_wake(struct ath10k *ar) { - if (ath10k_target_ps) + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) ath10k_do_pci_wake(ar); } static inline void ath10k_pci_sleep(struct ath10k *ar) { - if (ath10k_target_ps) + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) ath10k_do_pci_sleep(ar); } diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 7d4b798..55f90c7 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar) { int ret; + lockdep_assert_held(&ar->conf_mutex); + + if (ar->state == ATH10K_STATE_WEDGED) { + ath10k_warn("wmi flush skipped - device is wedged anyway\n"); + return; + } + ret = wait_event_timeout(ar->wmi.wq, atomic_read(&ar->wmi.pending_tx_count) == 0, 5*HZ); @@ -111,7 +118,7 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len); - status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb); + status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb); if (status) { dev_kfree_skb_any(skb); atomic_dec(&ar->wmi.pending_tx_count); @@ -383,9 +390,82 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) return 0; } +static int freq_to_idx(struct ath10k *ar, int freq) +{ + struct ieee80211_supported_band *sband; + int band, ch, idx = 0; + + for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { + sband = ar->hw->wiphy->bands[band]; + if (!sband) + continue; + + for (ch = 0; ch < sband->n_channels; ch++, idx++) + if (sband->channels[ch].center_freq == freq) + goto exit; + } + +exit: + return idx; +} + static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n"); + struct wmi_chan_info_event *ev; + struct survey_info *survey; + u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count; + int idx; + + ev = (struct wmi_chan_info_event *)skb->data; + + err_code = __le32_to_cpu(ev->err_code); + freq = __le32_to_cpu(ev->freq); + cmd_flags = __le32_to_cpu(ev->cmd_flags); + noise_floor = __le32_to_cpu(ev->noise_floor); + rx_clear_count = __le32_to_cpu(ev->rx_clear_count); + cycle_count = __le32_to_cpu(ev->cycle_count); + + ath10k_dbg(ATH10K_DBG_WMI, + "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n", + err_code, freq, cmd_flags, noise_floor, rx_clear_count, + cycle_count); + + spin_lock_bh(&ar->data_lock); + + if (!ar->scan.in_progress) { + ath10k_warn("chan info event without a scan request?\n"); + goto exit; + } + + idx = freq_to_idx(ar, freq); + if (idx >= ARRAY_SIZE(ar->survey)) { + ath10k_warn("chan info: invalid frequency %d (idx %d out of bounds)\n", + freq, idx); + goto exit; + } + + if (cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) { + /* During scanning chan info is reported twice for each + * visited channel. The reported cycle count is global + * and per-channel cycle count must be calculated */ + + cycle_count -= ar->survey_last_cycle_count; + rx_clear_count -= ar->survey_last_rx_clear_count; + + survey = &ar->survey[idx]; + survey->channel_time = WMI_CHAN_INFO_MSEC(cycle_count); + survey->channel_time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count); + survey->noise = noise_floor; + survey->filled = SURVEY_INFO_CHANNEL_TIME | + SURVEY_INFO_CHANNEL_TIME_RX | + SURVEY_INFO_NOISE_DBM; + } + + ar->survey_last_rx_clear_count = rx_clear_count; + ar->survey_last_cycle_count = cycle_count; + +exit: + spin_unlock_bh(&ar->data_lock); } static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) @@ -501,8 +581,8 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies, (u8 *)skb_tail_pointer(bcn) - ies); if (!ie) { - /* highly unlikely for mac80211 */ - ath10k_warn("no tim ie found;\n"); + if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS) + ath10k_warn("no tim ie found;\n"); return; } @@ -861,6 +941,13 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, (__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16; ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff); ar->phy_capability = __le32_to_cpu(ev->phy_capability); + ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains); + + if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) { + ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n", + ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM); + ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM; + } ar->ath_common.regulatory.current_rd = __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd); @@ -885,7 +972,7 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, } ath10k_dbg(ATH10K_DBG_WMI, - "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u\n", + "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n", __le32_to_cpu(ev->sw_version), __le32_to_cpu(ev->sw_version_1), __le32_to_cpu(ev->abi_version), @@ -894,7 +981,8 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, __le32_to_cpu(ev->vht_cap_info), __le32_to_cpu(ev->vht_supp_mcs), __le32_to_cpu(ev->sys_cap_info), - __le32_to_cpu(ev->num_mem_reqs)); + __le32_to_cpu(ev->num_mem_reqs), + __le32_to_cpu(ev->num_rf_chains)); complete(&ar->wmi.service_ready); } @@ -1114,7 +1202,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar) /* connect to control service */ conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; - status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp); + status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp); if (status) { ath10k_warn("failed to connect to WMI CONTROL service status: %d\n", status); @@ -1748,6 +1836,9 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, if (arg->key_data) memcpy(cmd->key_data, arg->key_data, arg->key_len); + ath10k_dbg(ATH10K_DBG_WMI, + "wmi vdev install key idx %d cipher %d len %d\n", + arg->key_idx, arg->key_cipher, arg->key_len); return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID); } @@ -2011,6 +2102,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, cmd->peer_vht_rates.tx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set); + ath10k_dbg(ATH10K_DBG_WMI, + "wmi peer assoc vdev %d addr %pM\n", + arg->vdev_id, arg->addr); return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID); } @@ -2079,3 +2173,22 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID); } + +int ath10k_wmi_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, u32 delay_ms) +{ + struct wmi_force_fw_hang_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_force_fw_hang_cmd *)skb->data; + cmd->type = __cpu_to_le32(type); + cmd->delay_ms = __cpu_to_le32(delay_ms); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n", + type, delay_ms); + return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID); +} diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 9555f5a..2c5a4f8 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -416,6 +416,7 @@ enum wmi_cmd_id { WMI_PDEV_FTM_INTG_CMDID, WMI_VDEV_SET_KEEPALIVE_CMDID, WMI_VDEV_GET_KEEPALIVE_CMDID, + WMI_FORCE_FW_HANG_CMDID, /* GPIO Configuration */ WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO), @@ -2930,6 +2931,11 @@ struct wmi_chan_info_event { __le32 cycle_count; } __packed; +#define WMI_CHAN_INFO_FLAG_COMPLETE BIT(0) + +/* FIXME: empirically extrapolated */ +#define WMI_CHAN_INFO_MSEC(x) ((x) / 76595) + /* Beacon filter wmi command info */ #define BCN_FLT_MAX_SUPPORTED_IES 256 #define BCN_FLT_MAX_ELEMS_IE_LIST (BCN_FLT_MAX_SUPPORTED_IES / 32) @@ -2972,6 +2978,22 @@ struct wmi_sta_keepalive_cmd { struct wmi_sta_keepalive_arp_resp arp_resp; } __packed; +enum wmi_force_fw_hang_type { + WMI_FORCE_FW_HANG_ASSERT = 1, + WMI_FORCE_FW_HANG_NO_DETECT, + WMI_FORCE_FW_HANG_CTRL_EP_FULL, + WMI_FORCE_FW_HANG_EMPTY_POINT, + WMI_FORCE_FW_HANG_STACK_OVERFLOW, + WMI_FORCE_FW_HANG_INFINITE_LOOP, +}; + +#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF + +struct wmi_force_fw_hang_cmd { + __le32 type; + __le32 delay_ms; +} __packed; + #define ATH10K_RTS_MAX 2347 #define ATH10K_FRAGMT_THRESHOLD_MIN 540 #define ATH10K_FRAGMT_THRESHOLD_MAX 2346 @@ -3048,5 +3070,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg); int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, const struct wmi_pdev_set_wmm_params_arg *arg); int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); +int ath10k_wmi_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, u32 delay_ms); #endif /* _WMI_H_ */ diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 2d691b8..74bd54d 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -29,6 +29,7 @@ #include <linux/average.h> #include <linux/leds.h> #include <net/mac80211.h> +#include <net/cfg80211.h> /* RX/TX descriptor hw structs * TODO: Driver part should only see sw structs */ diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index ce67ab79..48161ed 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -56,6 +56,7 @@ #include <linux/etherdevice.h> #include <linux/nl80211.h> +#include <net/cfg80211.h> #include <net/ieee80211_radiotap.h> #include <asm/unaligned.h> @@ -165,28 +166,36 @@ static const struct ieee80211_rate ath5k_rates[] = { .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, .hw_value = ATH5K_RATE_CODE_6M, - .flags = 0 }, + .flags = IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ }, { .bitrate = 90, .hw_value = ATH5K_RATE_CODE_9M, - .flags = 0 }, + .flags = IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ }, { .bitrate = 120, .hw_value = ATH5K_RATE_CODE_12M, - .flags = 0 }, + .flags = IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ }, { .bitrate = 180, .hw_value = ATH5K_RATE_CODE_18M, - .flags = 0 }, + .flags = IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ }, { .bitrate = 240, .hw_value = ATH5K_RATE_CODE_24M, - .flags = 0 }, + .flags = IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ }, { .bitrate = 360, .hw_value = ATH5K_RATE_CODE_36M, - .flags = 0 }, + .flags = IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ }, { .bitrate = 480, .hw_value = ATH5K_RATE_CODE_48M, - .flags = 0 }, + .flags = IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ }, { .bitrate = 540, .hw_value = ATH5K_RATE_CODE_54M, - .flags = 0 }, + .flags = IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ }, }; static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) @@ -435,11 +444,27 @@ ath5k_setup_bands(struct ieee80211_hw *hw) * Called with ah->lock. */ int -ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan) +ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef) { ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "channel set, resetting (%u -> %u MHz)\n", - ah->curchan->center_freq, chan->center_freq); + ah->curchan->center_freq, chandef->chan->center_freq); + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: + ah->ah_bwmode = AR5K_BWMODE_DEFAULT; + break; + case NL80211_CHAN_WIDTH_5: + ah->ah_bwmode = AR5K_BWMODE_5MHZ; + break; + case NL80211_CHAN_WIDTH_10: + ah->ah_bwmode = AR5K_BWMODE_10MHZ; + break; + default: + WARN_ON(1); + return -EINVAL; + } /* * To switch channels clear any pending DMA operations; @@ -447,7 +472,7 @@ ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan) * hardware at the new frequency, and then re-enable * the relevant bits of the h/w. */ - return ath5k_reset(ah, chan, true); + return ath5k_reset(ah, chandef->chan, true); } void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) @@ -1400,6 +1425,16 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, rxs->rate_idx = ath5k_hw_to_driver_rix(ah, rs->rs_rate); rxs->flag |= ath5k_rx_decrypted(ah, skb, rs); + switch (ah->ah_bwmode) { + case AR5K_BWMODE_5MHZ: + rxs->flag |= RX_FLAG_5MHZ; + break; + case AR5K_BWMODE_10MHZ: + rxs->flag |= RX_FLAG_10MHZ; + break; + default: + break; + } if (rxs->rate_idx >= 0 && rs->rs_rate == ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short) @@ -2507,6 +2542,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) /* SW support for IBSS_RSN is provided by mac80211 */ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; + /* both antennas can be configured as RX or TX */ hw->wiphy->available_antennas_tx = 0x3; hw->wiphy->available_antennas_rx = 0x3; diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index ca9a83c..97469d0 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -101,7 +101,7 @@ void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable); void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah, struct ieee80211_vif *vif); -int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan); +int ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef); void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf); void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf); void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index 9d00dab..b8d031a 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -245,9 +245,11 @@ static ssize_t write_file_beacon(struct file *file, struct ath5k_hw *ah = file->private_data; char buf[20]; - if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) + count = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, userbuf, count)) return -EFAULT; + buf[count] = '\0'; if (strncmp(buf, "disable", 7) == 0) { AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); pr_info("debugfs disable beacons\n"); @@ -345,9 +347,11 @@ static ssize_t write_file_debug(struct file *file, unsigned int i; char buf[20]; - if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) + count = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, userbuf, count)) return -EFAULT; + buf[count] = '\0'; for (i = 0; i < ARRAY_SIZE(dbg_info); i++) { if (strncmp(buf, dbg_info[i].name, strlen(dbg_info[i].name)) == 0) { @@ -448,9 +452,11 @@ static ssize_t write_file_antenna(struct file *file, unsigned int i; char buf[20]; - if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) + count = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, userbuf, count)) return -EFAULT; + buf[count] = '\0'; if (strncmp(buf, "diversity", 9) == 0) { ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT); pr_info("debug: enable diversity\n"); @@ -619,9 +625,11 @@ static ssize_t write_file_frameerrors(struct file *file, struct ath5k_statistics *st = &ah->stats; char buf[20]; - if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) + count = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, userbuf, count)) return -EFAULT; + buf[count] = '\0'; if (strncmp(buf, "clear", 5) == 0) { st->rxerr_crc = 0; st->rxerr_phy = 0; @@ -766,9 +774,11 @@ static ssize_t write_file_ani(struct file *file, struct ath5k_hw *ah = file->private_data; char buf[20]; - if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) + count = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, userbuf, count)) return -EFAULT; + buf[count] = '\0'; if (strncmp(buf, "sens-low", 8) == 0) { ath5k_ani_init(ah, ATH5K_ANI_MODE_MANUAL_HIGH); } else if (strncmp(buf, "sens-high", 9) == 0) { @@ -862,9 +872,11 @@ static ssize_t write_file_queue(struct file *file, struct ath5k_hw *ah = file->private_data; char buf[20]; - if (copy_from_user(buf, userbuf, min(count, sizeof(buf)))) + count = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, userbuf, count)) return -EFAULT; + buf[count] = '\0'; if (strncmp(buf, "start", 5) == 0) ieee80211_wake_queues(ah->hw); else if (strncmp(buf, "stop", 4) == 0) diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 40825d4..4ee01f6 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -202,7 +202,7 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&ah->lock); if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - ret = ath5k_chan_set(ah, conf->chandef.chan); + ret = ath5k_chan_set(ah, &conf->chandef); if (ret < 0) goto unlock; } diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 1f16b42..c60d36a 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -144,11 +144,13 @@ ath5k_hw_get_frame_duration(struct ath5k_hw *ah, enum ieee80211_band band, sifs = AR5K_INIT_SIFS_HALF_RATE; preamble *= 2; sym_time *= 2; + bitrate = DIV_ROUND_UP(bitrate, 2); break; case AR5K_BWMODE_5MHZ: sifs = AR5K_INIT_SIFS_QUARTER_RATE; preamble *= 4; sym_time *= 4; + bitrate = DIV_ROUND_UP(bitrate, 4); break; default: sifs = AR5K_INIT_SIFS_DEFAULT_BG; diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c index 65fe929..0583c69 100644 --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -566,9 +566,11 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) { struct ieee80211_channel *channel = ah->ah_current_channel; enum ieee80211_band band; + struct ieee80211_supported_band *sband; struct ieee80211_rate *rate; u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock; u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time); + u32 rate_flags, i; if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX) return -EINVAL; @@ -605,7 +607,28 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) else band = IEEE80211_BAND_2GHZ; - rate = &ah->sbands[band].bitrates[0]; + switch (ah->ah_bwmode) { + case AR5K_BWMODE_5MHZ: + rate_flags = IEEE80211_RATE_SUPPORTS_5MHZ; + break; + case AR5K_BWMODE_10MHZ: + rate_flags = IEEE80211_RATE_SUPPORTS_10MHZ; + break; + default: + rate_flags = 0; + break; + } + sband = &ah->sbands[band]; + rate = NULL; + for (i = 0; i < sband->n_bitrates; i++) { + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + rate = &sband->bitrates[i]; + break; + } + if (WARN_ON(!rate)) + return -EINVAL; + ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false); /* ack_tx_time includes an SIFS already */ diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 6a67881..4f316bd 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1836,6 +1836,9 @@ void ath6kl_stop_txrx(struct ath6kl *ar) clear_bit(WMI_READY, &ar->flag); + if (ar->fw_recovery.enable) + del_timer_sync(&ar->fw_recovery.hb_timer); + /* * After wmi_shudown all WMI events will be dropped. We * need to cleanup the buffers allocated in AP mode and diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index d4fcfca..5839fc2 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -29,6 +29,9 @@ struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr) struct ath6kl_sta *conn = NULL; u8 i, max_conn; + if (is_zero_ether_addr(node_addr)) + return NULL; + max_conn = (vif->nw_type == AP_NETWORK) ? AP_MAX_NUM_STA : 0; for (i = 0; i < max_conn; i++) { diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c index acc9aa8..d67170e 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.c +++ b/drivers/net/wireless/ath/ath6kl/testmode.c @@ -66,7 +66,8 @@ nla_put_failure: ath6kl_warn("nla_put failed on testmode rx skb!\n"); } -int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) +int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + void *data, int len) { struct ath6kl *ar = wiphy_priv(wiphy); struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; diff --git a/drivers/net/wireless/ath/ath6kl/testmode.h b/drivers/net/wireless/ath/ath6kl/testmode.h index fe651d6..9fbcdec3 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.h +++ b/drivers/net/wireless/ath/ath6kl/testmode.h @@ -20,7 +20,8 @@ #ifdef CONFIG_NL80211_TESTMODE void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len); -int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len); +int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + void *data, int len); #else @@ -29,7 +30,9 @@ static inline void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, { } -static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) +static inline int ath6kl_tm_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + void *data, int len) { return 0; } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 87aefb4..546d5da 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -568,8 +568,8 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, dlen, freq, vif->probe_req_report); if (vif->probe_req_report || vif->nw_type == AP_NETWORK) - cfg80211_rx_mgmt(&vif->wdev, freq, 0, - ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0, + GFP_ATOMIC); return 0; } @@ -608,8 +608,7 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); - cfg80211_rx_mgmt(&vif->wdev, freq, 0, - ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0, GFP_ATOMIC); return 0; } diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index d491a31..7944c25 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -56,7 +56,7 @@ config ATH9K_AHB config ATH9K_DEBUGFS bool "Atheros ath9k debugging" - depends on ATH9K + depends on ATH9K && DEBUG_FS select MAC80211_DEBUGFS select RELAY ---help--- @@ -96,6 +96,16 @@ config ATH9K_LEGACY_RATE_CONTROL has to be passed to mac80211 using the module parameter, ieee80211_default_rc_algo. +config ATH9K_RFKILL + bool "Atheros ath9k rfkill support" if EXPERT + depends on ATH9K + depends on RFKILL=y || RFKILL=ATH9K + default y + help + Say Y to have ath9k poll the RF-Kill GPIO every couple of + seconds. Turn off to save power, but enable it if you have + a platform that can toggle the RF-Kill GPIO. + config ATH9K_HTC tristate "Atheros HTC based wireless cards support" depends on USB && MAC80211 diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 4994bea..be466b0 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -319,9 +319,6 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) ah->ani_function = 0; } - /* always allow mode (on/off) to be controlled */ - ah->ani_function |= ATH9K_ANI_MODE; - ofdm_nil = max_t(int, ATH9K_ANI_OFDM_DEF_LEVEL, aniState->ofdmNoiseImmunityLevel); cck_nil = max_t(int, ATH9K_ANI_CCK_DEF_LEVEL, diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index b54a3fb..21e7b83 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -48,15 +48,10 @@ /* values here are relative to the INI */ enum ath9k_ani_cmd { - ATH9K_ANI_PRESENT = 0x1, - ATH9K_ANI_NOISE_IMMUNITY_LEVEL = 0x2, - ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION = 0x4, - ATH9K_ANI_CCK_WEAK_SIGNAL_THR = 0x8, - ATH9K_ANI_FIRSTEP_LEVEL = 0x10, - ATH9K_ANI_SPUR_IMMUNITY_LEVEL = 0x20, - ATH9K_ANI_MODE = 0x40, - ATH9K_ANI_PHYERR_RESET = 0x80, - ATH9K_ANI_MRC_CCK = 0x100, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION = 0x1, + ATH9K_ANI_FIRSTEP_LEVEL = 0x2, + ATH9K_ANI_SPUR_IMMUNITY_LEVEL = 0x4, + ATH9K_ANI_MRC_CCK = 0x8, ATH9K_ANI_ALL = 0xfff }; diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c index 664844c..dd1cc73 100644 --- a/drivers/net/wireless/ath/ath9k/antenna.c +++ b/drivers/net/wireless/ath/ath9k/antenna.c @@ -16,37 +16,119 @@ #include "ath9k.h" -static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, +/* + * AR9285 + * ====== + * + * EEPROM has 2 4-bit fields containing the card configuration. + * + * antdiv_ctl1: + * ------------ + * bb_enable_ant_div_lnadiv : 1 + * bb_ant_div_alt_gaintb : 1 + * bb_ant_div_main_gaintb : 1 + * bb_enable_ant_fast_div : 1 + * + * antdiv_ctl2: + * ----------- + * bb_ant_div_alt_lnaconf : 2 + * bb_ant_div_main_lnaconf : 2 + * + * The EEPROM bits are used as follows: + * ------------------------------------ + * + * bb_enable_ant_div_lnadiv - Enable LNA path rx antenna diversity/combining. + * Set in AR_PHY_MULTICHAIN_GAIN_CTL. + * + * bb_ant_div_[alt/main]_gaintb - 0 -> Antenna config Alt/Main uses gaintable 0 + * 1 -> Antenna config Alt/Main uses gaintable 1 + * Set in AR_PHY_MULTICHAIN_GAIN_CTL. + * + * bb_enable_ant_fast_div - Enable fast antenna diversity. + * Set in AR_PHY_CCK_DETECT. + * + * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config. + * Set in AR_PHY_MULTICHAIN_GAIN_CTL. + * 10=LNA1 + * 01=LNA2 + * 11=LNA1+LNA2 + * 00=LNA1-LNA2 + * + * AR9485 / AR9565 / AR9331 + * ======================== + * + * The same bits are present in the EEPROM, but the location in the + * EEPROM is different (ant_div_control in ar9300_BaseExtension_1). + * + * ant_div_alt_lnaconf ==> bit 0~1 + * ant_div_main_lnaconf ==> bit 2~3 + * ant_div_alt_gaintb ==> bit 4 + * ant_div_main_gaintb ==> bit 5 + * enable_ant_div_lnadiv ==> bit 6 + * enable_ant_fast_div ==> bit 7 + */ + +static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb, + int alt_ratio, int maxdelta, int mindelta, int main_rssi_avg, int alt_rssi_avg, int pkt_count) { - return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && - (alt_rssi_avg > main_rssi_avg + maxdelta)) || - (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50); + if (pkt_count <= 50) + return false; + + if (alt_rssi_avg > main_rssi_avg + mindelta) + return true; + + if (alt_ratio >= antcomb->ant_ratio2 && + alt_rssi_avg >= antcomb->low_rssi_thresh && + (alt_rssi_avg > main_rssi_avg + maxdelta)) + return true; + + return false; } -static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio, - int curr_main_set, int curr_alt_set, - int alt_rssi_avg, int main_rssi_avg) +static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf, + struct ath_ant_comb *antcomb, + int alt_ratio, int alt_rssi_avg, + int main_rssi_avg) { - bool result = false; - switch (div_group) { + bool result, set1, set2; + + result = set1 = set2 = false; + + if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 && + conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1) + set1 = true; + + if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 && + conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2) + set2 = true; + + switch (conf->div_group) { case 0: if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) result = true; break; case 1: case 2: - if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) && - (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) && - (alt_rssi_avg >= (main_rssi_avg - 5))) || - ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) && - (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) && - (alt_rssi_avg >= (main_rssi_avg - 2)))) && - (alt_rssi_avg >= 4)) + if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh) + break; + + if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) || + (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) || + (alt_ratio > antcomb->ant_ratio)) result = true; - else - result = false; + + break; + case 3: + if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh) + break; + + if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) || + (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) || + (alt_ratio > antcomb->ant_ratio)) + result = true; + break; } @@ -108,6 +190,74 @@ static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb, } } +static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb, + struct ath_hw_antcomb_conf *conf) +{ + /* set alt to the conf with maximun ratio */ + if (antcomb->first_ratio && antcomb->second_ratio) { + if (antcomb->rssi_second > antcomb->rssi_third) { + /* first alt*/ + if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || + (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2*/ + if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; + else + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + conf->alt_lna_conf = + antcomb->first_quick_scan_conf; + } else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || + (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) { + /* Set alt LNA1 or LNA2 */ + if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; + else + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; + } else { + /* Set alt to A+B or A-B */ + conf->alt_lna_conf = antcomb->second_quick_scan_conf; + } + } else if (antcomb->first_ratio) { + /* first alt */ + if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || + (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; + else + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + conf->alt_lna_conf = antcomb->first_quick_scan_conf; + } else if (antcomb->second_ratio) { + /* second alt */ + if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || + (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; + else + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + conf->alt_lna_conf = antcomb->second_quick_scan_conf; + } else { + /* main is largest */ + if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) || + (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; + else + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + conf->alt_lna_conf = antcomb->main_conf; + } +} + static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, struct ath_hw_antcomb_conf *div_ant_conf, int main_rssi_avg, int alt_rssi_avg, @@ -129,7 +279,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { /* main is LNA1 */ - if (ath_is_alt_ant_ratio_better(alt_ratio, + if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_HI, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, @@ -138,7 +288,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, else antcomb->first_ratio = false; } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { - if (ath_is_alt_ant_ratio_better(alt_ratio, + if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_MID, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, @@ -147,11 +297,11 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, else antcomb->first_ratio = false; } else { - if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && - (alt_rssi_avg > main_rssi_avg + - ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || - (alt_rssi_avg > main_rssi_avg)) && - (antcomb->total_pkt_count > 50)) + if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_HI, + 0, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) antcomb->first_ratio = true; else antcomb->first_ratio = false; @@ -164,17 +314,21 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, antcomb->rssi_first = main_rssi_avg; antcomb->rssi_third = alt_rssi_avg; - if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) + switch(antcomb->second_quick_scan_conf) { + case ATH_ANT_DIV_COMB_LNA1: antcomb->rssi_lna1 = alt_rssi_avg; - else if (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2) + break; + case ATH_ANT_DIV_COMB_LNA2: antcomb->rssi_lna2 = alt_rssi_avg; - else if (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) { + break; + case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) antcomb->rssi_lna2 = main_rssi_avg; else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) antcomb->rssi_lna1 = main_rssi_avg; + break; + default: + break; } if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + @@ -184,7 +338,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { - if (ath_is_alt_ant_ratio_better(alt_ratio, + if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_HI, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, @@ -193,7 +347,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, else antcomb->second_ratio = false; } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { - if (ath_is_alt_ant_ratio_better(alt_ratio, + if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, ATH_ANT_DIV_COMB_LNA1_DELTA_MID, ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, main_rssi_avg, alt_rssi_avg, @@ -202,105 +356,18 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, else antcomb->second_ratio = false; } else { - if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && - (alt_rssi_avg > main_rssi_avg + - ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || - (alt_rssi_avg > main_rssi_avg)) && - (antcomb->total_pkt_count > 50)) + if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_HI, + 0, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) antcomb->second_ratio = true; else antcomb->second_ratio = false; } - /* set alt to the conf with maximun ratio */ - if (antcomb->first_ratio && antcomb->second_ratio) { - if (antcomb->rssi_second > antcomb->rssi_third) { - /* first alt*/ - if ((antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2*/ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->first_quick_scan_conf; - } else if ((antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) { - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } else { - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->second_quick_scan_conf; - } - } else if (antcomb->first_ratio) { - /* first alt */ - if ((antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->first_quick_scan_conf; - } else if (antcomb->second_ratio) { - /* second alt */ - if ((antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->second_quick_scan_conf; - } else { - /* main is largest */ - if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) || - (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = antcomb->main_conf; - } + ath_ant_set_alt_ratio(antcomb, div_ant_conf); + break; default: break; @@ -430,8 +497,7 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x1; break; case 0x10: /* LNA2 A-B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; @@ -440,15 +506,13 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x1; break; case 0x13: /* LNA2 A+B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; break; case 0x20: /* LNA1 A-B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; @@ -457,8 +521,7 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x1; break; case 0x23: /* LNA1 A+B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; @@ -475,6 +538,9 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, default: break; } + + if (antcomb->fast_div_bias) + ant_conf->fast_div_bias = antcomb->fast_div_bias; } else if (ant_conf->div_group == 3) { switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) { @@ -540,6 +606,138 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, } } +static void ath_ant_try_scan(struct ath_ant_comb *antcomb, + struct ath_hw_antcomb_conf *conf, + int curr_alt_set, int alt_rssi_avg, + int main_rssi_avg) +{ + switch (curr_alt_set) { + case ATH_ANT_DIV_COMB_LNA2: + antcomb->rssi_lna2 = alt_rssi_avg; + antcomb->rssi_lna1 = main_rssi_avg; + antcomb->scan = true; + /* set to A+B */ + conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case ATH_ANT_DIV_COMB_LNA1: + antcomb->rssi_lna1 = alt_rssi_avg; + antcomb->rssi_lna2 = main_rssi_avg; + antcomb->scan = true; + /* set to A+B */ + conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: + antcomb->rssi_add = alt_rssi_avg; + antcomb->scan = true; + /* set to A-B */ + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + break; + case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: + antcomb->rssi_sub = alt_rssi_avg; + antcomb->scan = false; + if (antcomb->rssi_lna2 > + (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { + /* use LNA2 as main LNA */ + if ((antcomb->rssi_add > antcomb->rssi_lna1) && + (antcomb->rssi_add > antcomb->rssi_sub)) { + /* set to A+B */ + conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + } else if (antcomb->rssi_sub > + antcomb->rssi_lna1) { + /* set to A-B */ + conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + } else { + /* set to LNA1 */ + conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; + } + } else { + /* use LNA1 as main LNA */ + if ((antcomb->rssi_add > antcomb->rssi_lna2) && + (antcomb->rssi_add > antcomb->rssi_sub)) { + /* set to A+B */ + conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + } else if (antcomb->rssi_sub > + antcomb->rssi_lna1) { + /* set to A-B */ + conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + } else { + /* set to LNA2 */ + conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; + conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; + } + } + break; + default: + break; + } +} + +static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf, + struct ath_ant_comb *antcomb, + int alt_ratio, int alt_rssi_avg, + int main_rssi_avg, int curr_main_set, + int curr_alt_set) +{ + bool ret = false; + + if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio, + alt_rssi_avg, main_rssi_avg)) { + if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { + /* + * Switch main and alt LNA. + */ + div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; + div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; + } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { + div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; + div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; + } + + ret = true; + } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && + (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { + /* + Set alt to another LNA. + */ + if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; + else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) + div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; + + ret = true; + } + + return ret; +} + +static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb) +{ + int alt_ratio; + + if (!antcomb->scan || !antcomb->alt_good) + return false; + + if (time_after(jiffies, antcomb->scan_start_time + + msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) + return true; + + if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { + alt_ratio = ((antcomb->alt_recv_cnt * 100) / + antcomb->total_pkt_count); + if (alt_ratio < antcomb->ant_ratio) + return true; + } + + return false; +} + void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) { struct ath_hw_antcomb_conf div_ant_conf; @@ -549,41 +747,46 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) int main_rssi = rs->rs_rssi_ctl0; int alt_rssi = rs->rs_rssi_ctl1; int rx_ant_conf, main_ant_conf; - bool short_scan = false; + bool short_scan = false, ret; rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & ATH_ANT_RX_MASK; main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & ATH_ANT_RX_MASK; + if (alt_rssi >= antcomb->low_rssi_thresh) { + antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO; + antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2; + } else { + antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI; + antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI; + } + /* Record packet only when both main_rssi and alt_rssi is positive */ if (main_rssi > 0 && alt_rssi > 0) { antcomb->total_pkt_count++; antcomb->main_total_rssi += main_rssi; antcomb->alt_total_rssi += alt_rssi; + if (main_ant_conf == rx_ant_conf) antcomb->main_recv_cnt++; else antcomb->alt_recv_cnt++; } - /* Short scan check */ - if (antcomb->scan && antcomb->alt_good) { - if (time_after(jiffies, antcomb->scan_start_time + - msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) - short_scan = true; - else - if (antcomb->total_pkt_count == - ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { - alt_ratio = ((antcomb->alt_recv_cnt * 100) / - antcomb->total_pkt_count); - if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO) - short_scan = true; - } + if (main_ant_conf == rx_ant_conf) { + ANT_STAT_INC(ANT_MAIN, recv_cnt); + ANT_LNA_INC(ANT_MAIN, rx_ant_conf); + } else { + ANT_STAT_INC(ANT_ALT, recv_cnt); + ANT_LNA_INC(ANT_ALT, rx_ant_conf); } + /* Short scan check */ + short_scan = ath_ant_short_scan_check(antcomb); + if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || - rs->rs_moreaggr) && !short_scan) + rs->rs_moreaggr) && !short_scan) return; if (antcomb->total_pkt_count) { @@ -595,15 +798,13 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) antcomb->total_pkt_count); } - ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf); curr_alt_set = div_ant_conf.alt_lna_conf; curr_main_set = div_ant_conf.main_lna_conf; - antcomb->count++; if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { - if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { + if (alt_ratio > antcomb->ant_ratio) { ath_lnaconf_alt_good_scan(antcomb, div_ant_conf, main_rssi_avg); antcomb->alt_good = true; @@ -617,153 +818,47 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) } if (!antcomb->scan) { - if (ath_ant_div_comb_alt_check(div_ant_conf.div_group, - alt_ratio, curr_main_set, curr_alt_set, - alt_rssi_avg, main_rssi_avg)) { - if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { - /* Switch main and alt LNA */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } - - goto div_comb_done; - } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && - (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { - /* Set alt to another LNA */ - if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - - goto div_comb_done; - } - - if ((alt_rssi_avg < (main_rssi_avg + - div_ant_conf.lna1_lna2_delta))) + ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio, + alt_rssi_avg, main_rssi_avg, + curr_main_set, curr_alt_set); + if (ret) goto div_comb_done; } + if (!antcomb->scan && + (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta))) + goto div_comb_done; + if (!antcomb->scan_not_start) { - switch (curr_alt_set) { - case ATH_ANT_DIV_COMB_LNA2: - antcomb->rssi_lna2 = alt_rssi_avg; - antcomb->rssi_lna1 = main_rssi_avg; - antcomb->scan = true; - /* set to A+B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case ATH_ANT_DIV_COMB_LNA1: - antcomb->rssi_lna1 = alt_rssi_avg; - antcomb->rssi_lna2 = main_rssi_avg; - antcomb->scan = true; - /* set to A+B */ - div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: - antcomb->rssi_add = alt_rssi_avg; - antcomb->scan = true; - /* set to A-B */ - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - break; - case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: - antcomb->rssi_sub = alt_rssi_avg; - antcomb->scan = false; - if (antcomb->rssi_lna2 > - (antcomb->rssi_lna1 + - ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { - /* use LNA2 as main LNA */ - if ((antcomb->rssi_add > antcomb->rssi_lna1) && - (antcomb->rssi_add > antcomb->rssi_sub)) { - /* set to A+B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - } else if (antcomb->rssi_sub > - antcomb->rssi_lna1) { - /* set to A-B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - } else { - /* set to LNA1 */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - } - } else { - /* use LNA1 as main LNA */ - if ((antcomb->rssi_add > antcomb->rssi_lna2) && - (antcomb->rssi_add > antcomb->rssi_sub)) { - /* set to A+B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - } else if (antcomb->rssi_sub > - antcomb->rssi_lna1) { - /* set to A-B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - } else { - /* set to LNA2 */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } - } - break; - default: - break; - } + ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set, + alt_rssi_avg, main_rssi_avg); } else { if (!antcomb->alt_good) { antcomb->scan_not_start = false; /* Set alt to another LNA */ if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) { div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; + ATH_ANT_DIV_COMB_LNA2; div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; + ATH_ANT_DIV_COMB_LNA1; } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) { div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; + ATH_ANT_DIV_COMB_LNA1; div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; + ATH_ANT_DIV_COMB_LNA2; } goto div_comb_done; } + ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, + main_rssi_avg, alt_rssi_avg, + alt_ratio); + antcomb->quick_scan_cnt++; } - ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, - main_rssi_avg, alt_rssi_avg, - alt_ratio); - - antcomb->quick_scan_cnt++; - div_comb_done: ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio); ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf); + ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg); antcomb->scan_start_time = jiffies; antcomb->total_pkt_count = 0; @@ -772,26 +867,3 @@ div_comb_done: antcomb->main_recv_cnt = 0; antcomb->alt_recv_cnt = 0; } - -void ath_ant_comb_update(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - struct ath_hw_antcomb_conf div_ant_conf; - u8 lna_conf; - - ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); - - if (sc->ant_rx == 1) - lna_conf = ATH_ANT_DIV_COMB_LNA1; - else - lna_conf = ATH_ANT_DIV_COMB_LNA2; - - div_ant_conf.main_lna_conf = lna_conf; - div_ant_conf.alt_lna_conf = lna_conf; - - ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf); - - if (common->antenna_diversity) - ath9k_hw_antctrl_shared_chain_lnadiv(ah, true); -} diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 1576d58..0865647 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -1160,8 +1160,6 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, */ WARN_ON(1); break; - case ATH9K_ANI_PRESENT: - break; default: ath_dbg(common, ANI, "invalid cmd %u\n", cmd); return false; diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index 8dc2d08..fb61b08 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -269,13 +269,12 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah, if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE) val |= AR_WA_D3_L1_DISABLE; } else { - if (((AR_SREV_9285(ah) || - AR_SREV_9271(ah) || - AR_SREV_9287(ah)) && - (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)) || - (AR_SREV_9280(ah) && - (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE))) { - val |= AR_WA_D3_L1_DISABLE; + if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) { + if (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE) + val |= AR_WA_D3_L1_DISABLE; + } else if (AR_SREV_9280(ah)) { + if (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE) + val |= AR_WA_D3_L1_DISABLE; } } @@ -297,24 +296,18 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah, } else { if (ah->config.pcie_waen) { val = ah->config.pcie_waen; - if (!power_off) - val &= (~AR_WA_D3_L1_DISABLE); + val &= (~AR_WA_D3_L1_DISABLE); } else { - if (AR_SREV_9285(ah) || - AR_SREV_9271(ah) || - AR_SREV_9287(ah)) { + if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) { val = AR9285_WA_DEFAULT; - if (!power_off) - val &= (~AR_WA_D3_L1_DISABLE); - } - else if (AR_SREV_9280(ah)) { + val &= (~AR_WA_D3_L1_DISABLE); + } else if (AR_SREV_9280(ah)) { /* * For AR9280 chips, bit 22 of 0x4004 * needs to be set. */ val = AR9280_WA_DEFAULT; - if (!power_off) - val &= (~AR_WA_D3_L1_DISABLE); + val &= (~AR_WA_D3_L1_DISABLE); } else { val = AR_WA_DEFAULT; } diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index f400351..1fc1fa9 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -555,6 +555,69 @@ static void ar9002_hw_antdiv_comb_conf_set(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval); } +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + +static void ar9002_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) +{ + struct ath_btcoex_hw *btcoex = &ah->btcoex_hw; + u8 antdiv_ctrl1, antdiv_ctrl2; + u32 regval; + + if (enable) { + antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE; + antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE; + + /* + * Don't disable BT ant to allow BB to control SWCOM. + */ + btcoex->bt_coex_mode2 &= (~(AR_BT_DISABLE_BT_ANT)); + REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2); + + REG_WRITE(ah, AR_PHY_SWITCH_COM, ATH_BT_COEX_ANT_DIV_SWITCH_COM); + REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000); + } else { + /* + * Disable antenna diversity, use LNA1 only. + */ + antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A; + antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A; + + /* + * Disable BT Ant. to allow concurrent BT and WLAN receive. + */ + btcoex->bt_coex_mode2 |= AR_BT_DISABLE_BT_ANT; + REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2); + + /* + * Program SWCOM table to make sure RF switch always parks + * at BT side. + */ + REG_WRITE(ah, AR_PHY_SWITCH_COM, 0); + REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000); + } + + regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regval &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL)); + /* + * Clear ant_fast_div_bias [14:9] since for WB195, + * the main LNA is always LNA1. + */ + regval &= (~(AR_PHY_9285_FAST_DIV_BIAS)); + regval |= SM(antdiv_ctrl1, AR_PHY_9285_ANT_DIV_CTL); + regval |= SM(antdiv_ctrl2, AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regval |= SM((antdiv_ctrl2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regval |= SM((antdiv_ctrl1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB); + regval |= SM((antdiv_ctrl1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB); + REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval); + + regval = REG_READ(ah, AR_PHY_CCK_DETECT); + regval &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + regval |= SM((antdiv_ctrl1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); +} + +#endif + static void ar9002_hw_spectral_scan_config(struct ath_hw *ah, struct ath_spec_scan *param) { @@ -634,5 +697,9 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah) ops->spectral_scan_trigger = ar9002_hw_spectral_scan_trigger; ops->spectral_scan_wait = ar9002_hw_spectral_scan_wait; +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity; +#endif + ar9002_hw_set_nf_limits(ah); } diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.h b/drivers/net/wireless/ath/ath9k/ar9002_phy.h index f9eb2c3..6314ae2 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h @@ -317,13 +317,15 @@ #define AR_PHY_9285_ANT_DIV_ALT_GAINTB_S 29 #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB 0x40000000 #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB_S 30 -#define AR_PHY_9285_ANT_DIV_LNA1 2 -#define AR_PHY_9285_ANT_DIV_LNA2 1 -#define AR_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2 3 -#define AR_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0 #define AR_PHY_9285_ANT_DIV_GAINTB_0 0 #define AR_PHY_9285_ANT_DIV_GAINTB_1 1 +#define ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE 0x0b +#define ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE 0x09 +#define ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A 0x04 +#define ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A 0x09 +#define ATH_BT_COEX_ANT_DIV_SWITCH_COM 0x66666666 + #define AR_PHY_EXT_CCA0 0x99b8 #define AR_PHY_EXT_CCA0_THRESH62 0x000000FF #define AR_PHY_EXT_CCA0_THRESH62_S 0 diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index d105e43..f486480 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3541,13 +3541,12 @@ static u16 ar9003_switch_com_spdt_get(struct ath_hw *ah, bool is2ghz) return le16_to_cpu(ar9003_modal_header(ah, is2ghz)->switchcomspdt); } - -static u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz) +u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz) { return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon); } -static u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz) +u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz) { return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon2); } @@ -3561,6 +3560,7 @@ static u16 ar9003_hw_ant_ctrl_chain_get(struct ath_hw *ah, int chain, static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) { + struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_capabilities *pCap = &ah->caps; int chain; u32 regval, value, gpio; @@ -3614,6 +3614,11 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) } value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz); + if (AR_SREV_9485(ah) && common->bt_ant_diversity) { + value &= ~AR_SWITCH_TABLE_COM2_ALL; + value |= ah->config.ant_ctrl_comm2g_switch_enable; + + } REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value); if ((AR_SREV_9462(ah)) && (ah->rxchainmask == 0x2)) { @@ -3645,8 +3650,11 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) regval &= (~AR_PHY_ANT_DIV_LNADIV); regval |= ((value >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S; + if (AR_SREV_9485(ah) && common->bt_ant_diversity) + regval |= AR_ANT_DIV_ENABLE; + if (AR_SREV_9565(ah)) { - if (ah->shared_chain_lnadiv) { + if (common->bt_ant_diversity) { regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S); } else { regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S); @@ -3656,10 +3664,14 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); - /*enable fast_div */ + /* enable fast_div */ regval = REG_READ(ah, AR_PHY_CCK_DETECT); regval &= (~AR_FAST_DIV_ENABLE); regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S; + + if (AR_SREV_9485(ah) && common->bt_ant_diversity) + regval |= AR_FAST_DIV_ENABLE; + REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) { @@ -3673,9 +3685,9 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) AR_PHY_ANT_DIV_ALT_GAINTB | AR_PHY_ANT_DIV_MAIN_GAINTB)); /* by default use LNA1 for the main antenna */ - regval |= (AR_PHY_ANT_DIV_LNA1 << + regval |= (ATH_ANT_DIV_COMB_LNA1 << AR_PHY_ANT_DIV_MAIN_LNACONF_S); - regval |= (AR_PHY_ANT_DIV_LNA2 << + regval |= (ATH_ANT_DIV_COMB_LNA2 << AR_PHY_ANT_DIV_ALT_LNACONF_S); REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } @@ -3813,6 +3825,11 @@ static void ar9003_hw_atten_apply(struct ath_hw *ah, struct ath9k_channel *chan) else value = ar9003_hw_atten_chain_get_margin(ah, i, chan); + if (ah->config.alt_mingainidx) + REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_0, + AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, + value); + REG_RMW_FIELD(ah, ext_atten_reg[i], AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h index 874f657..75d4fb4 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h @@ -334,6 +334,8 @@ struct ar9300_eeprom { s32 ar9003_hw_get_tx_gain_idx(struct ath_hw *ah); s32 ar9003_hw_get_rx_gain_idx(struct ath_hw *ah); +u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz); +u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz); u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is_2ghz); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index d402cb3..608bb48 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -153,7 +153,7 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) if (!ah->is_clk_25mhz) INIT_INI_ARRAY(&ah->iniAdditional, ar9340_1p0_radio_core_40M); - } else if (AR_SREV_9485_11(ah)) { + } else if (AR_SREV_9485_11_OR_LATER(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9485_1_1_mac_core); @@ -424,7 +424,7 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah) else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_lowest_ob_db_tx_gain_table_1p0); - else if (AR_SREV_9485_11(ah)) + else if (AR_SREV_9485_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485_modes_lowest_ob_db_tx_gain_1_1); else if (AR_SREV_9550(ah)) @@ -458,7 +458,7 @@ static void ar9003_tx_gain_table_mode1(struct ath_hw *ah) else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_high_ob_db_tx_gain_table_1p0); - else if (AR_SREV_9485_11(ah)) + else if (AR_SREV_9485_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485Modes_high_ob_db_tx_gain_1_1); else if (AR_SREV_9580(ah)) @@ -492,7 +492,7 @@ static void ar9003_tx_gain_table_mode2(struct ath_hw *ah) else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_low_ob_db_tx_gain_table_1p0); - else if (AR_SREV_9485_11(ah)) + else if (AR_SREV_9485_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485Modes_low_ob_db_tx_gain_1_1); else if (AR_SREV_9580(ah)) @@ -517,7 +517,7 @@ static void ar9003_tx_gain_table_mode3(struct ath_hw *ah) else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_high_power_tx_gain_table_1p0); - else if (AR_SREV_9485_11(ah)) + else if (AR_SREV_9485_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485Modes_high_power_tx_gain_1_1); else if (AR_SREV_9580(ah)) @@ -552,7 +552,7 @@ static void ar9003_tx_gain_table_mode4(struct ath_hw *ah) static void ar9003_tx_gain_table_mode5(struct ath_hw *ah) { - if (AR_SREV_9485_11(ah)) + if (AR_SREV_9485_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485Modes_green_ob_db_tx_gain_1_1); else if (AR_SREV_9340(ah)) @@ -571,7 +571,7 @@ static void ar9003_tx_gain_table_mode6(struct ath_hw *ah) if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_low_ob_db_and_spur_tx_gain_table_1p0); - else if (AR_SREV_9485_11(ah)) + else if (AR_SREV_9485_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485Modes_green_spur_ob_db_tx_gain_1_1); else if (AR_SREV_9580(ah)) @@ -611,7 +611,7 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9340Common_rx_gain_table_1p0); - else if (AR_SREV_9485_11(ah)) + else if (AR_SREV_9485_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9485_common_rx_gain_1_1); else if (AR_SREV_9550(ah)) { @@ -644,7 +644,7 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah) else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9340Common_wo_xlna_rx_gain_table_1p0); - else if (AR_SREV_9485_11(ah)) + else if (AR_SREV_9485_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9485Common_wo_xlna_rx_gain_1_1); else if (AR_SREV_9462_21(ah)) @@ -745,16 +745,25 @@ static void ar9003_hw_init_mode_gain_regs(struct ath_hw *ah) static void ar9003_hw_configpcipowersave(struct ath_hw *ah, bool power_off) { + /* + * Increase L1 Entry Latency. Some WB222 boards don't have + * this change in eeprom/OTP. + * + */ + if (AR_SREV_9462(ah)) { + u32 val = ah->config.aspm_l1_fix; + if ((val & 0xff000000) == 0x17000000) { + val &= 0x00ffffff; + val |= 0x27000000; + REG_WRITE(ah, 0x570c, val); + } + } + /* Nothing to do on restore for 11N */ if (!power_off /* !restore */) { /* set bit 19 to allow forcing of pcie core into L1 state */ REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); - - /* Several PCIe massages to ensure proper behaviour */ - if (ah->config.pcie_waen) - REG_WRITE(ah, AR_WA, ah->config.pcie_waen); - else - REG_WRITE(ah, AR_WA, ah->WARegVal); + REG_WRITE(ah, AR_WA, ah->WARegVal); } /* diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 5163abd..f6c5c1b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -491,6 +491,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_rate = MS(rxsp->status1, AR_RxRate); rxs->rs_more = (rxsp->status2 & AR_RxMore) ? 1 : 0; + rxs->rs_firstaggr = (rxsp->status11 & AR_RxFirstAggr) ? 1 : 0; rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0; rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0; rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 1f694ab..e897648 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -632,6 +632,22 @@ static void ar9003_hw_override_ini(struct ath_hw *ah) REG_SET_BIT(ah, AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { + REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE, + AR_GLB_SWREG_DISCONT_EN_BT_WLAN); + + if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0, + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)) + ah->enabled_cals |= TX_IQ_CAL; + else + ah->enabled_cals &= ~TX_IQ_CAL; + + if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) + ah->enabled_cals |= TX_CL_CAL; + else + ah->enabled_cals &= ~TX_CL_CAL; + } } static void ar9003_hw_prog_ini(struct ath_hw *ah, @@ -814,29 +830,12 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, if (chan->channel == 2484) ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1); - if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) - REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE, - AR_GLB_SWREG_DISCONT_EN_BT_WLAN); - ah->modes_index = modesIndex; ar9003_hw_override_ini(ah); ar9003_hw_set_channel_regs(ah, chan); ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); ath9k_hw_apply_txpower(ah, chan, false); - if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { - if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0, - AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)) - ah->enabled_cals |= TX_IQ_CAL; - else - ah->enabled_cals &= ~TX_IQ_CAL; - - if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) - ah->enabled_cals |= TX_CL_CAL; - else - ah->enabled_cals &= ~TX_CL_CAL; - } - return 0; } @@ -1173,6 +1172,10 @@ skip_ws_det: * is_on == 0 means MRC CCK is OFF (more noise imm) */ bool is_on = param ? 1 : 0; + + if (ah->caps.rx_chainmask == 1) + break; + REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_ENABLE, is_on); REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, @@ -1190,8 +1193,6 @@ skip_ws_det: } break; } - case ATH9K_ANI_PRESENT: - break; default: ath_dbg(common, ANI, "invalid cmd %u\n", cmd); return false; @@ -1413,65 +1414,111 @@ static void ar9003_hw_antdiv_comb_conf_set(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } -static void ar9003_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah, - bool enable) +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + +static void ar9003_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) { + struct ath9k_hw_capabilities *pCap = &ah->caps; u8 ant_div_ctl1; u32 regval; - if (!AR_SREV_9565(ah)) + if (!AR_SREV_9485(ah) && !AR_SREV_9565(ah)) return; - ah->shared_chain_lnadiv = enable; + if (AR_SREV_9485(ah)) { + regval = ar9003_hw_ant_ctrl_common_2_get(ah, + IS_CHAN_2GHZ(ah->curchan)); + if (enable) { + regval &= ~AR_SWITCH_TABLE_COM2_ALL; + regval |= ah->config.ant_ctrl_comm2g_switch_enable; + } + REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, + AR_SWITCH_TABLE_COM2_ALL, regval); + } + ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); + /* + * Set MAIN/ALT LNA conf. + * Set MAIN/ALT gain_tb. + */ regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); regval &= (~AR_ANT_DIV_CTRL_ALL); regval |= (ant_div_ctl1 & 0x3f) << AR_ANT_DIV_CTRL_ALL_S; - regval &= ~AR_PHY_ANT_DIV_LNADIV; - regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S; - - if (enable) - regval |= AR_ANT_DIV_ENABLE; - REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); - regval = REG_READ(ah, AR_PHY_CCK_DETECT); - regval &= ~AR_FAST_DIV_ENABLE; - regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S; - - if (enable) - regval |= AR_FAST_DIV_ENABLE; - - REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); - - if (enable) { - REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL, - (1 << AR_PHY_ANT_SW_RX_PROT_S)); - if (ah->curchan && IS_CHAN_2GHZ(ah->curchan)) - REG_SET_BIT(ah, AR_PHY_RESTART, - AR_PHY_RESTART_ENABLE_DIV_M2FLAG); - REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, - AR_BTCOEX_WL_LNADIV_FORCE_ON); - } else { - REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE); - REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, - (1 << AR_PHY_ANT_SW_RX_PROT_S)); - REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE); - REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV, - AR_BTCOEX_WL_LNADIV_FORCE_ON); - + if (AR_SREV_9485_11_OR_LATER(ah)) { + /* + * Enable LNA diversity. + */ regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); - regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF | - AR_PHY_ANT_DIV_ALT_LNACONF | - AR_PHY_ANT_DIV_MAIN_GAINTB | - AR_PHY_ANT_DIV_ALT_GAINTB); - regval |= (AR_PHY_ANT_DIV_LNA1 << AR_PHY_ANT_DIV_MAIN_LNACONF_S); - regval |= (AR_PHY_ANT_DIV_LNA2 << AR_PHY_ANT_DIV_ALT_LNACONF_S); + regval &= ~AR_PHY_ANT_DIV_LNADIV; + regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S; + if (enable) + regval |= AR_ANT_DIV_ENABLE; + REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); + + /* + * Enable fast antenna diversity. + */ + regval = REG_READ(ah, AR_PHY_CCK_DETECT); + regval &= ~AR_FAST_DIV_ENABLE; + regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S; + if (enable) + regval |= AR_FAST_DIV_ENABLE; + + REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); + + if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) { + regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); + regval &= (~(AR_PHY_ANT_DIV_MAIN_LNACONF | + AR_PHY_ANT_DIV_ALT_LNACONF | + AR_PHY_ANT_DIV_ALT_GAINTB | + AR_PHY_ANT_DIV_MAIN_GAINTB)); + /* + * Set MAIN to LNA1 and ALT to LNA2 at the + * beginning. + */ + regval |= (ATH_ANT_DIV_COMB_LNA1 << + AR_PHY_ANT_DIV_MAIN_LNACONF_S); + regval |= (ATH_ANT_DIV_COMB_LNA2 << + AR_PHY_ANT_DIV_ALT_LNACONF_S); + REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); + } + } else if (AR_SREV_9565(ah)) { + if (enable) { + REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL, + (1 << AR_PHY_ANT_SW_RX_PROT_S)); + if (ah->curchan && IS_CHAN_2GHZ(ah->curchan)) + REG_SET_BIT(ah, AR_PHY_RESTART, + AR_PHY_RESTART_ENABLE_DIV_M2FLAG); + REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, + AR_BTCOEX_WL_LNADIV_FORCE_ON); + } else { + REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE); + REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, + (1 << AR_PHY_ANT_SW_RX_PROT_S)); + REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE); + REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV, + AR_BTCOEX_WL_LNADIV_FORCE_ON); + + regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); + regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF | + AR_PHY_ANT_DIV_ALT_LNACONF | + AR_PHY_ANT_DIV_MAIN_GAINTB | + AR_PHY_ANT_DIV_ALT_GAINTB); + regval |= (ATH_ANT_DIV_COMB_LNA1 << + AR_PHY_ANT_DIV_MAIN_LNACONF_S); + regval |= (ATH_ANT_DIV_COMB_LNA2 << + AR_PHY_ANT_DIV_ALT_LNACONF_S); + REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); + } } } +#endif + static int ar9003_hw_fast_chan_change(struct ath_hw *ah, struct ath9k_channel *chan, u8 *ini_reloaded) @@ -1518,6 +1565,18 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); + if (AR_SREV_9462_20_OR_LATER(ah)) { + /* + * CUS217 mix LNA mode. + */ + if (ar9003_hw_get_rx_gain_idx(ah) == 2) { + REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_core, + 1, regWrites); + REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_postamble, + modesIndex, regWrites); + } + } + /* * For 5GHz channels requiring Fast Clock, apply * different modal values. @@ -1528,7 +1587,11 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, if (AR_SREV_9565(ah)) REG_WRITE_ARRAY(&ah->iniModesFastClock, 1, regWrites); - REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites); + /* + * JAPAN regulatory. + */ + if (chan->channel == 2484) + ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1); ah->modes_index = modesIndex; *ini_reloaded = true; @@ -1631,11 +1694,14 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get; ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set; - ops->antctrl_shared_chain_lnadiv = ar9003_hw_antctrl_shared_chain_lnadiv; ops->spectral_scan_config = ar9003_hw_spectral_scan_config; ops->spectral_scan_trigger = ar9003_hw_spectral_scan_trigger; ops->spectral_scan_wait = ar9003_hw_spectral_scan_wait; +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity; +#endif + ar9003_hw_set_nf_limits(ah); ar9003_hw_set_radar_conf(ah); memcpy(ah->nf_regs, ar9300_cca_regs, sizeof(ah->nf_regs)); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index d4d39f3..6fd7523 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -148,6 +148,8 @@ #define AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28 #define AR_PHY_EXT_CCA_THRESH62 0x007F0000 #define AR_PHY_EXT_CCA_THRESH62_S 16 +#define AR_PHY_EXTCHN_PWRTHR1_ANT_DIV_ALT_ANT_MINGAINIDX 0x0000FF00 +#define AR_PHY_EXTCHN_PWRTHR1_ANT_DIV_ALT_ANT_MINGAINIDX_S 8 #define AR_PHY_EXT_MINCCA_PWR 0x01FF0000 #define AR_PHY_EXT_MINCCA_PWR_S 16 #define AR_PHY_EXT_CYCPWR_THR1 0x0000FE00L @@ -296,11 +298,6 @@ #define AR_PHY_ANT_DIV_MAIN_GAINTB 0x40000000 #define AR_PHY_ANT_DIV_MAIN_GAINTB_S 30 -#define AR_PHY_ANT_DIV_LNA1_MINUS_LNA2 0x0 -#define AR_PHY_ANT_DIV_LNA2 0x1 -#define AR_PHY_ANT_DIV_LNA1 0x2 -#define AR_PHY_ANT_DIV_LNA1_PLUS_LNA2 0x3 - #define AR_PHY_EXTCHN_PWRTHR1 (AR_AGC_BASE + 0x2c) #define AR_PHY_EXT_CHN_WIN (AR_AGC_BASE + 0x30) #define AR_PHY_20_40_DET_THR (AR_AGC_BASE + 0x34) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index c1224b5..2ee35f6 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -72,17 +72,12 @@ struct ath_config { /*************************/ #define ATH_TXBUF_RESET(_bf) do { \ - (_bf)->bf_stale = false; \ (_bf)->bf_lastbf = NULL; \ (_bf)->bf_next = NULL; \ memset(&((_bf)->bf_state), 0, \ sizeof(struct ath_buf_state)); \ } while (0) -#define ATH_RXBUF_RESET(_bf) do { \ - (_bf)->bf_stale = false; \ - } while (0) - /** * enum buffer_type - Buffer type flags * @@ -137,7 +132,8 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_AGGR_ENCRYPTDELIM 10 /* minimum h/w qdepth to be sustained to maximize aggregation */ #define ATH_AGGR_MIN_QDEPTH 2 -#define ATH_AMPDU_SUBFRAME_DEFAULT 32 +/* minimum h/w qdepth for non-aggregated traffic */ +#define ATH_NON_AGGR_MIN_QDEPTH 8 #define IEEE80211_SEQ_SEQ_SHIFT 4 #define IEEE80211_SEQ_MAX 4096 @@ -174,12 +170,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_TX_COMPLETE_POLL_INT 1000 -enum ATH_AGGR_STATUS { - ATH_AGGR_DONE, - ATH_AGGR_BAW_CLOSED, - ATH_AGGR_LIMITED, -}; - #define ATH_TXFIFO_DEPTH 8 struct ath_txq { int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */ @@ -201,10 +191,10 @@ struct ath_txq { struct ath_atx_ac { struct ath_txq *txq; - int sched; struct list_head list; struct list_head tid_q; bool clear_ps_filter; + bool sched; }; struct ath_frame_info { @@ -212,14 +202,16 @@ struct ath_frame_info { int framelen; enum ath9k_key_type keytype; u8 keyix; - u8 retries; u8 rtscts_rate; + u8 retries : 7; + u8 baw_tracked : 1; }; struct ath_buf_state { u8 bf_type; u8 bfs_paprd; u8 ndelim; + bool stale; u16 seqno; unsigned long bfs_paprd_timestamp; }; @@ -233,7 +225,6 @@ struct ath_buf { void *bf_desc; /* virtual addr of desc */ dma_addr_t bf_daddr; /* physical addr of desc */ dma_addr_t bf_buf_addr; /* physical addr of data buffer, for DMA */ - bool bf_stale; struct ieee80211_tx_rate rates[4]; struct ath_buf_state bf_state; }; @@ -241,16 +232,18 @@ struct ath_buf { struct ath_atx_tid { struct list_head list; struct sk_buff_head buf_q; + struct sk_buff_head retry_q; struct ath_node *an; struct ath_atx_ac *ac; unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)]; - int bar_index; u16 seq_start; u16 seq_next; u16 baw_size; - int tidno; + u8 tidno; int baw_head; /* first un-acked tx buffer */ int baw_tail; /* next unused tx buffer slot */ + + s8 bar_index; bool sched; bool paused; bool active; @@ -262,16 +255,13 @@ struct ath_node { struct ieee80211_vif *vif; /* interface with which we're associated */ struct ath_atx_tid tid[IEEE80211_NUM_TIDS]; struct ath_atx_ac ac[IEEE80211_NUM_ACS]; - int ps_key; u16 maxampdu; u8 mpdudensity; + s8 ps_key; bool sleeping; - -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) - struct dentry *node_stat; -#endif + bool no_ps_filter; }; struct ath_tx_control { @@ -317,6 +307,7 @@ struct ath_rx { struct ath_descdma rxdma; struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; + struct ath_buf *buf_hold; struct sk_buff *frag; u32 ampdu_ref; @@ -367,6 +358,7 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, /********/ struct ath_vif { + struct ath_node mcast_node; int av_bslot; bool primary_sta_vif; __le64 tsf_adjust; /* TSF adjustment for staggered beacons */ @@ -428,6 +420,7 @@ void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_set_beacon(struct ath_softc *sc); +bool ath9k_csa_is_finished(struct ath_softc *sc); /*******************/ /* Link Monitoring */ @@ -585,19 +578,14 @@ static inline void ath_fill_led_pin(struct ath_softc *sc) #define ATH_ANT_DIV_COMB_MAX_COUNT 100 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20 +#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI 50 +#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI 50 #define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1 #define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4 #define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2 #define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2 -enum ath9k_ant_div_comb_lna_conf { - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2, - ATH_ANT_DIV_COMB_LNA2, - ATH_ANT_DIV_COMB_LNA1, - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2, -}; - struct ath_ant_comb { u16 count; u16 total_pkt_count; @@ -614,27 +602,36 @@ struct ath_ant_comb { int rssi_first; int rssi_second; int rssi_third; + int ant_ratio; + int ant_ratio2; bool alt_good; int quick_scan_cnt; - int main_conf; + enum ath9k_ant_div_comb_lna_conf main_conf; enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf; enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf; bool first_ratio; bool second_ratio; unsigned long scan_start_time; + + /* + * Card-specific config values. + */ + int low_rssi_thresh; + int fast_div_bias; }; void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); -void ath_ant_comb_update(struct ath_softc *sc); /********************/ /* Main driver core */ /********************/ -#define ATH9K_PCI_CUS198 0x0001 -#define ATH9K_PCI_CUS230 0x0002 -#define ATH9K_PCI_CUS217 0x0004 -#define ATH9K_PCI_WOW 0x0008 +#define ATH9K_PCI_CUS198 0x0001 +#define ATH9K_PCI_CUS230 0x0002 +#define ATH9K_PCI_CUS217 0x0004 +#define ATH9K_PCI_WOW 0x0008 +#define ATH9K_PCI_BT_ANT_DIV 0x0010 +#define ATH9K_PCI_D3_L1_WAR 0x0020 /* * Default cache line size, in bytes. @@ -761,6 +758,7 @@ struct ath_softc { #endif struct ath_descdma txsdma; + struct ieee80211_vif *csa_vif; struct ath_ant_comb ant_comb; u8 ant_tx, ant_rx; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 1a17732..b5c16b3a 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -291,6 +291,23 @@ void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) (unsigned long long)tsfadjust, avp->av_bslot); } +bool ath9k_csa_is_finished(struct ath_softc *sc) +{ + struct ieee80211_vif *vif; + + vif = sc->csa_vif; + if (!vif || !vif->csa_active) + return false; + + if (!ieee80211_csa_is_complete(vif)) + return false; + + ieee80211_csa_finish(vif); + + sc->csa_vif = NULL; + return true; +} + void ath9k_beacon_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; @@ -336,6 +353,10 @@ void ath9k_beacon_tasklet(unsigned long data) return; } + /* EDMA devices check that in the tx completion function. */ + if (!edma && ath9k_csa_is_finished(sc)) + return; + slot = ath9k_beacon_choose_slot(sc); vif = sc->beacon.bslot[slot]; diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index 344fdde..d3063c2 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -49,37 +49,40 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb) } EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype); -static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) +static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef) { u32 chanmode = 0; - switch (chan->band) { + switch (chandef->chan->band) { case IEEE80211_BAND_2GHZ: - switch (channel_type) { - case NL80211_CHAN_NO_HT: - case NL80211_CHAN_HT20: + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: chanmode = CHANNEL_G_HT20; break; - case NL80211_CHAN_HT40PLUS: - chanmode = CHANNEL_G_HT40PLUS; + case NL80211_CHAN_WIDTH_40: + if (chandef->center_freq1 > chandef->chan->center_freq) + chanmode = CHANNEL_G_HT40PLUS; + else + chanmode = CHANNEL_G_HT40MINUS; break; - case NL80211_CHAN_HT40MINUS: - chanmode = CHANNEL_G_HT40MINUS; + default: break; } break; case IEEE80211_BAND_5GHZ: - switch (channel_type) { - case NL80211_CHAN_NO_HT: - case NL80211_CHAN_HT20: + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: chanmode = CHANNEL_A_HT20; break; - case NL80211_CHAN_HT40PLUS: - chanmode = CHANNEL_A_HT40PLUS; + case NL80211_CHAN_WIDTH_40: + if (chandef->center_freq1 > chandef->chan->center_freq) + chanmode = CHANNEL_A_HT40PLUS; + else + chanmode = CHANNEL_A_HT40MINUS; break; - case NL80211_CHAN_HT40MINUS: - chanmode = CHANNEL_A_HT40MINUS; + default: break; } break; @@ -94,13 +97,12 @@ static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan, * Update internal channel flags. */ void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) + struct cfg80211_chan_def *chandef) { - ichan->channel = chan->center_freq; - ichan->chan = chan; + ichan->channel = chandef->chan->center_freq; + ichan->chan = chandef->chan; - if (chan->band == IEEE80211_BAND_2GHZ) { + if (chandef->chan->band == IEEE80211_BAND_2GHZ) { ichan->chanmode = CHANNEL_G; ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM; } else { @@ -108,8 +110,22 @@ void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM; } - if (channel_type != NL80211_CHAN_NO_HT) - ichan->chanmode = ath9k_get_extchanmode(chan, channel_type); + switch (chandef->width) { + case NL80211_CHAN_WIDTH_5: + ichan->channelFlags |= CHANNEL_QUARTER; + break; + case NL80211_CHAN_WIDTH_10: + ichan->channelFlags |= CHANNEL_HALF; + break; + case NL80211_CHAN_WIDTH_20_NOHT: + break; + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_40: + ichan->chanmode = ath9k_get_extchanmode(chandef); + break; + default: + WARN_ON(1); + } } EXPORT_SYMBOL(ath9k_cmn_update_ichannel); @@ -125,8 +141,7 @@ struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, chan_idx = curchan->hw_value; channel = &ah->channels[chan_idx]; - ath9k_cmn_update_ichannel(channel, curchan, - cfg80211_get_chandef_type(&hw->conf.chandef)); + ath9k_cmn_update_ichannel(channel, &hw->conf.chandef); return channel; } diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index 207d069..e039bcb 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -44,8 +44,7 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb); void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type); + struct cfg80211_chan_def *chandef); struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, struct ath_hw *ah); int ath9k_cmn_count_streams(unsigned int chainmask, int max); diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 87454f6..c088744 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -88,90 +88,6 @@ static const struct file_operations fops_debug = { #define DMA_BUF_LEN 1024 -static ssize_t read_file_tx_chainmask(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_hw *ah = sc->sc_ah; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "0x%08x\n", ah->txchainmask); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t write_file_tx_chainmask(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_hw *ah = sc->sc_ah; - unsigned long mask; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &mask)) - return -EINVAL; - - ah->txchainmask = mask; - ah->caps.tx_chainmask = mask; - return count; -} - -static const struct file_operations fops_tx_chainmask = { - .read = read_file_tx_chainmask, - .write = write_file_tx_chainmask, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - - -static ssize_t read_file_rx_chainmask(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_hw *ah = sc->sc_ah; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "0x%08x\n", ah->rxchainmask); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t write_file_rx_chainmask(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_hw *ah = sc->sc_ah; - unsigned long mask; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &mask)) - return -EINVAL; - - ah->rxchainmask = mask; - ah->caps.rx_chainmask = mask; - return count; -} - -static const struct file_operations fops_rx_chainmask = { - .read = read_file_rx_chainmask, - .write = write_file_rx_chainmask, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; static ssize_t read_file_ani(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -270,25 +186,29 @@ static const struct file_operations fops_ani = { .llseek = default_llseek, }; -static ssize_t read_file_ant_diversity(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + +static ssize_t read_file_bt_ant_diversity(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); char buf[32]; unsigned int len; - len = sprintf(buf, "%d\n", common->antenna_diversity); + len = sprintf(buf, "%d\n", common->bt_ant_diversity); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } -static ssize_t write_file_ant_diversity(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t write_file_bt_ant_diversity(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - unsigned long antenna_diversity; + struct ath9k_hw_capabilities *pCap = &sc->sc_ah->caps; + unsigned long bt_ant_diversity; char buf[32]; ssize_t len; @@ -296,26 +216,147 @@ static ssize_t write_file_ant_diversity(struct file *file, if (copy_from_user(buf, user_buf, len)) return -EFAULT; - if (!AR_SREV_9565(sc->sc_ah)) + if (!(pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV)) goto exit; buf[len] = '\0'; - if (kstrtoul(buf, 0, &antenna_diversity)) + if (kstrtoul(buf, 0, &bt_ant_diversity)) return -EINVAL; - common->antenna_diversity = !!antenna_diversity; + common->bt_ant_diversity = !!bt_ant_diversity; ath9k_ps_wakeup(sc); - ath_ant_comb_update(sc); - ath_dbg(common, CONFIG, "Antenna diversity: %d\n", - common->antenna_diversity); + ath9k_hw_set_bt_ant_diversity(sc->sc_ah, common->bt_ant_diversity); + ath_dbg(common, CONFIG, "Enable WLAN/BT RX Antenna diversity: %d\n", + common->bt_ant_diversity); ath9k_ps_restore(sc); exit: return count; } -static const struct file_operations fops_ant_diversity = { - .read = read_file_ant_diversity, - .write = write_file_ant_diversity, +static const struct file_operations fops_bt_ant_diversity = { + .read = read_file_bt_ant_diversity, + .write = write_file_bt_ant_diversity, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +#endif + +void ath9k_debug_stat_ant(struct ath_softc *sc, + struct ath_hw_antcomb_conf *div_ant_conf, + int main_rssi_avg, int alt_rssi_avg) +{ + struct ath_antenna_stats *as_main = &sc->debug.stats.ant_stats[ANT_MAIN]; + struct ath_antenna_stats *as_alt = &sc->debug.stats.ant_stats[ANT_ALT]; + + as_main->lna_attempt_cnt[div_ant_conf->main_lna_conf]++; + as_alt->lna_attempt_cnt[div_ant_conf->alt_lna_conf]++; + + as_main->rssi_avg = main_rssi_avg; + as_alt->rssi_avg = alt_rssi_avg; +} + +static ssize_t read_file_antenna_diversity(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath_antenna_stats *as_main = &sc->debug.stats.ant_stats[ANT_MAIN]; + struct ath_antenna_stats *as_alt = &sc->debug.stats.ant_stats[ANT_ALT]; + struct ath_hw_antcomb_conf div_ant_conf; + unsigned int len = 0, size = 1024; + ssize_t retval = 0; + char *buf; + char *lna_conf_str[4] = {"LNA1_MINUS_LNA2", + "LNA2", + "LNA1", + "LNA1_PLUS_LNA2"}; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (!(pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)) { + len += snprintf(buf + len, size - len, "%s\n", + "Antenna Diversity Combining is disabled"); + goto exit; + } + + ath9k_ps_wakeup(sc); + ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); + len += snprintf(buf + len, size - len, "Current MAIN config : %s\n", + lna_conf_str[div_ant_conf.main_lna_conf]); + len += snprintf(buf + len, size - len, "Current ALT config : %s\n", + lna_conf_str[div_ant_conf.alt_lna_conf]); + len += snprintf(buf + len, size - len, "Average MAIN RSSI : %d\n", + as_main->rssi_avg); + len += snprintf(buf + len, size - len, "Average ALT RSSI : %d\n\n", + as_alt->rssi_avg); + ath9k_ps_restore(sc); + + len += snprintf(buf + len, size - len, "Packet Receive Cnt:\n"); + len += snprintf(buf + len, size - len, "-------------------\n"); + + len += snprintf(buf + len, size - len, "%30s%15s\n", + "MAIN", "ALT"); + len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "TOTAL COUNT", + as_main->recv_cnt, + as_alt->recv_cnt); + len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1]); + len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA2", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2]); + len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 + LNA2", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); + len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 - LNA2", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); + + len += snprintf(buf + len, size - len, "\nLNA Config Attempts:\n"); + len += snprintf(buf + len, size - len, "--------------------\n"); + + len += snprintf(buf + len, size - len, "%30s%15s\n", + "MAIN", "ALT"); + len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1]); + len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA2", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2]); + len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 + LNA2", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); + len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 - LNA2", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); + +exit: + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_antenna_diversity = { + .read = read_file_antenna_diversity, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, @@ -607,6 +648,28 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, return retval; } +static ssize_t print_queue(struct ath_softc *sc, struct ath_txq *txq, + char *buf, ssize_t size) +{ + ssize_t len = 0; + + ath_txq_lock(sc, txq); + + len += snprintf(buf + len, size - len, "%s: %d ", + "qnum", txq->axq_qnum); + len += snprintf(buf + len, size - len, "%s: %2d ", + "qdepth", txq->axq_depth); + len += snprintf(buf + len, size - len, "%s: %2d ", + "ampdu-depth", txq->axq_ampdu_depth); + len += snprintf(buf + len, size - len, "%s: %3d ", + "pending", txq->pending_frames); + len += snprintf(buf + len, size - len, "%s: %d\n", + "stopped", txq->stopped); + + ath_txq_unlock(sc, txq); + return len; +} + static ssize_t read_file_queues(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -624,24 +687,13 @@ static ssize_t read_file_queues(struct file *file, char __user *user_buf, for (i = 0; i < IEEE80211_NUM_ACS; i++) { txq = sc->tx.txq_map[i]; - len += snprintf(buf + len, size - len, "(%s): ", qname[i]); - - ath_txq_lock(sc, txq); - - len += snprintf(buf + len, size - len, "%s: %d ", - "qnum", txq->axq_qnum); - len += snprintf(buf + len, size - len, "%s: %2d ", - "qdepth", txq->axq_depth); - len += snprintf(buf + len, size - len, "%s: %2d ", - "ampdu-depth", txq->axq_ampdu_depth); - len += snprintf(buf + len, size - len, "%s: %3d ", - "pending", txq->pending_frames); - len += snprintf(buf + len, size - len, "%s: %d\n", - "stopped", txq->stopped); - - ath_txq_unlock(sc, txq); + len += snprintf(buf + len, size - len, "(%s): ", qname[i]); + len += print_queue(sc, txq, buf + len, size - len); } + len += snprintf(buf + len, size - len, "(CAB): "); + len += print_queue(sc, sc->beacon.cabq, buf + len, size - len); + if (len > size) len = size; @@ -1589,17 +1641,7 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, struct dentry *dir) { struct ath_node *an = (struct ath_node *)sta->drv_priv; - an->node_stat = debugfs_create_file("node_stat", S_IRUGO, - dir, an, &fops_node_stat); -} - -void ath9k_sta_remove_debugfs(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct dentry *dir) -{ - struct ath_node *an = (struct ath_node *)sta->drv_priv; - debugfs_remove(an->node_stat); + debugfs_create_file("node_stat", S_IRUGO, dir, an, &fops_node_stat); } /* Ethtool support for get-stats */ @@ -1770,10 +1812,10 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_reset); debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_recv); - debugfs_create_file("rx_chainmask", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, &fops_rx_chainmask); - debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, &fops_tx_chainmask); + debugfs_create_u8("rx_chainmask", S_IRUSR, sc->debug.debugfs_phy, + &ah->rxchainmask); + debugfs_create_u8("tx_chainmask", S_IRUSR, sc->debug.debugfs_phy, + &ah->txchainmask); debugfs_create_file("ani", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_ani); debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, @@ -1814,9 +1856,11 @@ int ath9k_init_debug(struct ath_hw *ah) sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_val); - debugfs_create_file("diversity", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, &fops_ant_diversity); + debugfs_create_file("antenna_diversity", S_IRUSR, + sc->debug.debugfs_phy, sc, &fops_antenna_diversity); #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + debugfs_create_file("bt_ant_diversity", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, &fops_bt_ant_diversity); debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_btcoex); #endif diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index fc67919..6e1556f 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -28,9 +28,13 @@ struct fft_sample_tlv; #ifdef CONFIG_ATH9K_DEBUGFS #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++ #define RESET_STAT_INC(sc, type) sc->debug.stats.reset[type]++ +#define ANT_STAT_INC(i, c) sc->debug.stats.ant_stats[i].c++ +#define ANT_LNA_INC(i, c) sc->debug.stats.ant_stats[i].lna_recv_cnt[c]++; #else #define TX_STAT_INC(q, c) do { } while (0) #define RESET_STAT_INC(sc, type) do { } while (0) +#define ANT_STAT_INC(i, c) do { } while (0) +#define ANT_LNA_INC(i, c) do { } while (0) #endif enum ath_reset_type { @@ -243,11 +247,22 @@ struct ath_rx_stats { u32 rx_spectral; }; +#define ANT_MAIN 0 +#define ANT_ALT 1 + +struct ath_antenna_stats { + u32 recv_cnt; + u32 rssi_avg; + u32 lna_recv_cnt[4]; + u32 lna_attempt_cnt[4]; +}; + struct ath_stats { struct ath_interrupt_stats istats; struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; struct ath_rx_stats rxstats; struct ath_dfs_stats dfs_stats; + struct ath_antenna_stats ant_stats[2]; u32 reset[__RESET_TYPE_MAX]; }; @@ -277,14 +292,11 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); -void ath9k_sta_remove_debugfs(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct dentry *dir); - void ath_debug_send_fft_sample(struct ath_softc *sc, struct fft_sample_tlv *fft_sample); - +void ath9k_debug_stat_ant(struct ath_softc *sc, + struct ath_hw_antcomb_conf *div_ant_conf, + int main_rssi_avg, int alt_rssi_avg); #else #define RX_STAT_INC(c) /* NOP */ @@ -297,12 +309,10 @@ static inline int ath9k_init_debug(struct ath_hw *ah) static inline void ath9k_deinit_debug(struct ath_softc *sc) { } - static inline void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) { } - static inline void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, @@ -310,11 +320,16 @@ static inline void ath_debug_stat_tx(struct ath_softc *sc, unsigned int flags) { } - static inline void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) { } +static inline void ath9k_debug_stat_ant(struct ath_softc *sc, + struct ath_hw_antcomb_conf *div_ant_conf, + int main_rssi_avg, int alt_rssi_avg) +{ + +} #endif /* CONFIG_ATH9K_DEBUGFS */ diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index c2bfd748..9ea8e4b 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -812,6 +812,7 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah, static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { + struct ath9k_hw_capabilities *pCap = &ah->caps; struct modal_eep_4k_header *pModal; struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; struct base_eep_header_4k *pBase = &eep->baseEepHeader; @@ -858,6 +859,24 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal); regVal = REG_READ(ah, AR_PHY_CCK_DETECT); + + if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) { + /* + * If diversity combining is enabled, + * set MAIN to LNA1 and ALT to LNA2 initially. + */ + regVal = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | + AR_PHY_9285_ANT_DIV_ALT_LNACONF)); + + regVal |= (ATH_ANT_DIV_COMB_LNA1 << + AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S); + regVal |= (ATH_ANT_DIV_COMB_LNA2 << + AR_PHY_9285_ANT_DIV_ALT_LNACONF_S); + regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS)); + regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S); + REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal); + } } if (pModal->version >= 2) { diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 5205a36..6d5d716 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -115,10 +115,10 @@ static int hif_usb_send_regout(struct hif_device_usb *hif_dev, cmd->skb = skb; cmd->hif_dev = hif_dev; - usb_fill_bulk_urb(urb, hif_dev->udev, - usb_sndbulkpipe(hif_dev->udev, USB_REG_OUT_PIPE), + usb_fill_int_urb(urb, hif_dev->udev, + usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE), skb->data, skb->len, - hif_usb_regout_cb, cmd); + hif_usb_regout_cb, cmd, 1); usb_anchor_urb(urb, &hif_dev->regout_submitted); ret = usb_submit_urb(urb, GFP_KERNEL); @@ -723,11 +723,11 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb) return; } - usb_fill_bulk_urb(urb, hif_dev->udev, - usb_rcvbulkpipe(hif_dev->udev, + usb_fill_int_urb(urb, hif_dev->udev, + usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), nskb->data, MAX_REG_IN_BUF_SIZE, - ath9k_hif_usb_reg_in_cb, nskb); + ath9k_hif_usb_reg_in_cb, nskb, 1); } resubmit: @@ -909,11 +909,11 @@ static int ath9k_hif_usb_alloc_reg_in_urbs(struct hif_device_usb *hif_dev) goto err_skb; } - usb_fill_bulk_urb(urb, hif_dev->udev, - usb_rcvbulkpipe(hif_dev->udev, + usb_fill_int_urb(urb, hif_dev->udev, + usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), skb->data, MAX_REG_IN_BUF_SIZE, - ath9k_hif_usb_reg_in_cb, skb); + ath9k_hif_usb_reg_in_cb, skb, 1); /* Anchor URB */ usb_anchor_urb(urb, &hif_dev->reg_in_submitted); @@ -1031,9 +1031,7 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) { - struct usb_host_interface *alt = &hif_dev->interface->altsetting[0]; - struct usb_endpoint_descriptor *endp; - int ret, idx; + int ret; ret = ath9k_hif_usb_download_fw(hif_dev); if (ret) { @@ -1043,20 +1041,6 @@ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) return ret; } - /* On downloading the firmware to the target, the USB descriptor of EP4 - * is 'patched' to change the type of the endpoint to Bulk. This will - * bring down CPU usage during the scan period. - */ - for (idx = 0; idx < alt->desc.bNumEndpoints; idx++) { - endp = &alt->endpoint[idx].desc; - if ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT) { - endp->bmAttributes &= ~USB_ENDPOINT_XFERTYPE_MASK; - endp->bmAttributes |= USB_ENDPOINT_XFER_BULK; - endp->bInterval = 0; - } - } - /* Alloc URBs */ ret = ath9k_hif_usb_alloc_urbs(hif_dev); if (ret) { @@ -1268,7 +1252,7 @@ static void ath9k_hif_usb_reboot(struct usb_device *udev) if (!buf) return; - ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, USB_REG_OUT_PIPE), + ret = usb_interrupt_msg(udev, usb_sndintpipe(udev, USB_REG_OUT_PIPE), buf, 4, NULL, HZ); if (ret) dev_err(&udev->dev, "ath9k_htc: USB reboot failed\n"); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 5c1bec1..d442581 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1203,16 +1203,13 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || chip_reset) { struct ieee80211_channel *curchan = hw->conf.chandef.chan; - enum nl80211_channel_type channel_type = - cfg80211_get_chandef_type(&hw->conf.chandef); int pos = curchan->hw_value; ath_dbg(common, CONFIG, "Set channel: %d MHz\n", curchan->center_freq); ath9k_cmn_update_ichannel(&priv->ah->channels[pos], - hw->conf.chandef.chan, - channel_type); + &hw->conf.chandef); if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 14b7011..83f4927 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -78,13 +78,16 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); } -static inline void ath9k_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah, - bool enable) +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + +static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) { - if (ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv) - ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv(ah, enable); + if (ath9k_hw_ops(ah)->set_bt_ant_diversity) + ath9k_hw_ops(ah)->set_bt_ant_diversity(ah, enable); } +#endif + /* Private hardware call ops */ /* PHY ops */ diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 4ca0cb0..ecc6ec4 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -450,7 +450,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah) ah->config.ack_6mb = 0x0; ah->config.cwm_ignore_extcca = 0; ah->config.pcie_clock_req = 0; - ah->config.pcie_waen = 0; ah->config.analog_shiftreg = 1; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { @@ -575,18 +574,17 @@ static int __ath9k_hw_init(struct ath_hw *ah) * We need to do this to avoid RMW of this register. We cannot * read the reg when chip is asleep. */ - ah->WARegVal = REG_READ(ah, AR_WA); - ah->WARegVal |= (AR_WA_D3_L1_DISABLE | - AR_WA_ASPM_TIMER_BASED_DISABLE); + if (AR_SREV_9300_20_OR_LATER(ah)) { + ah->WARegVal = REG_READ(ah, AR_WA); + ah->WARegVal |= (AR_WA_D3_L1_DISABLE | + AR_WA_ASPM_TIMER_BASED_DISABLE); + } if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { ath_err(common, "Couldn't reset chip\n"); return -EIO; } - if (AR_SREV_9462(ah)) - ah->WARegVal &= ~AR_WA_D3_L1_DISABLE; - if (AR_SREV_9565(ah)) { ah->WARegVal |= AR_WA_BIT22; REG_WRITE(ah, AR_WA, ah->WARegVal); @@ -656,8 +654,6 @@ static int __ath9k_hw_init(struct ath_hw *ah) ath9k_hw_init_cal_settings(ah); ah->ani_function = ATH9K_ANI_ALL; - if (AR_SREV_9280_20_OR_LATER(ah) && !AR_SREV_9300_20_OR_LATER(ah)) - ah->ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL; if (!AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function &= ~ATH9K_ANI_MRC_CCK; @@ -1069,7 +1065,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) if (IS_CHAN_A_FAST_CLOCK(ah, chan)) tx_lat += 11; - sifstime *= 2; + sifstime = 32; ack_offset = 16; slottime = 13; } else if (IS_CHAN_QUARTER_RATE(chan)) { @@ -1079,7 +1075,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) if (IS_CHAN_A_FAST_CLOCK(ah, chan)) tx_lat += 22; - sifstime *= 4; + sifstime = 64; ack_offset = 32; slottime = 21; } else { @@ -1116,7 +1112,6 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) ctstimeout += 48 - sifstime - ah->slottime; } - ath9k_hw_set_sifs_time(ah, sifstime); ath9k_hw_setslottime(ah, slottime); ath9k_hw_set_ack_timeout(ah, acktimeout); @@ -1496,16 +1491,18 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_hw_capabilities *pCap = &ah->caps; + bool band_switch = false, mode_diff = false; + u8 ini_reloaded = 0; u32 qnum; int r; - bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); - bool band_switch, mode_diff; - u8 ini_reloaded; - band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) != - (ah->curchan->channelFlags & (CHANNEL_2GHZ | - CHANNEL_5GHZ)); - mode_diff = (chan->chanmode != ah->curchan->chanmode); + if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) { + u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ); + u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ); + band_switch = (cur != new); + mode_diff = (chan->chanmode != ah->curchan->chanmode); + } for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { if (ath9k_hw_numtxpending(ah, qnum)) { @@ -1520,11 +1517,12 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, return false; } - if (edma && (band_switch || mode_diff)) { + if (band_switch || mode_diff) { ath9k_hw_mark_phy_inactive(ah); udelay(5); - ath9k_hw_init_pll(ah, NULL); + if (band_switch) + ath9k_hw_init_pll(ah, chan); if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) { ath_err(common, "Failed to do fast channel change\n"); @@ -1541,22 +1539,21 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, } ath9k_hw_set_clockrate(ah); ath9k_hw_apply_txpower(ah, chan, false); - ath9k_hw_rfbus_done(ah); if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) ath9k_hw_set_delta_slope(ah, chan); ath9k_hw_spur_mitigate_freq(ah, chan); - if (edma && (band_switch || mode_diff)) { - ah->ah_flags |= AH_FASTCC; - if (band_switch || ini_reloaded) - ah->eep_ops->set_board_values(ah, chan); + if (band_switch || ini_reloaded) + ah->eep_ops->set_board_values(ah, chan); - ath9k_hw_init_bb(ah, chan); + ath9k_hw_init_bb(ah, chan); + ath9k_hw_rfbus_done(ah); - if (band_switch || ini_reloaded) - ath9k_hw_init_cal(ah, chan); + if (band_switch || ini_reloaded) { + ah->ah_flags |= AH_FASTCC; + ath9k_hw_init_cal(ah, chan); ah->ah_flags &= ~AH_FASTCC; } @@ -1778,16 +1775,11 @@ static void ath9k_hw_init_desc(struct ath_hw *ah) /* * Fast channel change: * (Change synthesizer based on channel freq without resetting chip) - * - * Don't do FCC when - * - Flag is not set - * - Chip is just coming out of full sleep - * - Channel to be set is same as current channel - * - Channel flags are different, (eg.,moving from 2GHz to 5GHz channel) */ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_hw_capabilities *pCap = &ah->caps; int ret; if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI) @@ -1806,9 +1798,21 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) (CHANNEL_HALF | CHANNEL_QUARTER)) goto fail; - if ((chan->channelFlags & CHANNEL_ALL) != - (ah->curchan->channelFlags & CHANNEL_ALL)) - goto fail; + /* + * If cross-band fcc is not supoprted, bail out if + * either channelFlags or chanmode differ. + * + * chanmode will be different if the HT operating mode + * changes because of CSA. + */ + if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) { + if ((chan->channelFlags & CHANNEL_ALL) != + (ah->curchan->channelFlags & CHANNEL_ALL)) + goto fail; + + if (chan->chanmode != ah->curchan->chanmode) + goto fail; + } if (!ath9k_hw_check_alive(ah)) goto fail; @@ -2047,7 +2051,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_apply_gpio_override(ah); - if (AR_SREV_9565(ah) && ah->shared_chain_lnadiv) + if (AR_SREV_9565(ah) && common->bt_ant_diversity) REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); return 0; @@ -2504,7 +2508,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) else pCap->rts_aggr_limit = (8 * 1024); -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) +#ifdef CONFIG_ATH9K_RFKILL ah->rfsilent = ah->eep_ops->get_eeprom(ah, EEP_RF_SILENT); if (ah->rfsilent & EEP_RFSILENT_ENABLED) { ah->rfkill_gpio = @@ -2550,34 +2554,28 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah)) pCap->hw_caps |= ATH9K_HW_CAP_SGI_20; - if (AR_SREV_9285(ah)) + if (AR_SREV_9285(ah)) { if (ah->eep_ops->get_eeprom(ah, EEP_MODAL_VER) >= 3) { ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); - if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1)) + if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1)) { pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB; + ath_info(common, "Enable LNA combining\n"); + } } + } + if (AR_SREV_9300_20_OR_LATER(ah)) { if (ah->eep_ops->get_eeprom(ah, EEP_CHAIN_MASK_REDUCE)) pCap->hw_caps |= ATH9K_HW_CAP_APM; } - if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) { ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); - /* - * enable the diversity-combining algorithm only when - * both enable_lna_div and enable_fast_div are set - * Table for Diversity - * ant_div_alt_lnaconf bit 0-1 - * ant_div_main_lnaconf bit 2-3 - * ant_div_alt_gaintb bit 4 - * ant_div_main_gaintb bit 5 - * enable_ant_div_lnadiv bit 6 - * enable_ant_fast_div bit 7 - */ - if ((ant_div_ctl1 >> 0x6) == 0x3) + if ((ant_div_ctl1 >> 0x6) == 0x3) { pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB; + ath_info(common, "Enable LNA combining\n"); + } } if (ath9k_hw_dfs_tested(ah)) @@ -2610,6 +2608,13 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) pCap->hw_caps |= ATH9K_HW_CAP_PAPRD; + /* + * Fast channel change across bands is available + * only for AR9462 and AR9565. + */ + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) + pCap->hw_caps |= ATH9K_HW_CAP_FCC_BAND_SWITCH; + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index cd74b3a..69a907b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -247,6 +247,8 @@ enum ath9k_hw_caps { ATH9K_HW_CAP_DFS = BIT(16), ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17), ATH9K_HW_CAP_PAPRD = BIT(18), + ATH9K_HW_CAP_FCC_BAND_SWITCH = BIT(19), + ATH9K_HW_CAP_BT_ANT_DIV = BIT(20), }; /* @@ -309,8 +311,11 @@ struct ath9k_ops_config { u16 ani_poll_interval; /* ANI poll interval in ms */ /* Platform specific config */ + u32 aspm_l1_fix; u32 xlna_gpio; + u32 ant_ctrl_comm2g_switch_enable; bool xatten_margin_cfg; + bool alt_mingainidx; }; enum ath9k_int { @@ -716,11 +721,14 @@ struct ath_hw_ops { struct ath_hw_antcomb_conf *antconf); void (*antdiv_comb_conf_set)(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf); - void (*antctrl_shared_chain_lnadiv)(struct ath_hw *hw, bool enable); void (*spectral_scan_config)(struct ath_hw *ah, struct ath_spec_scan *param); void (*spectral_scan_trigger)(struct ath_hw *ah); void (*spectral_scan_wait)(struct ath_hw *ah); + +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable); +#endif }; struct ath_nf_limits { @@ -765,7 +773,6 @@ struct ath_hw { bool aspm_enabled; bool is_monitoring; bool need_an_top2_fixup; - bool shared_chain_lnadiv; u16 tx_trig_level; u32 nf_regs[6]; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 026a2a0..9a1f349 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -53,9 +53,9 @@ static int ath9k_btcoex_enable; module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444); MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence"); -static int ath9k_enable_diversity; -module_param_named(enable_diversity, ath9k_enable_diversity, int, 0444); -MODULE_PARM_DESC(enable_diversity, "Enable Antenna diversity for AR9565"); +static int ath9k_bt_ant_diversity; +module_param_named(bt_ant_diversity, ath9k_bt_ant_diversity, int, 0444); +MODULE_PARM_DESC(bt_ant_diversity, "Enable WLAN/BT RX antenna diversity"); bool is_ath9k_unloaded; /* We use the hw_value as an index into our private channel structure */ @@ -146,14 +146,22 @@ static struct ieee80211_rate ath9k_legacy_rates[] = { RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(60, 0x0b, 0), - RATE(90, 0x0f, 0), - RATE(120, 0x0a, 0), - RATE(180, 0x0e, 0), - RATE(240, 0x09, 0), - RATE(360, 0x0d, 0), - RATE(480, 0x08, 0), - RATE(540, 0x0c, 0), + RATE(60, 0x0b, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(90, 0x0f, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(120, 0x0a, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(180, 0x0e, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(240, 0x09, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(360, 0x0d, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(480, 0x08, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(540, 0x0c, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), }; #ifdef CONFIG_MAC80211_LEDS @@ -516,6 +524,7 @@ static void ath9k_init_misc(struct ath_softc *sc) static void ath9k_init_platform(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_common *common = ath9k_hw_common(ah); if (common->bus_ops->ath_bus_type != ATH_PCI) @@ -525,12 +534,27 @@ static void ath9k_init_platform(struct ath_softc *sc) ATH9K_PCI_CUS230)) { ah->config.xlna_gpio = 9; ah->config.xatten_margin_cfg = true; + ah->config.alt_mingainidx = true; + ah->config.ant_ctrl_comm2g_switch_enable = 0x000BBB88; + sc->ant_comb.low_rssi_thresh = 20; + sc->ant_comb.fast_div_bias = 3; ath_info(common, "Set parameters for %s\n", (sc->driver_data & ATH9K_PCI_CUS198) ? "CUS198" : "CUS230"); - } else if (sc->driver_data & ATH9K_PCI_CUS217) { + } + + if (sc->driver_data & ATH9K_PCI_CUS217) ath_info(common, "CUS217 card detected\n"); + + if (sc->driver_data & ATH9K_PCI_BT_ANT_DIV) { + pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV; + ath_info(common, "Set BT/WLAN RX diversity capability\n"); + } + + if (sc->driver_data & ATH9K_PCI_D3_L1_WAR) { + ah->config.pcie_waen = 0x0040473b; + ath_info(common, "Enable WAR for ASPM D3/L1\n"); } } @@ -584,6 +608,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, { struct ath9k_platform_data *pdata = sc->dev->platform_data; struct ath_hw *ah = NULL; + struct ath9k_hw_capabilities *pCap; struct ath_common *common; int ret = 0, i; int csz = 0; @@ -600,6 +625,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ah->reg_ops.rmw = ath9k_reg_rmw; atomic_set(&ah->intr_ref_cnt, -1); sc->sc_ah = ah; + pCap = &ah->caps; sc->dfs_detector = dfs_pattern_detector_init(ah, NL80211_DFS_UNSET); @@ -631,11 +657,15 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath9k_init_platform(sc); /* - * Enable Antenna diversity only when BTCOEX is disabled - * and the user manually requests the feature. + * Enable WLAN/BT RX Antenna diversity only when: + * + * - BTCOEX is disabled. + * - the user manually requests the feature. + * - the HW cap is set using the platform data. */ - if (!common->btcoex_enabled && ath9k_enable_diversity) - common->antenna_diversity = 1; + if (!common->btcoex_enabled && ath9k_bt_ant_diversity && + (pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV)) + common->bt_ant_diversity = 1; spin_lock_init(&common->cc_lock); @@ -710,13 +740,15 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band) struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; struct ath_hw *ah = sc->sc_ah; + struct cfg80211_chan_def chandef; int i; sband = &sc->sbands[band]; for (i = 0; i < sband->n_channels; i++) { chan = &sband->channels[i]; ah->curchan = &ah->channels[chan->hw_value]; - ath9k_cmn_update_ichannel(ah->curchan, chan, NL80211_CHAN_HT20); + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); + ath9k_cmn_update_ichannel(ah->curchan, &chandef); ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true); } } @@ -835,6 +867,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; + hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; #ifdef CONFIG_PM_SLEEP if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) && diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index fff5d3c..2f831db 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -41,7 +41,7 @@ void ath_tx_complete_poll_work(struct work_struct *work) txq->axq_tx_inprogress = true; } } - ath_txq_unlock_complete(sc, txq); + ath_txq_unlock(sc, txq); } if (needreset) { diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 2ef05eb..a3eff09 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -583,9 +583,9 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_rate = MS(ads.ds_rxstatus0, AR_RxRate); rs->rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0; + rs->rs_firstaggr = (ads.ds_rxstatus8 & AR_RxFirstAggr) ? 1 : 0; rs->rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0; - rs->rs_moreaggr = - (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0; + rs->rs_moreaggr = (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0; rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna); /* directly mapped flags for ieee80211_rx_status */ diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index b02dfce..bfccace 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -140,6 +140,7 @@ struct ath_rx_status { int8_t rs_rssi_ext1; int8_t rs_rssi_ext2; u8 rs_isaggr; + u8 rs_firstaggr; u8 rs_moreaggr; u8 rs_num_delims; u8 rs_flags; @@ -569,6 +570,7 @@ struct ar5416_desc { #define AR_RxAggr 0x00020000 #define AR_PostDelimCRCErr 0x00040000 #define AR_RxStatusRsvd71 0x3ff80000 +#define AR_RxFirstAggr 0x20000000 #define AR_DecryptBusyErr 0x40000000 #define AR_KeyMiss 0x80000000 diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index cb5a655..e4f6590 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -237,9 +237,6 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath_restart_work(sc); } - if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) - ath_ant_comb_update(sc); - ieee80211_wake_queues(sc->hw); return true; @@ -965,6 +962,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_node *an = &avp->mcast_node; mutex_lock(&sc->mutex); @@ -978,6 +977,12 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); + an->sc = sc; + an->sta = NULL; + an->vif = vif; + an->no_ps_filter = true; + ath_tx_node_init(sc, an); + mutex_unlock(&sc->mutex); return 0; } @@ -1015,6 +1020,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp = (void *)vif->drv_priv; ath_dbg(common, CONFIG, "Detach Interface\n"); @@ -1025,10 +1031,15 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); + if (sc->csa_vif == vif) + sc->csa_vif = NULL; + ath9k_ps_wakeup(sc); ath9k_calculate_summary_state(hw, NULL); ath9k_ps_restore(sc); + ath_tx_node_cleanup(sc, &avp->mcast_node); + mutex_unlock(&sc->mutex); } @@ -1192,8 +1203,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { struct ieee80211_channel *curchan = hw->conf.chandef.chan; - enum nl80211_channel_type channel_type = - cfg80211_get_chandef_type(&conf->chandef); int pos = curchan->hw_value; int old_pos = -1; unsigned long flags; @@ -1201,8 +1210,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if (ah->curchan) old_pos = ah->curchan - &ah->channels[0]; - ath_dbg(common, CONFIG, "Set channel: %d MHz type: %d\n", - curchan->center_freq, channel_type); + ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", + curchan->center_freq, hw->conf.chandef.width); /* update survey stats for the old channel before switching */ spin_lock_irqsave(&common->cc_lock, flags); @@ -1210,7 +1219,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) spin_unlock_irqrestore(&common->cc_lock, flags); ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos], - curchan, channel_type); + &conf->chandef); /* * If the operating channel changes, change the survey in-use flags @@ -1373,9 +1382,6 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_node *an = (struct ath_node *) sta->drv_priv; - if (!sta->ht_cap.ht_supported) - return; - switch (cmd) { case STA_NOTIFY_SLEEP: an->sleeping = true; @@ -2093,7 +2099,7 @@ static void ath9k_wow_add_pattern(struct ath_softc *sc, { struct ath_hw *ah = sc->sc_ah; struct ath9k_wow_pattern *wow_pattern = NULL; - struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns; + struct cfg80211_pkt_pattern *patterns = wowlan->patterns; int mask_len; s8 i = 0; @@ -2314,6 +2320,19 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) clear_bit(SC_OP_SCANNING, &sc->sc_flags); } +static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct ath_softc *sc = hw->priv; + + /* mac80211 does not support CSA in multi-if cases (yet) */ + if (WARN_ON(sc->csa_vif)) + return; + + sc->csa_vif = vif; +} + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, @@ -2358,8 +2377,8 @@ struct ieee80211_ops ath9k_ops = { #if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) .sta_add_debugfs = ath9k_sta_add_debugfs, - .sta_remove_debugfs = ath9k_sta_remove_debugfs, #endif .sw_scan_start = ath9k_sw_scan_start, .sw_scan_complete = ath9k_sw_scan_complete, + .channel_switch_beacon = ath9k_channel_switch_beacon, }; diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index c585c9b..d089a7c 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -29,6 +29,60 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = { { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ + + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + PCI_VENDOR_ID_AZWAVE, + 0x1C71), + .driver_data = ATH9K_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + PCI_VENDOR_ID_FOXCONN, + 0xE01F), + .driver_data = ATH9K_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x11AD, /* LITEON */ + 0x6632), + .driver_data = ATH9K_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x11AD, /* LITEON */ + 0x6642), + .driver_data = ATH9K_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + PCI_VENDOR_ID_QMI, + 0x0306), + .driver_data = ATH9K_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x185F, /* WNC */ + 0x309D), + .driver_data = ATH9K_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x10CF, /* Fujitsu */ + 0x147C), + .driver_data = ATH9K_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x10CF, /* Fujitsu */ + 0x147D), + .driver_data = ATH9K_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x10CF, /* Fujitsu */ + 0x1536), + .driver_data = ATH9K_PCI_D3_L1_WAR }, + + /* AR9285 card for Asus */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002B, + PCI_VENDOR_ID_AZWAVE, + 0x2C37), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */ { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI */ @@ -40,29 +94,106 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = { 0x0032, PCI_VENDOR_ID_AZWAVE, 0x2086), - .driver_data = ATH9K_PCI_CUS198 }, + .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0032, PCI_VENDOR_ID_AZWAVE, 0x1237), - .driver_data = ATH9K_PCI_CUS198 }, + .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0032, PCI_VENDOR_ID_AZWAVE, 0x2126), - .driver_data = ATH9K_PCI_CUS198 }, + .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x126A), + .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV }, /* PCI-E CUS230 */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0032, PCI_VENDOR_ID_AZWAVE, 0x2152), - .driver_data = ATH9K_PCI_CUS230 }, + .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0032, PCI_VENDOR_ID_FOXCONN, 0xE075), - .driver_data = ATH9K_PCI_CUS230 }, + .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV }, + + /* WB225 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_ATHEROS, + 0x3119), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_ATHEROS, + 0x3122), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x185F, /* WNC */ + 0x3119), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x185F, /* WNC */ + 0x3027), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x4105), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x4106), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x410D), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x410E), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x410F), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0xC706), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0xC680), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0xC708), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_LENOVO, + 0x3218), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_LENOVO, + 0x3219), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */ { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */ @@ -229,6 +360,22 @@ static void ath_pci_aspm_init(struct ath_common *common) return; } + /* + * 0x70c - Ack Frequency Register. + * + * Bits 27:29 - DEFAULT_L1_ENTRANCE_LATENCY. + * + * 000 : 1 us + * 001 : 2 us + * 010 : 4 us + * 011 : 8 us + * 100 : 16 us + * 101 : 32 us + * 110/111 : 64 us + */ + if (AR_SREV_9462(ah)) + pci_read_config_dword(pdev, 0x70c, &ah->config.aspm_l1_fix); + pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm); if (aspm & (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1)) { ah->aspm_enabled = true; diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h index 8b38030..4a1b992 100644 --- a/drivers/net/wireless/ath/ath9k/phy.h +++ b/drivers/net/wireless/ath/ath9k/phy.h @@ -48,4 +48,11 @@ #define AR_PHY_PLL_CONTROL 0x16180 #define AR_PHY_PLL_MODE 0x16184 +enum ath9k_ant_div_comb_lna_conf { + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2, + ATH_ANT_DIV_COMB_LNA2, + ATH_ANT_DIV_COMB_LNA1, + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2, +}; + #endif diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 7eb1f4b..d3d7c51 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1275,15 +1275,21 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, } static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta) { struct ath_softc *sc = priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_rate_priv *ath_rc_priv = priv_sta; int i, j = 0; + u32 rate_flags = ieee80211_chandef_rate_flags(&sc->hw->conf.chandef); for (i = 0; i < sband->n_bitrates; i++) { if (sta->supp_rates[sband->band] & BIT(i)) { + if ((rate_flags & sband->bitrates[i].flags) + != rate_flags) + continue; + ath_rc_priv->neg_rates.rs_rates[j] = (sband->bitrates[i].bitrate * 2) / 10; j++; @@ -1313,6 +1319,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, } static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta, u32 changed) { @@ -1324,8 +1331,8 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, ath_rc_init(sc, priv_sta); ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG, - "Operating HT Bandwidth changed to: %d\n", - cfg80211_get_chandef_type(&sc->hw->conf.chandef)); + "Operating Bandwidth changed to: %d\n", + sc->hw->conf.chandef.width); } } diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 865e043..4ee472a 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -42,8 +42,6 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) struct ath_desc *ds; struct sk_buff *skb; - ATH_RXBUF_RESET(bf); - ds = bf->bf_desc; ds->ds_link = 0; /* link to null */ ds->ds_data = bf->bf_buf_addr; @@ -70,6 +68,14 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) sc->rx.rxlink = &ds->ds_link; } +static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf) +{ + if (sc->rx.buf_hold) + ath_rx_buf_link(sc, sc->rx.buf_hold); + + sc->rx.buf_hold = bf; +} + static void ath_setdefantenna(struct ath_softc *sc, u32 antenna) { /* XXX block beacon interrupts */ @@ -117,7 +123,6 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc, skb = bf->bf_mpdu; - ATH_RXBUF_RESET(bf); memset(skb->data, 0, ah->caps.rx_status_len); dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, ah->caps.rx_status_len, DMA_TO_DEVICE); @@ -185,7 +190,7 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc) static void ath_rx_edma_init_queue(struct ath_rx_edma *rx_edma, int size) { - skb_queue_head_init(&rx_edma->rx_fifo); + __skb_queue_head_init(&rx_edma->rx_fifo); rx_edma->rx_fifo_hwsize = size; } @@ -432,6 +437,7 @@ int ath_startrecv(struct ath_softc *sc) if (list_empty(&sc->rx.rxbuf)) goto start_recv; + sc->rx.buf_hold = NULL; sc->rx.rxlink = NULL; list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) { ath_rx_buf_link(sc, bf); @@ -677,6 +683,9 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, } bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); + if (bf == sc->rx.buf_hold) + return NULL; + ds = bf->bf_desc; /* @@ -755,7 +764,6 @@ static bool ath9k_rx_accept(struct ath_common *common, bool is_mc, is_valid_tkip, strip_mic, mic_error; struct ath_hw *ah = common->ah; __le16 fc; - u8 rx_status_len = ah->caps.rx_status_len; fc = hdr->frame_control; @@ -777,25 +785,6 @@ static bool ath9k_rx_accept(struct ath_common *common, !test_bit(rx_stats->rs_keyix, common->ccmp_keymap)) rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS; - if (!rx_stats->rs_datalen) { - RX_STAT_INC(rx_len_err); - return false; - } - - /* - * rs_status follows rs_datalen so if rs_datalen is too large - * we can take a hint that hardware corrupted it, so ignore - * those frames. - */ - if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len)) { - RX_STAT_INC(rx_len_err); - return false; - } - - /* Only use error bits from the last fragment */ - if (rx_stats->rs_more) - return true; - mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) && !ieee80211_has_morefrags(fc) && !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) && @@ -814,8 +803,6 @@ static bool ath9k_rx_accept(struct ath_common *common, rxs->flag |= RX_FLAG_FAILED_FCS_CRC; mic_error = false; } - if (rx_stats->rs_status & ATH9K_RXERR_PHY) - return false; if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) || (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) { @@ -865,6 +852,17 @@ static int ath9k_process_rate(struct ath_common *common, band = hw->conf.chandef.chan->band; sband = hw->wiphy->bands[band]; + switch (hw->conf.chandef.width) { + case NL80211_CHAN_WIDTH_5: + rxs->flag |= RX_FLAG_5MHZ; + break; + case NL80211_CHAN_WIDTH_10: + rxs->flag |= RX_FLAG_10MHZ; + break; + default: + break; + } + if (rx_stats->rs_rate & 0x80) { /* HT rate */ rxs->flag |= RX_FLAG_HT; @@ -898,129 +896,65 @@ static int ath9k_process_rate(struct ath_common *common, static void ath9k_process_rssi(struct ath_common *common, struct ieee80211_hw *hw, - struct ieee80211_hdr *hdr, - struct ath_rx_status *rx_stats) + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = common->ah; int last_rssi; int rssi = rx_stats->rs_rssi; - if (!rx_stats->is_mybeacon || - ((ah->opmode != NL80211_IFTYPE_STATION) && - (ah->opmode != NL80211_IFTYPE_ADHOC))) + /* + * RSSI is not available for subframes in an A-MPDU. + */ + if (rx_stats->rs_moreaggr) { + rxs->flag |= RX_FLAG_NO_SIGNAL_VAL; return; - - if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && !rx_stats->rs_moreaggr) - ATH_RSSI_LPF(sc->last_rssi, rx_stats->rs_rssi); - - last_rssi = sc->last_rssi; - if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) - rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER); - if (rssi < 0) - rssi = 0; - - /* Update Beacon RSSI, this is used by ANI. */ - ah->stats.avgbrssi = rssi; -} - -/* - * For Decrypt or Demic errors, we only mark packet status here and always push - * up the frame up to let mac80211 handle the actual error case, be it no - * decryption key or real decryption error. This let us keep statistics there. - */ -static int ath9k_rx_skb_preprocess(struct ath_softc *sc, - struct ieee80211_hdr *hdr, - struct ath_rx_status *rx_stats, - struct ieee80211_rx_status *rx_status, - bool *decrypt_error) -{ - struct ieee80211_hw *hw = sc->hw; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - bool discard_current = sc->rx.discard_next; - - sc->rx.discard_next = rx_stats->rs_more; - if (discard_current) - return -EINVAL; + } /* - * everything but the rate is checked here, the rate check is done - * separately to avoid doing two lookups for a rate for each frame. + * Check if the RSSI for the last subframe in an A-MPDU + * or an unaggregated frame is valid. */ - if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) - return -EINVAL; - - /* Only use status info from the last fragment */ - if (rx_stats->rs_more) - return 0; + if (rx_stats->rs_rssi == ATH9K_RSSI_BAD) { + rxs->flag |= RX_FLAG_NO_SIGNAL_VAL; + return; + } - if (ath9k_process_rate(common, hw, rx_stats, rx_status)) - return -EINVAL; + /* + * Update Beacon RSSI, this is used by ANI. + */ + if (rx_stats->is_mybeacon && + ((ah->opmode == NL80211_IFTYPE_STATION) || + (ah->opmode == NL80211_IFTYPE_ADHOC))) { + ATH_RSSI_LPF(sc->last_rssi, rx_stats->rs_rssi); + last_rssi = sc->last_rssi; - ath9k_process_rssi(common, hw, hdr, rx_stats); + if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) + rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER); + if (rssi < 0) + rssi = 0; - rx_status->band = hw->conf.chandef.chan->band; - rx_status->freq = hw->conf.chandef.chan->center_freq; - rx_status->signal = ah->noise + rx_stats->rs_rssi; - rx_status->antenna = rx_stats->rs_antenna; - rx_status->flag |= RX_FLAG_MACTIME_END; - if (rx_stats->rs_moreaggr) - rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + ah->stats.avgbrssi = rssi; + } - sc->rx.discard_next = false; - return 0; + rxs->signal = ah->noise + rx_stats->rs_rssi; } -static void ath9k_rx_skb_postprocess(struct ath_common *common, - struct sk_buff *skb, - struct ath_rx_status *rx_stats, - struct ieee80211_rx_status *rxs, - bool decrypt_error) +static void ath9k_process_tsf(struct ath_rx_status *rs, + struct ieee80211_rx_status *rxs, + u64 tsf) { - struct ath_hw *ah = common->ah; - struct ieee80211_hdr *hdr; - int hdrlen, padpos, padsize; - u8 keyix; - __le16 fc; - - /* see if any padding is done by the hw and remove it */ - hdr = (struct ieee80211_hdr *) skb->data; - hdrlen = ieee80211_get_hdrlen_from_skb(skb); - fc = hdr->frame_control; - padpos = ieee80211_hdrlen(fc); - - /* The MAC header is padded to have 32-bit boundary if the - * packet payload is non-zero. The general calculation for - * padsize would take into account odd header lengths: - * padsize = (4 - padpos % 4) % 4; However, since only - * even-length headers are used, padding can only be 0 or 2 - * bytes and we can optimize this a bit. In addition, we must - * not try to remove padding from short control frames that do - * not have payload. */ - padsize = padpos & 3; - if (padsize && skb->len>=padpos+padsize+FCS_LEN) { - memmove(skb->data + padsize, skb->data, padpos); - skb_pull(skb, padsize); - } + u32 tsf_lower = tsf & 0xffffffff; - keyix = rx_stats->rs_keyix; + rxs->mactime = (tsf & ~0xffffffffULL) | rs->rs_tstamp; + if (rs->rs_tstamp > tsf_lower && + unlikely(rs->rs_tstamp - tsf_lower > 0x10000000)) + rxs->mactime -= 0x100000000ULL; - if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error && - ieee80211_has_protected(fc)) { - rxs->flag |= RX_FLAG_DECRYPTED; - } else if (ieee80211_has_protected(fc) - && !decrypt_error && skb->len >= hdrlen + 4) { - keyix = skb->data[hdrlen + 3] >> 6; - - if (test_bit(keyix, common->keymap)) - rxs->flag |= RX_FLAG_DECRYPTED; - } - if (ah->sw_mgmt_crypto && - (rxs->flag & RX_FLAG_DECRYPTED) && - ieee80211_is_mgmt(fc)) - /* Use software decrypt for management frames. */ - rxs->flag &= ~RX_FLAG_DECRYPTED; + if (rs->rs_tstamp < tsf_lower && + unlikely(tsf_lower - rs->rs_tstamp > 0x10000000)) + rxs->mactime += 0x100000000ULL; } #ifdef CONFIG_ATH9K_DEBUGFS @@ -1133,6 +1067,234 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, #endif } +static bool ath9k_is_mybeacon(struct ath_softc *sc, struct ieee80211_hdr *hdr) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + + if (ieee80211_is_beacon(hdr->frame_control)) { + RX_STAT_INC(rx_beacons); + if (!is_zero_ether_addr(common->curbssid) && + ether_addr_equal(hdr->addr3, common->curbssid)) + return true; + } + + return false; +} + +/* + * For Decrypt or Demic errors, we only mark packet status here and always push + * up the frame up to let mac80211 handle the actual error case, be it no + * decryption key or real decryption error. This let us keep statistics there. + */ +static int ath9k_rx_skb_preprocess(struct ath_softc *sc, + struct sk_buff *skb, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rx_status, + bool *decrypt_error, u64 tsf) +{ + struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_hdr *hdr; + bool discard_current = sc->rx.discard_next; + int ret = 0; + + /* + * Discard corrupt descriptors which are marked in + * ath_get_next_rx_buf(). + */ + sc->rx.discard_next = rx_stats->rs_more; + if (discard_current) + return -EINVAL; + + /* + * Discard zero-length packets. + */ + if (!rx_stats->rs_datalen) { + RX_STAT_INC(rx_len_err); + return -EINVAL; + } + + /* + * rs_status follows rs_datalen so if rs_datalen is too large + * we can take a hint that hardware corrupted it, so ignore + * those frames. + */ + if (rx_stats->rs_datalen > (common->rx_bufsize - ah->caps.rx_status_len)) { + RX_STAT_INC(rx_len_err); + return -EINVAL; + } + + /* Only use status info from the last fragment */ + if (rx_stats->rs_more) + return 0; + + /* + * Return immediately if the RX descriptor has been marked + * as corrupt based on the various error bits. + * + * This is different from the other corrupt descriptor + * condition handled above. + */ + if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC) { + ret = -EINVAL; + goto exit; + } + + hdr = (struct ieee80211_hdr *) (skb->data + ah->caps.rx_status_len); + + ath9k_process_tsf(rx_stats, rx_status, tsf); + ath_debug_stat_rx(sc, rx_stats); + + /* + * Process PHY errors and return so that the packet + * can be dropped. + */ + if (rx_stats->rs_status & ATH9K_RXERR_PHY) { + ath9k_dfs_process_phyerr(sc, hdr, rx_stats, rx_status->mactime); + if (ath_process_fft(sc, hdr, rx_stats, rx_status->mactime)) + RX_STAT_INC(rx_spectral); + + ret = -EINVAL; + goto exit; + } + + /* + * everything but the rate is checked here, the rate check is done + * separately to avoid doing two lookups for a rate for each frame. + */ + if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) { + ret = -EINVAL; + goto exit; + } + + rx_stats->is_mybeacon = ath9k_is_mybeacon(sc, hdr); + if (rx_stats->is_mybeacon) { + sc->hw_busy_count = 0; + ath_start_rx_poll(sc, 3); + } + + if (ath9k_process_rate(common, hw, rx_stats, rx_status)) { + ret =-EINVAL; + goto exit; + } + + ath9k_process_rssi(common, hw, rx_stats, rx_status); + + rx_status->band = hw->conf.chandef.chan->band; + rx_status->freq = hw->conf.chandef.chan->center_freq; + rx_status->antenna = rx_stats->rs_antenna; + rx_status->flag |= RX_FLAG_MACTIME_END; + +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + if (ieee80211_is_data_present(hdr->frame_control) && + !ieee80211_is_qos_nullfunc(hdr->frame_control)) + sc->rx.num_pkts++; +#endif + +exit: + sc->rx.discard_next = false; + return ret; +} + +static void ath9k_rx_skb_postprocess(struct ath_common *common, + struct sk_buff *skb, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs, + bool decrypt_error) +{ + struct ath_hw *ah = common->ah; + struct ieee80211_hdr *hdr; + int hdrlen, padpos, padsize; + u8 keyix; + __le16 fc; + + /* see if any padding is done by the hw and remove it */ + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + fc = hdr->frame_control; + padpos = ieee80211_hdrlen(fc); + + /* The MAC header is padded to have 32-bit boundary if the + * packet payload is non-zero. The general calculation for + * padsize would take into account odd header lengths: + * padsize = (4 - padpos % 4) % 4; However, since only + * even-length headers are used, padding can only be 0 or 2 + * bytes and we can optimize this a bit. In addition, we must + * not try to remove padding from short control frames that do + * not have payload. */ + padsize = padpos & 3; + if (padsize && skb->len>=padpos+padsize+FCS_LEN) { + memmove(skb->data + padsize, skb->data, padpos); + skb_pull(skb, padsize); + } + + keyix = rx_stats->rs_keyix; + + if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error && + ieee80211_has_protected(fc)) { + rxs->flag |= RX_FLAG_DECRYPTED; + } else if (ieee80211_has_protected(fc) + && !decrypt_error && skb->len >= hdrlen + 4) { + keyix = skb->data[hdrlen + 3] >> 6; + + if (test_bit(keyix, common->keymap)) + rxs->flag |= RX_FLAG_DECRYPTED; + } + if (ah->sw_mgmt_crypto && + (rxs->flag & RX_FLAG_DECRYPTED) && + ieee80211_is_mgmt(fc)) + /* Use software decrypt for management frames. */ + rxs->flag &= ~RX_FLAG_DECRYPTED; +} + +/* + * Run the LNA combining algorithm only in these cases: + * + * Standalone WLAN cards with both LNA/Antenna diversity + * enabled in the EEPROM. + * + * WLAN+BT cards which are in the supported card list + * in ath_pci_id_table and the user has loaded the + * driver with "bt_ant_diversity" set to true. + */ +static void ath9k_antenna_check(struct ath_softc *sc, + struct ath_rx_status *rs) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath_common *common = ath9k_hw_common(ah); + + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)) + return; + + /* + * All MPDUs in an aggregate will use the same LNA + * as the first MPDU. + */ + if (rs->rs_isaggr && !rs->rs_firstaggr) + return; + + /* + * Change the default rx antenna if rx diversity + * chooses the other antenna 3 times in a row. + */ + if (sc->rx.defant != rs->rs_antenna) { + if (++sc->rx.rxotherant >= 3) + ath_setdefantenna(sc, rs->rs_antenna); + } else { + sc->rx.rxotherant = 0; + } + + if (pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV) { + if (common->bt_ant_diversity) + ath_ant_comb_scan(sc, rs); + } else { + ath_ant_comb_scan(sc, rs); + } +} + static void ath9k_apply_ampdu_details(struct ath_softc *sc, struct ath_rx_status *rs, struct ieee80211_rx_status *rxs) { @@ -1159,15 +1321,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_hw *hw = sc->hw; - struct ieee80211_hdr *hdr; int retval; struct ath_rx_status rs; enum ath9k_rx_qtype qtype; bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); int dma_type; - u8 rx_status_len = ah->caps.rx_status_len; u64 tsf = 0; - u32 tsf_lower = 0; unsigned long flags; dma_addr_t new_buf_addr; @@ -1179,7 +1338,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP; tsf = ath9k_hw_gettsf64(ah); - tsf_lower = tsf & 0xffffffff; do { bool decrypt_error = false; @@ -1206,55 +1364,14 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) else hdr_skb = skb; - hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len); rxs = IEEE80211_SKB_RXCB(hdr_skb); - if (ieee80211_is_beacon(hdr->frame_control)) { - RX_STAT_INC(rx_beacons); - if (!is_zero_ether_addr(common->curbssid) && - ether_addr_equal(hdr->addr3, common->curbssid)) - rs.is_mybeacon = true; - else - rs.is_mybeacon = false; - } - else - rs.is_mybeacon = false; - - if (ieee80211_is_data_present(hdr->frame_control) && - !ieee80211_is_qos_nullfunc(hdr->frame_control)) - sc->rx.num_pkts++; - - ath_debug_stat_rx(sc, &rs); - memset(rxs, 0, sizeof(struct ieee80211_rx_status)); - rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp; - if (rs.rs_tstamp > tsf_lower && - unlikely(rs.rs_tstamp - tsf_lower > 0x10000000)) - rxs->mactime -= 0x100000000ULL; - - if (rs.rs_tstamp < tsf_lower && - unlikely(tsf_lower - rs.rs_tstamp > 0x10000000)) - rxs->mactime += 0x100000000ULL; - - if (rs.rs_phyerr == ATH9K_PHYERR_RADAR) - ath9k_dfs_process_phyerr(sc, hdr, &rs, rxs->mactime); - - if (rs.rs_status & ATH9K_RXERR_PHY) { - if (ath_process_fft(sc, hdr, &rs, rxs->mactime)) { - RX_STAT_INC(rx_spectral); - goto requeue_drop_frag; - } - } - - retval = ath9k_rx_skb_preprocess(sc, hdr, &rs, rxs, - &decrypt_error); + retval = ath9k_rx_skb_preprocess(sc, hdr_skb, &rs, rxs, + &decrypt_error, tsf); if (retval) goto requeue_drop_frag; - if (rs.is_mybeacon) { - sc->hw_busy_count = 0; - ath_start_rx_poll(sc, 3); - } /* Ensure we always have an skb to requeue once we are done * processing the current buffer's skb */ requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC); @@ -1308,8 +1425,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) sc->rx.frag = skb; goto requeue; } - if (rs.rs_status & ATH9K_RXERR_CORRUPT_DESC) - goto requeue_drop_frag; if (sc->rx.frag) { int space = skb->len - skb_tailroom(hdr_skb); @@ -1328,22 +1443,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) skb = hdr_skb; } - - if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) { - - /* - * change the default rx antenna if rx diversity - * chooses the other antenna 3 times in a row. - */ - if (sc->rx.defant != rs.rs_antenna) { - if (++sc->rx.rxotherant >= 3) - ath_setdefantenna(sc, rs.rs_antenna); - } else { - sc->rx.rxotherant = 0; - } - - } - if (rxs->flag & RX_FLAG_MMIC_STRIPPED) skb_trim(skb, skb->len - 8); @@ -1355,8 +1454,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ath_rx_ps(sc, skb, rs.is_mybeacon); spin_unlock_irqrestore(&sc->sc_pm_lock, flags); - if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3) - ath_ant_comb_scan(sc, &rs); + ath9k_antenna_check(sc, &rs); ath9k_apply_ampdu_details(sc, &rs, rxs); @@ -1375,7 +1473,7 @@ requeue: if (edma) { ath_rx_edma_buf_link(sc, qtype); } else { - ath_rx_buf_link(sc, bf); + ath_rx_buf_relink(sc, bf); ath9k_hw_rxena(ah); } } while (1); diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 5af9744..a13b2d1 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -893,9 +893,9 @@ #define AR_SREV_9485(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485)) -#define AR_SREV_9485_11(_ah) \ - (AR_SREV_9485(_ah) && \ - ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_11)) +#define AR_SREV_9485_11_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9485_11)) #define AR_SREV_9485_OR_LATER(_ah) \ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9485)) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 9279927..35b515f 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -135,6 +135,9 @@ static struct ath_frame_info *get_frame_info(struct sk_buff *skb) static void ath_send_bar(struct ath_atx_tid *tid, u16 seqno) { + if (!tid->an->sta) + return; + ieee80211_send_bar(tid->an->vif, tid->an->sta->addr, tid->tidno, seqno << IEEE80211_SEQ_SEQ_SHIFT); } @@ -168,6 +171,71 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, } } +static struct ath_atx_tid * +ath_get_skb_tid(struct ath_softc *sc, struct ath_node *an, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + u8 tidno = 0; + + hdr = (struct ieee80211_hdr *) skb->data; + if (ieee80211_is_data_qos(hdr->frame_control)) + tidno = ieee80211_get_qos_ctl(hdr)[0]; + + tidno &= IEEE80211_QOS_CTL_TID_MASK; + return ATH_AN_2_TID(an, tidno); +} + +static bool ath_tid_has_buffered(struct ath_atx_tid *tid) +{ + return !skb_queue_empty(&tid->buf_q) || !skb_queue_empty(&tid->retry_q); +} + +static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid) +{ + struct sk_buff *skb; + + skb = __skb_dequeue(&tid->retry_q); + if (!skb) + skb = __skb_dequeue(&tid->buf_q); + + return skb; +} + +/* + * ath_tx_tid_change_state: + * - clears a-mpdu flag of previous session + * - force sequence number allocation to fix next BlockAck Window + */ +static void +ath_tx_tid_change_state(struct ath_softc *sc, struct ath_atx_tid *tid) +{ + struct ath_txq *txq = tid->ac->txq; + struct ieee80211_tx_info *tx_info; + struct sk_buff *skb, *tskb; + struct ath_buf *bf; + struct ath_frame_info *fi; + + skb_queue_walk_safe(&tid->buf_q, skb, tskb) { + fi = get_frame_info(skb); + bf = fi->bf; + + tx_info = IEEE80211_SKB_CB(skb); + tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU; + + if (bf) + continue; + + bf = ath_tx_setup_buffer(sc, txq, tid, skb); + if (!bf) { + __skb_unlink(skb, &tid->buf_q); + ath_txq_skb_done(sc, txq, skb); + ieee80211_free_txskb(sc->hw, skb); + continue; + } + } + +} + static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) { struct ath_txq *txq = tid->ac->txq; @@ -182,28 +250,22 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) memset(&ts, 0, sizeof(ts)); - while ((skb = __skb_dequeue(&tid->buf_q))) { + while ((skb = __skb_dequeue(&tid->retry_q))) { fi = get_frame_info(skb); bf = fi->bf; - if (!bf) { - bf = ath_tx_setup_buffer(sc, txq, tid, skb); - if (!bf) { - ath_txq_skb_done(sc, txq, skb); - ieee80211_free_txskb(sc->hw, skb); - continue; - } + ath_txq_skb_done(sc, txq, skb); + ieee80211_free_txskb(sc->hw, skb); + continue; } - if (fi->retries) { - list_add_tail(&bf->list, &bf_head); + if (fi->baw_tracked) { ath_tx_update_baw(sc, tid, bf->bf_state.seqno); - ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); sendbar = true; - } else { - ath_set_rates(tid->an->vif, tid->an->sta, bf); - ath_tx_send_normal(sc, txq, NULL, skb); } + + list_add_tail(&bf->list, &bf_head); + ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); } if (sendbar) { @@ -232,13 +294,16 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, } static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid, - u16 seqno) + struct ath_buf *bf) { + struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu); + u16 seqno = bf->bf_state.seqno; int index, cindex; index = ATH_BA_INDEX(tid->seq_start, seqno); cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); __set_bit(cindex, tid->tx_buf); + fi->baw_tracked = 1; if (index >= ((tid->baw_tail - tid->baw_head) & (ATH_TID_MAX_BUFS - 1))) { @@ -247,12 +312,6 @@ static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid, } } -/* - * TODO: For frame(s) that are in the retry state, we will reuse the - * sequence number(s) without setting the retry bit. The - * alternative is to give up on these and BAR the receiver's window - * forward. - */ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid) @@ -266,7 +325,7 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq, memset(&ts, 0, sizeof(ts)); INIT_LIST_HEAD(&bf_head); - while ((skb = __skb_dequeue(&tid->buf_q))) { + while ((skb = ath_tid_dequeue(tid))) { fi = get_frame_info(skb); bf = fi->bf; @@ -276,14 +335,8 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq, } list_add_tail(&bf->list, &bf_head); - - ath_tx_update_baw(sc, tid, bf->bf_state.seqno); ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); } - - tid->seq_next = tid->seq_start; - tid->baw_tail = tid->baw_head; - tid->bar_index = -1; } static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq, @@ -403,7 +456,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ieee80211_tx_rate rates[4]; struct ath_frame_info *fi; int nframes; - u8 tidno; bool flush = !!(ts->ts_status & ATH9K_TX_FLUSH); int i, retries; int bar_index = -1; @@ -429,7 +481,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, while (bf) { bf_next = bf->bf_next; - if (!bf->bf_stale || bf_next != NULL) + if (!bf->bf_state.stale || bf_next != NULL) list_move_tail(&bf->list, &bf_head); ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, 0); @@ -440,8 +492,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, } an = (struct ath_node *)sta->drv_priv; - tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; - tid = ATH_AN_2_TID(an, tidno); + tid = ath_get_skb_tid(sc, an, skb); seq_first = tid->seq_start; isba = ts->ts_flags & ATH9K_TX_BA; @@ -453,7 +504,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, * Only BlockAcks have a TID and therefore normal Acks cannot be * checked */ - if (isba && tidno != ts->tid) + if (isba && tid->tidno != ts->tid) txok = false; isaggr = bf_isaggr(bf); @@ -489,7 +540,8 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, tx_info = IEEE80211_SKB_CB(skb); fi = get_frame_info(skb); - if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) { + if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno) || + !tid->active) { /* * Outside of the current BlockAck window, * maybe part of a previous session @@ -522,7 +574,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, * not a holding desc. */ INIT_LIST_HEAD(&bf_head); - if (bf_next != NULL || !bf_last->bf_stale) + if (bf_next != NULL || !bf_last->bf_state.stale) list_move_tail(&bf->list, &bf_head); if (!txpending) { @@ -546,7 +598,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, ieee80211_sta_eosp(sta); } /* retry the un-acked ones */ - if (bf->bf_next == NULL && bf_last->bf_stale) { + if (bf->bf_next == NULL && bf_last->bf_state.stale) { struct ath_buf *tbf; tbf = ath_clone_txbuf(sc, bf_last); @@ -583,7 +635,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, if (an->sleeping) ieee80211_sta_set_buffered(sta, tid->tidno, true); - skb_queue_splice(&bf_pending, &tid->buf_q); + skb_queue_splice_tail(&bf_pending, &tid->retry_q); if (!an->sleeping) { ath_tx_queue_tid(txq, tid); @@ -641,7 +693,7 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, } else ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok); - if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && !flush) + if (!flush) ath_txq_schedule(sc, txq); } @@ -815,15 +867,20 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, static struct ath_buf * ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, - struct ath_atx_tid *tid) + struct ath_atx_tid *tid, struct sk_buff_head **q) { + struct ieee80211_tx_info *tx_info; struct ath_frame_info *fi; struct sk_buff *skb; struct ath_buf *bf; u16 seqno; while (1) { - skb = skb_peek(&tid->buf_q); + *q = &tid->retry_q; + if (skb_queue_empty(*q)) + *q = &tid->buf_q; + + skb = skb_peek(*q); if (!skb) break; @@ -831,14 +888,26 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, bf = fi->bf; if (!fi->bf) bf = ath_tx_setup_buffer(sc, txq, tid, skb); + else + bf->bf_state.stale = false; if (!bf) { - __skb_unlink(skb, &tid->buf_q); + __skb_unlink(skb, *q); ath_txq_skb_done(sc, txq, skb); ieee80211_free_txskb(sc->hw, skb); continue; } + bf->bf_next = NULL; + bf->bf_lastbf = bf; + + tx_info = IEEE80211_SKB_CB(skb); + tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT; + if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) { + bf->bf_state.bf_type = 0; + return bf; + } + bf->bf_state.bf_type = BUF_AMPDU | BUF_AGGR; seqno = bf->bf_state.seqno; @@ -852,73 +921,52 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, INIT_LIST_HEAD(&bf_head); list_add(&bf->list, &bf_head); - __skb_unlink(skb, &tid->buf_q); + __skb_unlink(skb, *q); ath_tx_update_baw(sc, tid, seqno); ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); continue; } - bf->bf_next = NULL; - bf->bf_lastbf = bf; return bf; } return NULL; } -static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, - struct ath_txq *txq, - struct ath_atx_tid *tid, - struct list_head *bf_q, - int *aggr_len) +static bool +ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid, struct list_head *bf_q, + struct ath_buf *bf_first, struct sk_buff_head *tid_q, + int *aggr_len) { #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) - struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL; - int rl = 0, nframes = 0, ndelim, prev_al = 0; + struct ath_buf *bf = bf_first, *bf_prev = NULL; + int nframes = 0, ndelim; u16 aggr_limit = 0, al = 0, bpad = 0, - al_delta, h_baw = tid->baw_size / 2; - enum ATH_AGGR_STATUS status = ATH_AGGR_DONE; + al_delta, h_baw = tid->baw_size / 2; struct ieee80211_tx_info *tx_info; struct ath_frame_info *fi; struct sk_buff *skb; + bool closed = false; - do { - bf = ath_tx_get_tid_subframe(sc, txq, tid); - if (!bf) { - status = ATH_AGGR_BAW_CLOSED; - break; - } + bf = bf_first; + aggr_limit = ath_lookup_rate(sc, bf, tid); + do { skb = bf->bf_mpdu; fi = get_frame_info(skb); - if (!bf_first) - bf_first = bf; - - if (!rl) { - ath_set_rates(tid->an->vif, tid->an->sta, bf); - aggr_limit = ath_lookup_rate(sc, bf, tid); - rl = 1; - } - /* do not exceed aggregation limit */ al_delta = ATH_AGGR_DELIM_SZ + fi->framelen; + if (nframes) { + if (aggr_limit < al + bpad + al_delta || + ath_lookup_legacy(bf) || nframes >= h_baw) + break; - if (nframes && - ((aggr_limit < (al + bpad + al_delta + prev_al)) || - ath_lookup_legacy(bf))) { - status = ATH_AGGR_LIMITED; - break; - } - - tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); - if (nframes && (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) - break; - - /* do not exceed subframe limit */ - if (nframes >= min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) { - status = ATH_AGGR_LIMITED; - break; + tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); + if ((tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) || + !(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) + break; } /* add padding for previous frame to aggregation length */ @@ -936,22 +984,37 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, bf->bf_next = NULL; /* link buffers of this frame to the aggregate */ - if (!fi->retries) - ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); + if (!fi->baw_tracked) + ath_tx_addto_baw(sc, tid, bf); bf->bf_state.ndelim = ndelim; - __skb_unlink(skb, &tid->buf_q); + __skb_unlink(skb, tid_q); list_add_tail(&bf->list, bf_q); if (bf_prev) bf_prev->bf_next = bf; bf_prev = bf; - } while (!skb_queue_empty(&tid->buf_q)); + bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q); + if (!bf) { + closed = true; + break; + } + } while (ath_tid_has_buffered(tid)); + + bf = bf_first; + bf->bf_lastbf = bf_prev; + + if (bf == bf_prev) { + al = get_frame_info(bf->bf_mpdu)->framelen; + bf->bf_state.bf_type = BUF_AMPDU; + } else { + TX_STAT_INC(txq->axq_qnum, a_aggr); + } *aggr_len = al; - return status; + return closed; #undef PADBYTES } @@ -1023,7 +1086,7 @@ void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop) } static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, - struct ath_tx_info *info, int len) + struct ath_tx_info *info, int len, bool rts) { struct ath_hw *ah = sc->sc_ah; struct sk_buff *skb; @@ -1032,6 +1095,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, const struct ieee80211_rate *rate; struct ieee80211_hdr *hdr; struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu); + u32 rts_thresh = sc->hw->wiphy->rts_threshold; int i; u8 rix = 0; @@ -1054,7 +1118,17 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, rix = rates[i].idx; info->rates[i].Tries = rates[i].count; - if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) { + /* + * Handle RTS threshold for unaggregated HT frames. + */ + if (bf_isampdu(bf) && !bf_isaggr(bf) && + (rates[i].flags & IEEE80211_TX_RC_MCS) && + unlikely(rts_thresh != (u32) -1)) { + if (!rts_thresh || (len > rts_thresh)) + rts = true; + } + + if (rts || rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) { info->rates[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS; info->flags |= ATH9K_TXDESC_RTSENA; } else if (rates[i].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { @@ -1147,6 +1221,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, struct ath_hw *ah = sc->sc_ah; struct ath_buf *bf_first = NULL; struct ath_tx_info info; + u32 rts_thresh = sc->hw->wiphy->rts_threshold; + bool rts = false; memset(&info, 0, sizeof(info)); info.is_first = true; @@ -1183,7 +1259,22 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S; - ath_buf_set_rate(sc, bf, &info, len); + /* + * mac80211 doesn't handle RTS threshold for HT because + * the decision has to be taken based on AMPDU length + * and aggregation is done entirely inside ath9k. + * Set the RTS/CTS flag for the first subframe based + * on the threshold. + */ + if (aggr && (bf == bf_first) && + unlikely(rts_thresh != (u32) -1)) { + /* + * "len" is the size of the entire AMPDU. + */ + if (!rts_thresh || (len > rts_thresh)) + rts = true; + } + ath_buf_set_rate(sc, bf, &info, len, rts); } info.buf_addr[0] = bf->bf_buf_addr; @@ -1212,53 +1303,86 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, } } -static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, - struct ath_atx_tid *tid) +static void +ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid, struct list_head *bf_q, + struct ath_buf *bf_first, struct sk_buff_head *tid_q) { - struct ath_buf *bf; - enum ATH_AGGR_STATUS status; - struct ieee80211_tx_info *tx_info; - struct list_head bf_q; - int aggr_len; + struct ath_buf *bf = bf_first, *bf_prev = NULL; + struct sk_buff *skb; + int nframes = 0; do { - if (skb_queue_empty(&tid->buf_q)) - return; + struct ieee80211_tx_info *tx_info; + skb = bf->bf_mpdu; - INIT_LIST_HEAD(&bf_q); + nframes++; + __skb_unlink(skb, tid_q); + list_add_tail(&bf->list, bf_q); + if (bf_prev) + bf_prev->bf_next = bf; + bf_prev = bf; - status = ath_tx_form_aggr(sc, txq, tid, &bf_q, &aggr_len); + if (nframes >= 2) + break; - /* - * no frames picked up to be aggregated; - * block-ack window is not open. - */ - if (list_empty(&bf_q)) + bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q); + if (!bf) break; - bf = list_first_entry(&bf_q, struct ath_buf, list); - bf->bf_lastbf = list_entry(bf_q.prev, struct ath_buf, list); tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) + break; - if (tid->ac->clear_ps_filter) { - tid->ac->clear_ps_filter = false; - tx_info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; - } else { - tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT; - } + ath_set_rates(tid->an->vif, tid->an->sta, bf); + } while (1); +} - /* if only one frame, send as non-aggregate */ - if (bf == bf->bf_lastbf) { - aggr_len = get_frame_info(bf->bf_mpdu)->framelen; - bf->bf_state.bf_type = BUF_AMPDU; - } else { - TX_STAT_INC(txq->axq_qnum, a_aggr); - } +static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid, bool *stop) +{ + struct ath_buf *bf; + struct ieee80211_tx_info *tx_info; + struct sk_buff_head *tid_q; + struct list_head bf_q; + int aggr_len = 0; + bool aggr, last = true; + + if (!ath_tid_has_buffered(tid)) + return false; + + INIT_LIST_HEAD(&bf_q); + + bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q); + if (!bf) + return false; + + tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); + aggr = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU); + if ((aggr && txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) || + (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) { + *stop = true; + return false; + } - ath_tx_fill_desc(sc, bf, txq, aggr_len); - ath_tx_txqaddbuf(sc, txq, &bf_q, false); - } while (txq->axq_ampdu_depth < ATH_AGGR_MIN_QDEPTH && - status != ATH_AGGR_BAW_CLOSED); + ath_set_rates(tid->an->vif, tid->an->sta, bf); + if (aggr) + last = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf, + tid_q, &aggr_len); + else + ath_tx_form_burst(sc, txq, tid, &bf_q, bf, tid_q); + + if (list_empty(&bf_q)) + return false; + + if (tid->ac->clear_ps_filter || tid->an->no_ps_filter) { + tid->ac->clear_ps_filter = false; + tx_info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; + } + + ath_tx_fill_desc(sc, bf, txq, aggr_len); + ath_tx_txqaddbuf(sc, txq, &bf_q, false); + return true; } int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, @@ -1282,6 +1406,9 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, an->mpdudensity = density; } + /* force sequence number allocation for pending frames */ + ath_tx_tid_change_state(sc, txtid); + txtid->active = true; txtid->paused = true; *ssn = txtid->seq_start = txtid->seq_next; @@ -1301,8 +1428,9 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) ath_txq_lock(sc, txq); txtid->active = false; - txtid->paused = true; + txtid->paused = false; ath_tx_flush_tid(sc, txtid); + ath_tx_tid_change_state(sc, txtid); ath_txq_unlock_complete(sc, txq); } @@ -1326,7 +1454,7 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, ath_txq_lock(sc, txq); - buffered = !skb_queue_empty(&tid->buf_q); + buffered = ath_tid_has_buffered(tid); tid->sched = false; list_del(&tid->list); @@ -1358,7 +1486,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) ath_txq_lock(sc, txq); ac->clear_ps_filter = true; - if (!skb_queue_empty(&tid->buf_q) && !tid->paused) { + if (!tid->paused && ath_tid_has_buffered(tid)) { ath_tx_queue_tid(txq, tid); ath_txq_schedule(sc, txq); } @@ -1383,7 +1511,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; tid->paused = false; - if (!skb_queue_empty(&tid->buf_q)) { + if (ath_tid_has_buffered(tid)) { ath_tx_queue_tid(txq, tid); ath_txq_schedule(sc, txq); } @@ -1403,6 +1531,7 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_tx_info *info; struct list_head bf_q; struct ath_buf *bf_tail = NULL, *bf; + struct sk_buff_head *tid_q; int sent = 0; int i; @@ -1418,15 +1547,15 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, continue; ath_txq_lock(sc, tid->ac->txq); - while (!skb_queue_empty(&tid->buf_q) && nframes > 0) { - bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid); + while (nframes > 0) { + bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid, &tid_q); if (!bf) break; - __skb_unlink(bf->bf_mpdu, &tid->buf_q); + __skb_unlink(bf->bf_mpdu, tid_q); list_add_tail(&bf->list, &bf_q); ath_set_rates(tid->an->vif, tid->an->sta, bf); - ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); + ath_tx_addto_baw(sc, tid, bf); bf->bf_state.bf_type &= ~BUF_AGGR; if (bf_tail) bf_tail->bf_next = bf; @@ -1436,7 +1565,7 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, sent++; TX_STAT_INC(txq->axq_qnum, a_queued_hw); - if (skb_queue_empty(&tid->buf_q)) + if (an->sta && !ath_tid_has_buffered(tid)) ieee80211_sta_set_buffered(an->sta, i, false); } ath_txq_unlock_complete(sc, tid->ac->txq); @@ -1595,7 +1724,7 @@ static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, while (!list_empty(list)) { bf = list_first_entry(list, struct ath_buf, list); - if (bf->bf_stale) { + if (bf->bf_state.stale) { list_del(&bf->list); ath_tx_return_buffer(sc, bf); @@ -1689,25 +1818,27 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) */ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) { - struct ath_atx_ac *ac, *ac_tmp, *last_ac; + struct ath_atx_ac *ac, *last_ac; struct ath_atx_tid *tid, *last_tid; + bool sent = false; if (test_bit(SC_OP_HW_RESET, &sc->sc_flags) || - list_empty(&txq->axq_acq) || - txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) + list_empty(&txq->axq_acq)) return; rcu_read_lock(); - ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list); last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list); + while (!list_empty(&txq->axq_acq)) { + bool stop = false; - list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) { + ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list); last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list); list_del(&ac->list); ac->sched = false; while (!list_empty(&ac->tid_q)) { + tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, list); list_del(&tid->list); @@ -1716,17 +1847,17 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) if (tid->paused) continue; - ath_tx_sched_aggr(sc, txq, tid); + if (ath_tx_sched_aggr(sc, txq, tid, &stop)) + sent = true; /* * add tid to round-robin queue if more frames * are pending for the tid */ - if (!skb_queue_empty(&tid->buf_q)) + if (ath_tid_has_buffered(tid)) ath_tx_queue_tid(txq, tid); - if (tid == last_tid || - txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) + if (stop || tid == last_tid) break; } @@ -1735,9 +1866,17 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) list_add_tail(&ac->list, &txq->axq_acq); } - if (ac == last_ac || - txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) + if (stop) break; + + if (ac == last_ac) { + if (!sent) + break; + + sent = false; + last_ac = list_entry(txq->axq_acq.prev, + struct ath_atx_ac, list); + } } rcu_read_unlock(); @@ -1816,58 +1955,6 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, } } -static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_txq *txq, - struct ath_atx_tid *tid, struct sk_buff *skb, - struct ath_tx_control *txctl) -{ - struct ath_frame_info *fi = get_frame_info(skb); - struct list_head bf_head; - struct ath_buf *bf; - - /* - * Do not queue to h/w when any of the following conditions is true: - * - there are pending frames in software queue - * - the TID is currently paused for ADDBA/BAR request - * - seqno is not within block-ack window - * - h/w queue depth exceeds low water mark - */ - if ((!skb_queue_empty(&tid->buf_q) || tid->paused || - !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) || - txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) && - txq != sc->tx.uapsdq) { - /* - * Add this frame to software queue for scheduling later - * for aggregation. - */ - TX_STAT_INC(txq->axq_qnum, a_queued_sw); - __skb_queue_tail(&tid->buf_q, skb); - if (!txctl->an || !txctl->an->sleeping) - ath_tx_queue_tid(txq, tid); - return; - } - - bf = ath_tx_setup_buffer(sc, txq, tid, skb); - if (!bf) { - ath_txq_skb_done(sc, txq, skb); - ieee80211_free_txskb(sc->hw, skb); - return; - } - - ath_set_rates(tid->an->vif, tid->an->sta, bf); - bf->bf_state.bf_type = BUF_AMPDU; - INIT_LIST_HEAD(&bf_head); - list_add(&bf->list, &bf_head); - - /* Add sub-frame to BAW */ - ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); - - /* Queue to h/w without aggregation */ - TX_STAT_INC(txq->axq_qnum, a_queued_hw); - bf->bf_lastbf = bf; - ath_tx_fill_desc(sc, bf, txq, fi->framelen); - ath_tx_txqaddbuf(sc, txq, &bf_head, false); -} - static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct sk_buff *skb) { @@ -2010,6 +2097,7 @@ static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; + struct ath_vif *avp; struct ath_softc *sc = hw->priv; int frmlen = skb->len + FCS_LEN; int padpos, padsize; @@ -2017,6 +2105,10 @@ static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, /* NOTE: sta can be NULL according to net/mac80211.h */ if (sta) txctl->an = (struct ath_node *)sta->drv_priv; + else if (vif && ieee80211_is_data(hdr->frame_control)) { + avp = (void *)vif->drv_priv; + txctl->an = &avp->mcast_node; + } if (info->control.hw_key) frmlen += info->control.hw_key->icv_len; @@ -2066,7 +2158,6 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_txq *txq = txctl->txq; struct ath_atx_tid *tid = NULL; struct ath_buf *bf; - u8 tidno; int q; int ret; @@ -2094,22 +2185,25 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, ath_txq_unlock(sc, txq); txq = sc->tx.uapsdq; ath_txq_lock(sc, txq); - } - - if (txctl->an && ieee80211_is_data_qos(hdr->frame_control)) { - tidno = ieee80211_get_qos_ctl(hdr)[0] & - IEEE80211_QOS_CTL_TID_MASK; - tid = ATH_AN_2_TID(txctl->an, tidno); + } else if (txctl->an && + ieee80211_is_data_present(hdr->frame_control)) { + tid = ath_get_skb_tid(sc, txctl->an, skb); WARN_ON(tid->ac->txq != txctl->txq); - } - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && tid) { + if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) + tid->ac->clear_ps_filter = true; + /* - * Try aggregation if it's a unicast data frame - * and the destination is HT capable. + * Add this frame to software queue for scheduling later + * for aggregation. */ - ath_tx_send_ampdu(sc, txq, tid, skb, txctl); + TX_STAT_INC(txq->axq_qnum, a_queued_sw); + __skb_queue_tail(&tid->buf_q, skb); + if (!txctl->an->sleeping) + ath_tx_queue_tid(txq, tid); + + ath_txq_schedule(sc, txq); goto out; } @@ -2168,7 +2262,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif, bf->bf_lastbf = bf; ath_set_rates(vif, NULL, bf); - ath_buf_set_rate(sc, bf, &info, fi->framelen); + ath_buf_set_rate(sc, bf, &info, fi->framelen, false); duration += info.rates[0].PktDuration; if (bf_tail) bf_tail->bf_next = bf; @@ -2372,8 +2466,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) if (list_empty(&txq->axq_q)) { txq->axq_link = NULL; - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) - ath_txq_schedule(sc, txq); + ath_txq_schedule(sc, txq); break; } bf = list_first_entry(&txq->axq_q, struct ath_buf, list); @@ -2387,7 +2480,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) * it with the STALE flag. */ bf_held = NULL; - if (bf->bf_stale) { + if (bf->bf_state.stale) { bf_held = bf; if (list_is_last(&bf_held->list, &txq->axq_q)) break; @@ -2411,7 +2504,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) * however leave the last descriptor back as the holding * descriptor for hw. */ - lastbf->bf_stale = true; + lastbf->bf_state.stale = true; INIT_LIST_HEAD(&bf_head); if (!list_is_singular(&lastbf->list)) list_cut_position(&bf_head, @@ -2466,6 +2559,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) if (ts.qid == sc->beacon.beaconq) { sc->beacon.tx_processed = true; sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK); + + ath9k_csa_is_finished(sc); continue; } @@ -2482,7 +2577,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) } bf = list_first_entry(fifo_list, struct ath_buf, list); - if (bf->bf_stale) { + if (bf->bf_state.stale) { list_del(&bf->list); ath_tx_return_buffer(sc, bf); bf = list_first_entry(fifo_list, struct ath_buf, list); @@ -2504,7 +2599,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) ath_tx_txqaddbuf(sc, txq, &bf_q, true); } } else { - lastbf->bf_stale = true; + lastbf->bf_state.stale = true; if (bf != lastbf) list_cut_position(&bf_head, fifo_list, lastbf->list.prev); @@ -2595,6 +2690,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) tid->paused = false; tid->active = false; __skb_queue_head_init(&tid->buf_q); + __skb_queue_head_init(&tid->retry_q); acno = TID_TO_WME_AC(tidno); tid->ac = &an->ac[acno]; } @@ -2602,6 +2698,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) for (acno = 0, ac = &an->ac[acno]; acno < IEEE80211_NUM_ACS; acno++, ac++) { ac->sched = false; + ac->clear_ps_filter = true; ac->txq = sc->tx.txq_map[acno]; INIT_LIST_HEAD(&ac->tid_q); } diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 4684dd9..e935f61 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -602,8 +602,8 @@ static void carl9170_ba_check(struct ar9170 *ar, void *data, unsigned int len) if (bar->start_seq_num == entry_bar->start_seq_num && TID_CHECK(bar->control, entry_bar->control) && - compare_ether_addr(bar->ra, entry_bar->ta) == 0 && - compare_ether_addr(bar->ta, entry_bar->ra) == 0) { + ether_addr_equal(bar->ra, entry_bar->ta) && + ether_addr_equal(bar->ta, entry_bar->ra)) { struct ieee80211_tx_info *tx_info; tx_info = IEEE80211_SKB_CB(entry_skb); diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index f891d51..990dd42a 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -11,9 +11,6 @@ wil6210-y += txrx.o wil6210-y += debug.o wil6210-$(CONFIG_WIL6210_TRACING) += trace.o -ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) - subdir-ccflags-y += -Werror -endif # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index ab63676..1caa319 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -51,7 +51,7 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, if ((i % 64) == 0 && (i != 0)) seq_printf(s, "\n"); seq_printf(s, "%s", (d->dma.status & BIT(0)) ? - "S" : (vring->ctx[i] ? "H" : "h")); + "S" : (vring->ctx[i].skb ? "H" : "h")); } seq_printf(s, "\n"); } @@ -406,7 +406,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) volatile struct vring_tx_desc *d = &(vring->va[dbg_txdesc_index].tx); volatile u32 *u = (volatile u32 *)d; - struct sk_buff *skb = vring->ctx[dbg_txdesc_index]; + struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb; seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 29dd1e5..717178f 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -127,6 +127,8 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) ndev->netdev_ops = &wil_netdev_ops; ndev->ieee80211_ptr = wdev; + ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; diff --git a/drivers/net/wireless/ath/wil6210/trace.h b/drivers/net/wireless/ath/wil6210/trace.h index eff1239..e59239d 100644 --- a/drivers/net/wireless/ath/wil6210/trace.h +++ b/drivers/net/wireless/ath/wil6210/trace.h @@ -37,36 +37,40 @@ static inline void trace_ ## name(proto) {} #endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */ DECLARE_EVENT_CLASS(wil6210_wmi, - TP_PROTO(u16 id, void *buf, u16 buf_len), + TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len), - TP_ARGS(id, buf, buf_len), + TP_ARGS(wmi, buf, buf_len), TP_STRUCT__entry( + __field(u8, mid) __field(u16, id) + __field(u32, timestamp) __field(u16, buf_len) __dynamic_array(u8, buf, buf_len) ), TP_fast_assign( - __entry->id = id; + __entry->mid = wmi->mid; + __entry->id = le16_to_cpu(wmi->id); + __entry->timestamp = le32_to_cpu(wmi->timestamp); __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); ), TP_printk( - "id 0x%04x len %d", - __entry->id, __entry->buf_len + "MID %d id 0x%04x len %d timestamp %d", + __entry->mid, __entry->id, __entry->buf_len, __entry->timestamp ) ); DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd, - TP_PROTO(u16 id, void *buf, u16 buf_len), - TP_ARGS(id, buf, buf_len) + TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len), + TP_ARGS(wmi, buf, buf_len) ); DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event, - TP_PROTO(u16 id, void *buf, u16 buf_len), - TP_ARGS(id, buf, buf_len) + TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len), + TP_ARGS(wmi, buf, buf_len) ); #define WIL6210_MSG_MAX (200) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index d240b24..d505b26 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -18,6 +18,9 @@ #include <net/ieee80211_radiotap.h> #include <linux/if_arp.h> #include <linux/moduleparam.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <net/ipv6.h> #include "wil6210.h" #include "wmi.h" @@ -70,7 +73,7 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) vring->swhead = 0; vring->swtail = 0; - vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL); + vring->ctx = kcalloc(vring->size, sizeof(vring->ctx[0]), GFP_KERNEL); if (!vring->ctx) { vring->va = NULL; return -ENOMEM; @@ -108,39 +111,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, while (!wil_vring_is_empty(vring)) { dma_addr_t pa; - struct sk_buff *skb; u16 dmalen; + struct wil_ctx *ctx; if (tx) { struct vring_tx_desc dd, *d = ⅆ volatile struct vring_tx_desc *_d = &vring->va[vring->swtail].tx; + ctx = &vring->ctx[vring->swtail]; *d = *_d; pa = wil_desc_addr(&d->dma.addr); dmalen = le16_to_cpu(d->dma.length); - skb = vring->ctx[vring->swtail]; - if (skb) { - dma_unmap_single(dev, pa, dmalen, - DMA_TO_DEVICE); - dev_kfree_skb_any(skb); - vring->ctx[vring->swtail] = NULL; - } else { + if (vring->ctx[vring->swtail].mapped_as_page) { dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); + } else { + dma_unmap_single(dev, pa, dmalen, + DMA_TO_DEVICE); } + if (ctx->skb) + dev_kfree_skb_any(ctx->skb); vring->swtail = wil_vring_next_tail(vring); } else { /* rx */ struct vring_rx_desc dd, *d = ⅆ volatile struct vring_rx_desc *_d = - &vring->va[vring->swtail].rx; + &vring->va[vring->swhead].rx; + ctx = &vring->ctx[vring->swhead]; *d = *_d; pa = wil_desc_addr(&d->dma.addr); dmalen = le16_to_cpu(d->dma.length); - skb = vring->ctx[vring->swhead]; dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE); - kfree_skb(skb); + kfree_skb(ctx->skb); wil_vring_advance_head(vring, 1); } } @@ -187,7 +190,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ d->dma.length = cpu_to_le16(sz); *_d = *d; - vring->ctx[i] = skb; + vring->ctx[i].skb = skb; return 0; } @@ -352,11 +355,11 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, return NULL; } - skb = vring->ctx[vring->swhead]; + skb = vring->ctx[vring->swhead].skb; d = wil_skb_rxdesc(skb); *d = *_d; pa = wil_desc_addr(&d->dma.addr); - vring->ctx[vring->swhead] = NULL; + vring->ctx[vring->swhead].skb = NULL; wil_vring_advance_head(vring, 1); dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); @@ -407,6 +410,21 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, return NULL; } + /* L4 IDENT is on when HW calculated checksum, check status + * and in case of error drop the packet + * higher stack layers will handle retransmission (if required) + */ + if (d->dma.status & RX_DMA_STATUS_L4_IDENT) { + /* L4 protocol identified, csum calculated */ + if ((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0) + skb->ip_summed = CHECKSUM_UNNECESSARY; + /* If HW reports bad checksum, let IP stack re-check it + * For example, HW don't understand Microsoft IP stack that + * mis-calculates TCP checksum - if it should be 0x0, + * it writes 0xffff in violation of RFC 1624 + */ + } + ds_bits = wil_rxdesc_ds_bits(d); if (ds_bits == 1) { /* @@ -646,6 +664,53 @@ static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len, return 0; } +static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil, + struct vring_tx_desc *d, + struct sk_buff *skb) +{ + int protocol; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + switch (skb->protocol) { + case cpu_to_be16(ETH_P_IP): + protocol = ip_hdr(skb)->protocol; + break; + case cpu_to_be16(ETH_P_IPV6): + protocol = ipv6_hdr(skb)->nexthdr; + break; + default: + return -EINVAL; + } + + switch (protocol) { + case IPPROTO_TCP: + d->dma.d0 |= (2 << DMA_CFG_DESC_TX_0_L4_TYPE_POS); + /* L4 header len: TCP header length */ + d->dma.d0 |= + (tcp_hdrlen(skb) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK); + break; + case IPPROTO_UDP: + /* L4 header len: UDP header length */ + d->dma.d0 |= + (sizeof(struct udphdr) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK); + break; + default: + return -EINVAL; + } + + d->dma.ip_length = skb_network_header_len(skb); + d->dma.b11 = ETH_HLEN; /* MAC header length */ + d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS); + /* Enable TCP/UDP checksum */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS); + /* Calculate pseudo-header */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS); + + return 0; +} + static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, struct sk_buff *skb) { @@ -655,7 +720,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, u32 swhead = vring->swhead; int avail = wil_vring_avail_tx(vring); int nr_frags = skb_shinfo(skb)->nr_frags; - uint f; + uint f = 0; int vring_index = vring - wil->vring_tx; uint i = swhead; dma_addr_t pa; @@ -686,13 +751,20 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, return -EINVAL; /* 1-st segment */ wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index); + /* Process TCP/UDP checksum offloading */ + if (wil_tx_desc_offload_cksum_set(wil, d, skb)) { + wil_err(wil, "VRING #%d Failed to set cksum, drop packet\n", + vring_index); + goto dma_error; + } + d->mac.d[2] |= ((nr_frags + 1) << MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); if (nr_frags) *_d = *d; /* middle segments */ - for (f = 0; f < nr_frags; f++) { + for (; f < nr_frags; f++) { const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[f]; int len = skb_frag_size(frag); @@ -703,7 +775,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, if (unlikely(dma_mapping_error(dev, pa))) goto dma_error; wil_tx_desc_map(d, pa, len, vring_index); - vring->ctx[i] = NULL; + vring->ctx[i].mapped_as_page = 1; *_d = *d; } /* for the last seg only */ @@ -712,6 +784,12 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); *_d = *d; + /* hold reference to skb + * to prevent skb release before accounting + * in case of immediate "tx done" + */ + vring->ctx[i].skb = skb_get(skb); + wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); @@ -720,29 +798,31 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags); iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); - /* hold reference to skb - * to prevent skb release before accounting - * in case of immediate "tx done" - */ - vring->ctx[i] = skb_get(skb); return 0; dma_error: /* unmap what we have mapped */ - /* Note: increment @f to operate with positive index */ - for (f++; f > 0; f--) { + nr_frags = f + 1; /* frags mapped + one for skb head */ + for (f = 0; f < nr_frags; f++) { u16 dmalen; + struct wil_ctx *ctx; i = (swhead + f) % vring->size; + ctx = &vring->ctx[i]; _d = &(vring->va[i].tx); *d = *_d; _d->dma.status = TX_DMA_STATUS_DU; pa = wil_desc_addr(&d->dma.addr); dmalen = le16_to_cpu(d->dma.length); - if (vring->ctx[i]) - dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); - else + if (ctx->mapped_as_page) dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); + else + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); + + if (ctx->skb) + dev_kfree_skb_any(ctx->skb); + + memset(ctx, 0, sizeof(*ctx)); } return -EINVAL; @@ -821,8 +901,9 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) &vring->va[vring->swtail].tx; struct vring_tx_desc dd, *d = ⅆ dma_addr_t pa; - struct sk_buff *skb; u16 dmalen; + struct wil_ctx *ctx = &vring->ctx[vring->swtail]; + struct sk_buff *skb = ctx->skb; *d = *_d; @@ -840,7 +921,11 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) (const void *)d, sizeof(*d), false); pa = wil_desc_addr(&d->dma.addr); - skb = vring->ctx[vring->swtail]; + if (ctx->mapped_as_page) + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); + else + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); + if (skb) { if (d->dma.error == 0) { ndev->stats.tx_packets++; @@ -849,16 +934,15 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) ndev->stats.tx_errors++; } - dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); dev_kfree_skb_any(skb); - vring->ctx[vring->swtail] = NULL; - } else { - dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); } - d->dma.addr.addr_low = 0; - d->dma.addr.addr_high = 0; - d->dma.length = 0; - d->dma.status = TX_DMA_STATUS_DU; + memset(ctx, 0, sizeof(*ctx)); + /* + * There is no need to touch HW descriptor: + * - ststus bit TX_DMA_STATUS_DU is set by design, + * so hardware will not try to process this desc., + * - rest of descriptor will be initialized on Tx. + */ vring->swtail = wil_vring_next_tail(vring); done++; } diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 859aea6..b382827 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -235,7 +235,16 @@ struct vring_tx_mac { #define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30 #define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2 -#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 +#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 /* L4 type: 0-UDP, 2-TCP */ + + +#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_POS 0 +#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_LEN 7 +#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_MSK 0x7F /* MAC hdr len */ + +#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS 7 +#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_LEN 1 +#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_MSK 0x80 /* 1-IPv4, 0-IPv6 */ #define TX_DMA_STATUS_DU BIT(0) @@ -334,8 +343,17 @@ struct vring_rx_mac { #define RX_DMA_D0_CMD_DMA_IT BIT(10) +/* Error field, offload bits */ +#define RX_DMA_ERROR_L3_ERR BIT(4) +#define RX_DMA_ERROR_L4_ERR BIT(5) + + +/* Status field */ #define RX_DMA_STATUS_DU BIT(0) #define RX_DMA_STATUS_ERROR BIT(2) + +#define RX_DMA_STATUS_L3_IDENT BIT(4) +#define RX_DMA_STATUS_L4_IDENT BIT(5) #define RX_DMA_STATUS_PHY_INFO BIT(6) struct vring_rx_dma { diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 44fdab5..c4a5163 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -156,11 +156,22 @@ struct wil6210_mbox_hdr { /* max. value for wil6210_mbox_hdr.len */ #define MAX_MBOXITEM_SIZE (240) +/** + * struct wil6210_mbox_hdr_wmi - WMI header + * + * @mid: MAC ID + * 00 - default, created by FW + * 01..0f - WiFi ports, driver to create + * 10..fe - debug + * ff - broadcast + * @id: command/event ID + * @timestamp: FW fills for events, free-running msec timer + */ struct wil6210_mbox_hdr_wmi { - u8 reserved0[2]; + u8 mid; + u8 reserved; __le16 id; - __le16 info1; /* bits [0..3] - device_id, rest - unused */ - u8 reserved1[2]; + __le32 timestamp; } __packed; struct pending_wmi_event { @@ -172,6 +183,14 @@ struct pending_wmi_event { } __packed event; }; +/** + * struct wil_ctx - software context for Vring descriptor + */ +struct wil_ctx { + struct sk_buff *skb; + u8 mapped_as_page:1; +}; + union vring_desc; struct vring { @@ -181,7 +200,7 @@ struct vring { u32 swtail; u32 swhead; u32 hwtail; /* write here to inform hw */ - void **ctx; /* void *ctx[size] - software context */ + struct wil_ctx *ctx; /* ctx[size] - software context */ }; enum { /* for wil6210_priv.status */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index dc8059a..063963e 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -172,8 +172,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) .len = cpu_to_le16(sizeof(cmd.wmi) + len), }, .wmi = { + .mid = 0, .id = cpu_to_le16(cmdid), - .info1 = 0, }, }; struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; @@ -248,7 +248,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) iowrite32(r->head = next_head, wil->csr + HOST_MBOX + offsetof(struct wil6210_mbox_ctl, tx.head)); - trace_wil6210_wmi_cmd(cmdid, buf, len); + trace_wil6210_wmi_cmd(&cmd.wmi, buf, len); /* interrupt to FW */ iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); @@ -339,7 +339,7 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) } } else { cfg80211_rx_mgmt(wil->wdev, freq, signal, - (void *)rx_mgmt_frame, d_len, GFP_KERNEL); + (void *)rx_mgmt_frame, d_len, 0, GFP_KERNEL); } } @@ -640,9 +640,13 @@ void wmi_recv_cmd(struct wil6210_priv *wil) hdr.flags); if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { - u16 id = le16_to_cpu(evt->event.wmi.id); - wil_dbg_wmi(wil, "WMI event 0x%04x\n", id); - trace_wil6210_wmi_event(id, &evt->event.wmi, len); + struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi; + u16 id = le16_to_cpu(wmi->id); + u32 tstamp = le32_to_cpu(wmi->timestamp); + wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n", + id, wmi->mid, tstamp); + trace_wil6210_wmi_event(wmi, &wmi[1], + len - sizeof(*wmi)); } wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1, &evt->event.hdr, sizeof(hdr) + len, true); @@ -920,6 +924,12 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) cmd.sniffer_cfg.phy_support = cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); + } else { + /* Initialize offload (in non-sniffer mode). + * Linux IP stack always calculates IP checksum + * HW always calculate TCP/UDP checksum + */ + cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS); } /* typical time for secure PCP is 840ms */ rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index f7c70b3..c51d2dc 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -431,9 +431,9 @@ static int alloc_ringmemory(struct b43_dmaring *ring) u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; - ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev, - ring_mem_size, &(ring->dmabase), - GFP_KERNEL | __GFP_ZERO); + ring->descbase = dma_zalloc_coherent(ring->dev->dev->dma_dev, + ring_mem_size, &(ring->dmabase), + GFP_KERNEL); if (!ring->descbase) return -ENOMEM; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 0e933bb..ccd24f0a 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4645,6 +4645,19 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, B43_MACCTL_PSM_JMP0); + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_core_pci_down(dev->dev->bdev->bus); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + /* TODO */ + break; +#endif + } + b43_dma_free(dev); b43_pio_free(dev); b43_chip_exit(dev); @@ -4684,6 +4697,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) case B43_BUS_BCMA: bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci[0], dev->dev->bdev, true); + bcma_core_pci_up(dev->dev->bdev->bus); break; #endif #ifdef CONFIG_B43_SSB diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index faeafe2..42eb26c 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -331,10 +331,9 @@ void free_descriptor_buffer(struct b43legacy_dmaring *ring, static int alloc_ringmemory(struct b43legacy_dmaring *ring) { /* GFP flags must match the flags in free_ringmemory()! */ - ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev, - B43legacy_DMA_RINGMEMSIZE, - &(ring->dmabase), - GFP_KERNEL | __GFP_ZERO); + ring->descbase = dma_zalloc_coherent(ring->dev->dev->dma_dev, + B43legacy_DMA_RINGMEMSIZE, + &(ring->dmabase), GFP_KERNEL); if (!ring->descbase) return -ENOMEM; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index e3f3c48..e13b1a6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -592,6 +592,7 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes) { struct sk_buff *mypkt; + struct sk_buff_head pktq; int err; mypkt = brcmu_pkt_buf_get_skb(nbytes); @@ -602,7 +603,10 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, } memcpy(mypkt->data, buf, nbytes); - err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, mypkt); + __skb_queue_head_init(&pktq); + __skb_queue_tail(&pktq, mypkt); + err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq); + __skb_dequeue_tail(&pktq); brcmu_pkt_buf_free_skb(mypkt); return err; @@ -611,22 +615,18 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt) + uint flags, struct sk_buff_head *pktq) { uint width; int err = 0; - struct sk_buff_head pkt_list; brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", - fn, addr, pkt->len); + fn, addr, pktq->qlen); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; brcmf_sdio_addrprep(sdiodev, width, &addr); - skb_queue_head_init(&pkt_list); - skb_queue_tail(&pkt_list, pkt); - err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, &pkt_list); - skb_dequeue_tail(&pkt_list); + err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq); return err; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index 289e386..64f4a2b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -350,7 +350,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, sdiodev->bus_if = bus_if; bus_if->bus_priv.sdio = sdiodev; - bus_if->align = BRCMF_SDALIGN; dev_set_drvdata(&func->dev, bus_if); dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); sdiodev->dev = &sdiodev->func[1]->dev; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 86cbfe2..2eb9e64 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -194,6 +194,8 @@ #define BRCMF_E_IF_DEL 2 #define BRCMF_E_IF_CHANGE 3 +#define BRCMF_E_IF_FLAG_NOIF 1 + #define BRCMF_E_IF_ROLE_STA 0 #define BRCMF_E_IF_ROLE_AP 1 #define BRCMF_E_IF_ROLE_WDS 2 @@ -209,6 +211,8 @@ #define BRCMF_DCMD_MEDLEN 1536 #define BRCMF_DCMD_MAXLEN 8192 +#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256 + /* Pattern matching filter. Specifies an offset within received packets to * start matching, the pattern to match, the size of the pattern, and a bitmask * that indicates which bits within the pattern should be matched. @@ -505,6 +509,25 @@ struct brcmf_dcmd { uint needed; /* bytes needed (optional) */ }; +/** + * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info + * + * @pktslots: dynamic allocated array for ordering AMPDU packets. + * @flow_id: AMPDU flow identifier. + * @cur_idx: last AMPDU index from firmware. + * @exp_idx: expected next AMPDU index. + * @max_idx: maximum amount of packets per AMPDU. + * @pend_pkts: number of packets currently in @pktslots. + */ +struct brcmf_ampdu_rx_reorder { + struct sk_buff **pktslots; + u8 flow_id; + u8 cur_idx; + u8 exp_idx; + u8 max_idx; + u8 pend_pkts; +}; + /* Forward decls for struct brcmf_pub (see below) */ struct brcmf_proto; /* device communication protocol info */ struct brcmf_cfg80211_dev; /* cfg80211 device info */ @@ -536,9 +559,10 @@ struct brcmf_pub { struct brcmf_fweh_info fweh; - bool fw_signals; struct brcmf_fws_info *fws; - spinlock_t fws_spinlock; + + struct brcmf_ampdu_rx_reorder + *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS]; #ifdef DEBUG struct dentry *dbgfs_dir; #endif @@ -604,6 +628,9 @@ struct brcmf_if { wait_queue_head_t pend_8021x_wait; }; +struct brcmf_skb_reorder_data { + u8 *reorder; +}; extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 080395f..f7c1985 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -36,7 +36,11 @@ struct brcmf_bus_dcmd { * * @init: prepare for communication with dongle. * @stop: clear pending frames, disable data flow. - * @txdata: send a data frame to the dongle (callee disposes skb). + * @txdata: send a data frame to the dongle. When the data + * has been transferred, the common driver must be + * notified using brcmf_txcomplete(). The common + * driver calls this function with interrupts + * disabled. * @txctl: transmit a control request message to dongle. * @rxctl: receive a control response message from dongle. * @gettxq: obtain a reference of bus transmit queue (optional). @@ -65,7 +69,6 @@ struct brcmf_bus_ops { * @maxctl: maximum size for rxctl request message. * @tx_realloc: number of tx packets realloced for headroom. * @dstats: dongle-based statistical data. - * @align: alignment requirement for the bus. * @dcmd_list: bus/device specific dongle initialization commands. * @chip: device identifier of the dongle chip. * @chiprev: revision of the dongle chip. @@ -80,7 +83,6 @@ struct brcmf_bus { enum brcmf_bus_state state; uint maxctl; unsigned long tx_realloc; - u8 align; u32 chip; u32 chiprev; struct list_head dcmd_list; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 8009901..e067aec 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -38,6 +38,19 @@ MODULE_LICENSE("Dual BSD/GPL"); #define MAX_WAIT_FOR_8021X_TX 50 /* msecs */ +/* AMPDU rx reordering definitions */ +#define BRCMF_RXREORDER_FLOWID_OFFSET 0 +#define BRCMF_RXREORDER_MAXIDX_OFFSET 2 +#define BRCMF_RXREORDER_FLAGS_OFFSET 4 +#define BRCMF_RXREORDER_CURIDX_OFFSET 6 +#define BRCMF_RXREORDER_EXPIDX_OFFSET 8 + +#define BRCMF_RXREORDER_DEL_FLOW 0x01 +#define BRCMF_RXREORDER_FLUSH_ALL 0x02 +#define BRCMF_RXREORDER_CURIDX_VALID 0x04 +#define BRCMF_RXREORDER_EXPIDX_VALID 0x08 +#define BRCMF_RXREORDER_NEW_HOLE 0x10 + /* Error bits */ int brcmf_msg_level; module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); @@ -265,17 +278,234 @@ void brcmf_txflowblock(struct device *dev, bool state) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; - int i; brcmf_dbg(TRACE, "Enter\n"); - if (brcmf_fws_fc_active(drvr->fws)) { - brcmf_fws_bus_blocked(drvr, state); + brcmf_fws_bus_blocked(drvr, state); +} + +static void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) +{ + skb->dev = ifp->ndev; + skb->protocol = eth_type_trans(skb, skb->dev); + + if (skb->pkt_type == PACKET_MULTICAST) + ifp->stats.multicast++; + + /* Process special event packets */ + brcmf_fweh_process_skb(ifp->drvr, skb); + + if (!(ifp->ndev->flags & IFF_UP)) { + brcmu_pkt_buf_free_skb(skb); + return; + } + + ifp->stats.rx_bytes += skb->len; + ifp->stats.rx_packets++; + + brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol)); + if (in_interrupt()) + netif_rx(skb); + else + /* If the receive is not processed inside an ISR, + * the softirqd must be woken explicitly to service + * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni(). + */ + netif_rx_ni(skb); +} + +static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi, + u8 start, u8 end, + struct sk_buff_head *skb_list) +{ + /* initialize return list */ + __skb_queue_head_init(skb_list); + + if (rfi->pend_pkts == 0) { + brcmf_dbg(INFO, "no packets in reorder queue\n"); + return; + } + + do { + if (rfi->pktslots[start]) { + __skb_queue_tail(skb_list, rfi->pktslots[start]); + rfi->pktslots[start] = NULL; + } + start++; + if (start > rfi->max_idx) + start = 0; + } while (start != end); + rfi->pend_pkts -= skb_queue_len(skb_list); +} + +static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data, + struct sk_buff *pkt) +{ + u8 flow_id, max_idx, cur_idx, exp_idx, end_idx; + struct brcmf_ampdu_rx_reorder *rfi; + struct sk_buff_head reorder_list; + struct sk_buff *pnext; + u8 flags; + u32 buf_size; + + flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET]; + flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET]; + + /* validate flags and flow id */ + if (flags == 0xFF) { + brcmf_err("invalid flags...so ignore this packet\n"); + brcmf_netif_rx(ifp, pkt); + return; + } + + rfi = ifp->drvr->reorder_flows[flow_id]; + if (flags & BRCMF_RXREORDER_DEL_FLOW) { + brcmf_dbg(INFO, "flow-%d: delete\n", + flow_id); + + if (rfi == NULL) { + brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n", + flow_id); + brcmf_netif_rx(ifp, pkt); + return; + } + + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx, + &reorder_list); + /* add the last packet */ + __skb_queue_tail(&reorder_list, pkt); + kfree(rfi); + ifp->drvr->reorder_flows[flow_id] = NULL; + goto netif_rx; + } + /* from here on we need a flow reorder instance */ + if (rfi == NULL) { + buf_size = sizeof(*rfi); + max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; + + buf_size += (max_idx + 1) * sizeof(pkt); + + /* allocate space for flow reorder info */ + brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n", + flow_id, max_idx); + rfi = kzalloc(buf_size, GFP_ATOMIC); + if (rfi == NULL) { + brcmf_err("failed to alloc buffer\n"); + brcmf_netif_rx(ifp, pkt); + return; + } + + ifp->drvr->reorder_flows[flow_id] = rfi; + rfi->pktslots = (struct sk_buff **)(rfi+1); + rfi->max_idx = max_idx; + } + if (flags & BRCMF_RXREORDER_NEW_HOLE) { + if (rfi->pend_pkts) { + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, + rfi->exp_idx, + &reorder_list); + WARN_ON(rfi->pend_pkts); + } else { + __skb_queue_head_init(&reorder_list); + } + rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; + rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; + rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; + rfi->pktslots[rfi->cur_idx] = pkt; + rfi->pend_pkts++; + brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n", + flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts); + } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) { + cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; + exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; + + if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) { + /* still in the current hole */ + /* enqueue the current on the buffer chain */ + if (rfi->pktslots[cur_idx] != NULL) { + brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n"); + brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); + rfi->pktslots[cur_idx] = NULL; + } + rfi->pktslots[cur_idx] = pkt; + rfi->pend_pkts++; + rfi->cur_idx = cur_idx; + brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n", + flow_id, cur_idx, exp_idx, rfi->pend_pkts); + + /* can return now as there is no reorder + * list to process. + */ + return; + } + if (rfi->exp_idx == cur_idx) { + if (rfi->pktslots[cur_idx] != NULL) { + brcmf_dbg(INFO, "error buffer pending..free it\n"); + brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); + rfi->pktslots[cur_idx] = NULL; + } + rfi->pktslots[cur_idx] = pkt; + rfi->pend_pkts++; + + /* got the expected one. flush from current to expected + * and update expected + */ + brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n", + flow_id, cur_idx, exp_idx, rfi->pend_pkts); + + rfi->cur_idx = cur_idx; + rfi->exp_idx = exp_idx; + + brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx, + &reorder_list); + brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n", + flow_id, skb_queue_len(&reorder_list), + rfi->pend_pkts); + } else { + u8 end_idx; + + brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n", + flow_id, flags, rfi->cur_idx, rfi->exp_idx, + cur_idx, exp_idx); + if (flags & BRCMF_RXREORDER_FLUSH_ALL) + end_idx = rfi->exp_idx; + else + end_idx = exp_idx; + + /* flush pkts first */ + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, + &reorder_list); + + if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) { + __skb_queue_tail(&reorder_list, pkt); + } else { + rfi->pktslots[cur_idx] = pkt; + rfi->pend_pkts++; + } + rfi->exp_idx = exp_idx; + rfi->cur_idx = cur_idx; + } } else { - for (i = 0; i < BRCMF_MAX_IFS; i++) - brcmf_txflowblock_if(drvr->iflist[i], - BRCMF_NETIF_STOP_REASON_BLOCK_BUS, - state); + /* explicity window move updating the expected index */ + exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; + + brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n", + flow_id, flags, rfi->exp_idx, exp_idx); + if (flags & BRCMF_RXREORDER_FLUSH_ALL) + end_idx = rfi->exp_idx; + else + end_idx = exp_idx; + + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, + &reorder_list); + __skb_queue_tail(&reorder_list, pkt); + /* set the new expected idx */ + rfi->exp_idx = exp_idx; + } +netif_rx: + skb_queue_walk_safe(&reorder_list, pkt, pnext) { + __skb_unlink(pkt, &reorder_list); + brcmf_netif_rx(ifp, pkt); } } @@ -285,16 +515,18 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) struct brcmf_if *ifp; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_skb_reorder_data *rd; u8 ifidx; int ret; - brcmf_dbg(DATA, "Enter\n"); + brcmf_dbg(DATA, "Enter: %s: count=%u\n", dev_name(dev), + skb_queue_len(skb_list)); skb_queue_walk_safe(skb_list, skb, pnext) { skb_unlink(skb, skb_list); /* process and remove protocol-specific header */ - ret = brcmf_proto_hdrpull(drvr, drvr->fw_signals, &ifidx, skb); + ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb); ifp = drvr->iflist[ifidx]; if (ret || !ifp || !ifp->ndev) { @@ -304,31 +536,11 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) continue; } - skb->dev = ifp->ndev; - skb->protocol = eth_type_trans(skb, skb->dev); - - if (skb->pkt_type == PACKET_MULTICAST) - ifp->stats.multicast++; - - /* Process special event packets */ - brcmf_fweh_process_skb(drvr, skb); - - if (!(ifp->ndev->flags & IFF_UP)) { - brcmu_pkt_buf_free_skb(skb); - continue; - } - - ifp->stats.rx_bytes += skb->len; - ifp->stats.rx_packets++; - - if (in_interrupt()) - netif_rx(skb); + rd = (struct brcmf_skb_reorder_data *)skb->cb; + if (rd->reorder) + brcmf_rxreorder_process_info(ifp, rd->reorder, skb); else - /* If the receive is not processed inside an ISR, - * the softirqd must be woken explicitly to service the - * NET_RX_SOFTIRQ. This is handled by netif_rx_ni(). - */ - netif_rx_ni(skb); + brcmf_netif_rx(ifp, skb); } } @@ -889,7 +1101,6 @@ int brcmf_bus_start(struct device *dev) if (ret < 0) goto fail; - drvr->fw_signals = true; ret = brcmf_fws_init(drvr); if (ret < 0) goto fail; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 2641119..1aa75d5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -201,13 +201,6 @@ struct rte_console { #define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */ #define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */ -/* HW frame tag */ -#define SDPCM_FRAMETAG_LEN 4 /* 2 bytes len, 2 bytes check val */ - -/* Total length of frame header for dongle protocol */ -#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN) -#define SDPCM_RESERVE (SDPCM_HDRLEN + BRCMF_SDALIGN) - /* * Software allocation of To SB Mailbox resources */ @@ -250,38 +243,6 @@ struct rte_console { /* Current protocol version */ #define SDPCM_PROT_VERSION 4 -/* SW frame header */ -#define SDPCM_PACKET_SEQUENCE(p) (((u8 *)p)[0] & 0xff) - -#define SDPCM_CHANNEL_MASK 0x00000f00 -#define SDPCM_CHANNEL_SHIFT 8 -#define SDPCM_PACKET_CHANNEL(p) (((u8 *)p)[1] & 0x0f) - -#define SDPCM_NEXTLEN_OFFSET 2 - -/* Data Offset from SOF (HW Tag, SW Tag, Pad) */ -#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */ -#define SDPCM_DOFFSET_VALUE(p) (((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff) -#define SDPCM_DOFFSET_MASK 0xff000000 -#define SDPCM_DOFFSET_SHIFT 24 -#define SDPCM_FCMASK_OFFSET 4 /* Flow control */ -#define SDPCM_FCMASK_VALUE(p) (((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff) -#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */ -#define SDPCM_WINDOW_VALUE(p) (((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff) - -#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */ - -/* logical channel numbers */ -#define SDPCM_CONTROL_CHANNEL 0 /* Control channel Id */ -#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */ -#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */ -#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets */ -#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */ - -#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for 8bit frame seq */ - -#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80) - /* * Shared structure between dongle and the host. * The structure contains pointers to trap or assert information. @@ -396,8 +357,8 @@ struct sdpcm_shared_le { __le32 brpt_addr; }; -/* SDIO read frame info */ -struct brcmf_sdio_read { +/* dongle SDIO bus specific header info */ +struct brcmf_sdio_hdrinfo { u8 seq_num; u8 channel; u16 len; @@ -431,7 +392,7 @@ struct brcmf_sdio { u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN]; u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ u8 rx_seq; /* Receive sequence number (expected) */ - struct brcmf_sdio_read cur_read; + struct brcmf_sdio_hdrinfo cur_read; /* info of current read frame */ bool rxskip; /* Skip receive (awaiting NAK ACK) */ bool rxpending; /* Data frame pending in dongle */ @@ -500,6 +461,8 @@ struct brcmf_sdio { struct brcmf_sdio_count sdcnt; bool sr_enabled; /* SaveRestore enabled */ bool sleeping; /* SDIO bus sleeping */ + + u8 tx_hdrlen; /* sdio bus header length for tx packet */ }; /* clkstate */ @@ -510,7 +473,6 @@ struct brcmf_sdio { #ifdef DEBUG static int qcount[NUMPRIO]; -static int tx_packets[NUMPRIO]; #endif /* DEBUG */ #define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */ @@ -1043,18 +1005,63 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) } } -static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, - struct brcmf_sdio_read *rd, - enum brcmf_sdio_frmtype type) +/** + * brcmfmac sdio bus specific header + * This is the lowest layer header wrapped on the packets transmitted between + * host and WiFi dongle which contains information needed for SDIO core and + * firmware + * + * It consists of 2 parts: hw header and software header + * hardware header (frame tag) - 4 bytes + * Byte 0~1: Frame length + * Byte 2~3: Checksum, bit-wise inverse of frame length + * software header - 8 bytes + * Byte 0: Rx/Tx sequence number + * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag + * Byte 2: Length of next data frame, reserved for Tx + * Byte 3: Data offset + * Byte 4: Flow control bits, reserved for Tx + * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet + * Byte 6~7: Reserved + */ +#define SDPCM_HWHDR_LEN 4 +#define SDPCM_SWHDR_LEN 8 +#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN) +/* software header */ +#define SDPCM_SEQ_MASK 0x000000ff +#define SDPCM_SEQ_WRAP 256 +#define SDPCM_CHANNEL_MASK 0x00000f00 +#define SDPCM_CHANNEL_SHIFT 8 +#define SDPCM_CONTROL_CHANNEL 0 /* Control */ +#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */ +#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */ +#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */ +#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */ +#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80) +#define SDPCM_NEXTLEN_MASK 0x00ff0000 +#define SDPCM_NEXTLEN_SHIFT 16 +#define SDPCM_DOFFSET_MASK 0xff000000 +#define SDPCM_DOFFSET_SHIFT 24 +#define SDPCM_FCMASK_MASK 0x000000ff +#define SDPCM_WINDOW_MASK 0x0000ff00 +#define SDPCM_WINDOW_SHIFT 8 + +static inline u8 brcmf_sdio_getdatoffset(u8 *swheader) +{ + u32 hdrvalue; + hdrvalue = *(u32 *)swheader; + return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT); +} + +static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, + struct brcmf_sdio_hdrinfo *rd, + enum brcmf_sdio_frmtype type) { u16 len, checksum; u8 rx_seq, fc, tx_seq_max; + u32 swheader; - /* - * 4 bytes hardware header (frame tag) - * Byte 0~1: Frame length - * Byte 2~3: Checksum, bit-wise inverse of frame length - */ + /* hw header */ len = get_unaligned_le16(header); checksum = get_unaligned_le16(header + sizeof(u16)); /* All zero means no more to read */ @@ -1083,24 +1090,16 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, } rd->len = len; - /* - * 8 bytes hardware header - * Byte 0: Rx sequence number - * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag - * Byte 2: Length of next data frame - * Byte 3: Data offset - * Byte 4: Flow control bits - * Byte 5: Maximum Sequence number allow for Tx - * Byte 6~7: Reserved - */ - if (type == BRCMF_SDIO_FT_SUPER && - SDPCM_GLOMDESC(&header[SDPCM_FRAMETAG_LEN])) { + /* software header */ + header += SDPCM_HWHDR_LEN; + swheader = le32_to_cpu(*(__le32 *)header); + if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) { brcmf_err("Glom descriptor found in superframe head\n"); rd->len = 0; return -EINVAL; } - rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]); - rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]); + rx_seq = (u8)(swheader & SDPCM_SEQ_MASK); + rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT; if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL && type != BRCMF_SDIO_FT_SUPER) { brcmf_err("HW header length too long\n"); @@ -1120,7 +1119,7 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, rd->len = 0; return -EINVAL; } - rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]); + rd->dat_offset = brcmf_sdio_getdatoffset(header); if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { brcmf_err("seq %d: bad data offset\n", rx_seq); bus->sdcnt.rx_badhdr++; @@ -1137,14 +1136,15 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, /* no need to check the reset for subframe */ if (type == BRCMF_SDIO_FT_SUB) return 0; - rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; + rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT; if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { /* only warm for NON glom packet */ if (rd->channel != SDPCM_GLOM_CHANNEL) brcmf_err("seq %d: next length error\n", rx_seq); rd->len_nxtfrm = 0; } - fc = SDPCM_FCMASK_VALUE(&header[SDPCM_FRAMETAG_LEN]); + swheader = le32_to_cpu(*(__le32 *)(header + 4)); + fc = swheader & SDPCM_FCMASK_MASK; if (bus->flowcontrol != fc) { if (~bus->flowcontrol & fc) bus->sdcnt.fc_xoff++; @@ -1153,7 +1153,7 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, bus->sdcnt.fc_rcvd++; bus->flowcontrol = fc; } - tx_seq_max = SDPCM_WINDOW_VALUE(&header[SDPCM_FRAMETAG_LEN]); + tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT; if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) { brcmf_err("seq %d: max tx seq number error\n", rx_seq); tx_seq_max = bus->tx_seq + 2; @@ -1163,18 +1163,40 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, return 0; } +static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length) +{ + *(__le16 *)header = cpu_to_le16(frm_length); + *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length); +} + +static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header, + struct brcmf_sdio_hdrinfo *hd_info) +{ + u32 sw_header; + + brcmf_sdio_update_hwhdr(header, hd_info->len); + + sw_header = bus->tx_seq; + sw_header |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & + SDPCM_CHANNEL_MASK; + sw_header |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & + SDPCM_DOFFSET_MASK; + *(((__le32 *)header) + 1) = cpu_to_le32(sw_header); + *(((__le32 *)header) + 2) = 0; +} + static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) { u16 dlen, totlen; u8 *dptr, num = 0; - + u32 align = 0; u16 sublen; struct sk_buff *pfirst, *pnext; int errcode; u8 doff, sfdoff; - struct brcmf_sdio_read rd_new; + struct brcmf_sdio_hdrinfo rd_new; /* If packets, issue read(s) and send up packet chain */ /* Return sequence numbers consumed? */ @@ -1182,6 +1204,11 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) brcmf_dbg(SDIO, "start: glomd %p glom %p\n", bus->glomd, skb_peek(&bus->glom)); + if (bus->sdiodev->pdata) + align = bus->sdiodev->pdata->sd_sgentry_align; + if (align < 4) + align = 4; + /* If there's a descriptor, generate the packet chain */ if (bus->glomd) { pfirst = pnext = NULL; @@ -1205,9 +1232,9 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) pnext = NULL; break; } - if (sublen % BRCMF_SDALIGN) { + if (sublen % align) { brcmf_err("sublen %d not multiple of %d\n", - sublen, BRCMF_SDALIGN); + sublen, align); } totlen += sublen; @@ -1220,7 +1247,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } /* Allocate/chain packet for next subframe */ - pnext = brcmu_pkt_buf_get_skb(sublen + BRCMF_SDALIGN); + pnext = brcmu_pkt_buf_get_skb(sublen + align); if (pnext == NULL) { brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n", num, sublen); @@ -1229,7 +1256,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) skb_queue_tail(&bus->glom, pnext); /* Adhere to start alignment requirements */ - pkt_align(pnext, sublen, BRCMF_SDALIGN); + pkt_align(pnext, sublen, align); } /* If all allocations succeeded, save packet chain @@ -1305,8 +1332,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) rd_new.seq_num = rxseq; rd_new.len = dlen; sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdio_hdparser(bus, pfirst->data, &rd_new, - BRCMF_SDIO_FT_SUPER); + errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new, + BRCMF_SDIO_FT_SUPER); sdio_release_host(bus->sdiodev->func[1]); bus->cur_read.len = rd_new.len_nxtfrm << 4; @@ -1324,8 +1351,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) rd_new.len = pnext->len; rd_new.seq_num = rxseq++; sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdio_hdparser(bus, pnext->data, &rd_new, - BRCMF_SDIO_FT_SUB); + errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new, + BRCMF_SDIO_FT_SUB); sdio_release_host(bus->sdiodev->func[1]); brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), pnext->data, 32, "subframe:\n"); @@ -1357,7 +1384,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) skb_queue_walk_safe(&bus->glom, pfirst, pnext) { dptr = (u8 *) (pfirst->data); sublen = get_unaligned_le16(dptr); - doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); + doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]); brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), dptr, pfirst->len, @@ -1535,7 +1562,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) uint rxleft = 0; /* Remaining number of frames allowed */ int ret; /* Return code from calls */ uint rxcount = 0; /* Total frames read */ - struct brcmf_sdio_read *rd = &bus->cur_read, rd_new; + struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new; u8 head_read = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -1583,8 +1610,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) bus->rxhdr, SDPCM_HDRLEN, "RxHdr:\n"); - if (brcmf_sdio_hdparser(bus, bus->rxhdr, rd, - BRCMF_SDIO_FT_NORMAL)) { + if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd, + BRCMF_SDIO_FT_NORMAL)) { sdio_release_host(bus->sdiodev->func[1]); if (!bus->rxpending) break; @@ -1648,8 +1675,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); rd_new.seq_num = rd->seq_num; sdio_claim_host(bus->sdiodev->func[1]); - if (brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new, - BRCMF_SDIO_FT_NORMAL)) { + if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new, + BRCMF_SDIO_FT_NORMAL)) { rd->len = 0; brcmu_pkt_buf_free_skb(pkt); } @@ -1693,7 +1720,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) /* Save superframe descriptor and allocate packet frame */ if (rd->channel == SDPCM_GLOM_CHANNEL) { - if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) { + if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) { brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n", rd->len); brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), @@ -1759,85 +1786,168 @@ brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) return; } +/* flag marking a dummy skb added for DMA alignment requirement */ +#define DUMMY_SKB_FLAG 0x10000 +/* bit mask of data length chopped from the previous packet */ +#define DUMMY_SKB_CHOP_LEN_MASK 0xffff +/** + * brcmf_sdio_txpkt_prep - packet preparation for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * @chan: virtual channel to transmit the packet + * + * Processes to be applied to the packet + * - Align data buffer pointer + * - Align data buffer length + * - Prepare header + * Return: negative value if there is error + */ +static int +brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + uint chan) +{ + u16 head_pad, tail_pad, tail_chop, head_align, sg_align; + int ntail; + struct sk_buff *pkt_next, *pkt_new; + u8 *dat_buf; + unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize; + struct brcmf_sdio_hdrinfo hd_info = {0}; + + /* SDIO ADMA requires at least 32 bit alignment */ + head_align = 4; + sg_align = 4; + if (bus->sdiodev->pdata) { + head_align = bus->sdiodev->pdata->sd_head_align > 4 ? + bus->sdiodev->pdata->sd_head_align : 4; + sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ? + bus->sdiodev->pdata->sd_sgentry_align : 4; + } + /* sg entry alignment should be a divisor of block size */ + WARN_ON(blksize % sg_align); + + pkt_next = pktq->next; + dat_buf = (u8 *)(pkt_next->data); + + /* Check head padding */ + head_pad = ((unsigned long)dat_buf % head_align); + if (head_pad) { + if (skb_headroom(pkt_next) < head_pad) { + bus->sdiodev->bus_if->tx_realloc++; + head_pad = 0; + if (skb_cow(pkt_next, head_pad)) + return -ENOMEM; + } + skb_push(pkt_next, head_pad); + dat_buf = (u8 *)(pkt_next->data); + memset(dat_buf, 0, head_pad + bus->tx_hdrlen); + } + + /* Check tail padding */ + pkt_new = NULL; + tail_chop = pkt_next->len % sg_align; + tail_pad = sg_align - tail_chop; + tail_pad += blksize - (pkt_next->len + tail_pad) % blksize; + if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) { + pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop); + if (pkt_new == NULL) + return -ENOMEM; + memcpy(pkt_new->data, + pkt_next->data + pkt_next->len - tail_chop, + tail_chop); + *(u32 *)(pkt_new->cb) = DUMMY_SKB_FLAG + tail_chop; + skb_trim(pkt_next, pkt_next->len - tail_chop); + __skb_queue_after(pktq, pkt_next, pkt_new); + } else { + ntail = pkt_next->data_len + tail_pad - + (pkt_next->end - pkt_next->tail); + if (skb_cloned(pkt_next) || ntail > 0) + if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC)) + return -ENOMEM; + if (skb_linearize(pkt_next)) + return -ENOMEM; + dat_buf = (u8 *)(pkt_next->data); + __skb_put(pkt_next, tail_pad); + } + + /* Now prep the header */ + if (pkt_new) + hd_info.len = pkt_next->len + tail_chop; + else + hd_info.len = pkt_next->len - tail_pad; + hd_info.channel = chan; + hd_info.dat_offset = head_pad + bus->tx_hdrlen; + brcmf_sdio_hdpack(bus, dat_buf, &hd_info); + + if (BRCMF_BYTES_ON() && + ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || + (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) + brcmf_dbg_hex_dump(true, pkt_next, hd_info.len, "Tx Frame:\n"); + else if (BRCMF_HDRS_ON()) + brcmf_dbg_hex_dump(true, pkt_next, head_pad + bus->tx_hdrlen, + "Tx Header:\n"); + + return 0; +} + +/** + * brcmf_sdio_txpkt_postp - packet post processing for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * + * Processes to be applied to the packet + * - Remove head padding + * - Remove tail padding + */ +static void +brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) +{ + u8 *hdr; + u32 dat_offset; + u32 dummy_flags, chop_len; + struct sk_buff *pkt_next, *tmp, *pkt_prev; + + skb_queue_walk_safe(pktq, pkt_next, tmp) { + dummy_flags = *(u32 *)(pkt_next->cb); + if (dummy_flags & DUMMY_SKB_FLAG) { + chop_len = dummy_flags & DUMMY_SKB_CHOP_LEN_MASK; + if (chop_len) { + pkt_prev = pkt_next->prev; + memcpy(pkt_prev->data + pkt_prev->len, + pkt_next->data, chop_len); + skb_put(pkt_prev, chop_len); + } + __skb_unlink(pkt_next, pktq); + brcmu_pkt_buf_free_skb(pkt_next); + } else { + hdr = pkt_next->data + SDPCM_HWHDR_LEN; + dat_offset = le32_to_cpu(*(__le32 *)hdr); + dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >> + SDPCM_DOFFSET_SHIFT; + skb_pull(pkt_next, dat_offset); + } + } +} + /* Writes a HW/SW header into the packet and sends it. */ /* Assumes: (a) header space already there, (b) caller holds lock */ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, uint chan) { int ret; - u8 *frame; - u16 len, pad = 0; - u32 swheader; int i; + struct sk_buff_head localq; brcmf_dbg(TRACE, "Enter\n"); - frame = (u8 *) (pkt->data); - - /* Add alignment padding, allocate new packet if needed */ - pad = ((unsigned long)frame % BRCMF_SDALIGN); - if (pad) { - if (skb_headroom(pkt) < pad) { - brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n", - skb_headroom(pkt), pad); - bus->sdiodev->bus_if->tx_realloc++; - ret = skb_cow(pkt, BRCMF_SDALIGN); - if (ret) - goto done; - pad = ((unsigned long)frame % BRCMF_SDALIGN); - } - skb_push(pkt, pad); - frame = (u8 *) (pkt->data); - memset(frame, 0, pad + SDPCM_HDRLEN); - } - /* precondition: pad < BRCMF_SDALIGN */ - - /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ - len = (u16) (pkt->len); - *(__le16 *) frame = cpu_to_le16(len); - *(((__le16 *) frame) + 1) = cpu_to_le16(~len); - - /* Software tag: channel, sequence number, data offset */ - swheader = - ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | - (((pad + - SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); - - *(((__le32 *) frame) + 1) = cpu_to_le32(swheader); - *(((__le32 *) frame) + 2) = 0; - -#ifdef DEBUG - tx_packets[pkt->priority]++; -#endif - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && - ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || - (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)), - frame, len, "Tx Frame:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && - ((BRCMF_CTL_ON() && - chan == SDPCM_CONTROL_CHANNEL) || - (BRCMF_DATA_ON() && - chan != SDPCM_CONTROL_CHANNEL))) && - BRCMF_HDRS_ON(), - frame, min_t(u16, len, 16), "TxHdr:\n"); - - /* Raise len to next SDIO block to eliminate tail command */ - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - u16 pad = bus->blocksize - (len % bus->blocksize); - if ((pad <= bus->roundup) && (pad < bus->blocksize)) - len += pad; - } else if (len % BRCMF_SDALIGN) { - len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN); - } - - /* Some controllers have trouble with odd bytes -- round to even */ - if (len & (ALIGNMENT - 1)) - len = roundup(len, ALIGNMENT); + __skb_queue_head_init(&localq); + __skb_queue_tail(&localq, pkt); + ret = brcmf_sdio_txpkt_prep(bus, &localq, chan); + if (ret) + goto done; sdio_claim_host(bus->sdiodev->func[1]); ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, pkt); + SDIO_FUNC_2, F2SYNC, &localq); bus->sdcnt.f2txdata++; if (ret < 0) { @@ -1865,11 +1975,11 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, } sdio_release_host(bus->sdiodev->func[1]); if (ret == 0) - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; done: - /* restore pkt buffer pointer before calling tx complete routine */ - skb_pull(pkt, SDPCM_HDRLEN + pad); + brcmf_sdio_txpkt_postp(bus, &localq); + __skb_dequeue_tail(&localq); brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0); return ret; } @@ -1880,7 +1990,6 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) u32 intstatus = 0; int ret = 0, prec_out; uint cnt = 0; - uint datalen; u8 tx_prec_map; brcmf_dbg(TRACE, "Enter\n"); @@ -1896,7 +2005,6 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) break; } spin_unlock_bh(&bus->txqlock); - datalen = pkt->len - SDPCM_HDRLEN; ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL); @@ -2221,7 +2329,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) } } else { - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; } sdio_release_host(bus->sdiodev->func[1]); bus->ctrl_frame_stat = false; @@ -2276,13 +2384,14 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; + ulong flags; brcmf_dbg(TRACE, "Enter\n"); datalen = pkt->len; /* Add space for the header */ - skb_push(pkt, SDPCM_HDRLEN); + skb_push(pkt, bus->tx_hdrlen); /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */ prec = prio2prec((pkt->priority & PRIOMASK)); @@ -2293,10 +2402,9 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->sdcnt.fcqueued++; /* Priority based enq */ - spin_lock_bh(&bus->txqlock); + spin_lock_irqsave(&bus->txqlock, flags); if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { - skb_pull(pkt, SDPCM_HDRLEN); - brcmf_txcomplete(bus->sdiodev->dev, pkt, false); + skb_pull(pkt, bus->tx_hdrlen); brcmf_err("out of bus->txq !!!\n"); ret = -ENOSR; } else { @@ -2307,7 +2415,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->txoff = true; brcmf_txflowblock(bus->sdiodev->dev, true); } - spin_unlock_bh(&bus->txqlock); + spin_unlock_irqrestore(&bus->txqlock, flags); #ifdef DEBUG if (pktq_plen(&bus->txq, prec) > qcount[prec]) @@ -2436,7 +2544,7 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) return ret; } - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; return ret; } @@ -2446,19 +2554,19 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) { u8 *frame; u16 len; - u32 swheader; uint retries = 0; u8 doff = 0; int ret = -1; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; + struct brcmf_sdio_hdrinfo hd_info = {0}; brcmf_dbg(TRACE, "Enter\n"); /* Back the pointer to make a room for bus header */ - frame = msg - SDPCM_HDRLEN; - len = (msglen += SDPCM_HDRLEN); + frame = msg - bus->tx_hdrlen; + len = (msglen += bus->tx_hdrlen); /* Add alignment padding (optional for ctl frames) */ doff = ((unsigned long)frame % BRCMF_SDALIGN); @@ -2466,10 +2574,10 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) frame -= doff; len += doff; msglen += doff; - memset(frame, 0, doff + SDPCM_HDRLEN); + memset(frame, 0, doff + bus->tx_hdrlen); } /* precondition: doff < BRCMF_SDALIGN */ - doff += SDPCM_HDRLEN; + doff += bus->tx_hdrlen; /* Round send length to next SDIO block */ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { @@ -2491,18 +2599,10 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) brcmf_sdbrcm_bus_sleep(bus, false, false); sdio_release_host(bus->sdiodev->func[1]); - /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ - *(__le16 *) frame = cpu_to_le16((u16) msglen); - *(((__le16 *) frame) + 1) = cpu_to_le16(~msglen); - - /* Software tag: channel, sequence number, data offset */ - swheader = - ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & - SDPCM_CHANNEL_MASK) - | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & - SDPCM_DOFFSET_MASK); - put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN); - put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); + hd_info.len = (u16)msglen; + hd_info.channel = SDPCM_CONTROL_CHANNEL; + hd_info.dat_offset = doff; + brcmf_sdio_hdpack(bus, frame, &hd_info); if (!data_ok(bus)) { brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", @@ -3733,7 +3833,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) struct brcmf_sdio *bus; struct brcmf_bus_dcmd *dlst; u32 dngl_txglom; - u32 dngl_txglomalign; + u32 txglomalign = 0; u8 idx; brcmf_dbg(TRACE, "Enter\n"); @@ -3752,7 +3852,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) bus->txbound = BRCMF_TXBOUND; bus->rxbound = BRCMF_RXBOUND; bus->txminmax = BRCMF_TXMINMAX; - bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1; + bus->tx_seq = SDPCM_SEQ_WRAP - 1; INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); @@ -3794,8 +3894,11 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) bus->sdiodev->bus_if->chip = bus->ci->chip; bus->sdiodev->bus_if->chiprev = bus->ci->chiprev; - /* Attach to the brcmf/OS/network interface */ - ret = brcmf_attach(SDPCM_RESERVE, bus->sdiodev->dev); + /* default sdio bus header length for tx packet */ + bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; + + /* Attach to the common layer, reserve hdr space */ + ret = brcmf_attach(bus->tx_hdrlen, bus->sdiodev->dev); if (ret != 0) { brcmf_err("brcmf_attach failed\n"); goto fail; @@ -3827,9 +3930,13 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) dlst->param_len = sizeof(u32); } else { /* otherwise, set txglomalign */ - dngl_txglomalign = bus->sdiodev->bus_if->align; + if (sdiodev->pdata) + txglomalign = sdiodev->pdata->sd_sgentry_align; + /* SDIO ADMA requires at least 32 bit alignment */ + if (txglomalign < 4) + txglomalign = 4; dlst->name = "bus:txglomalign"; - dlst->param = (char *)&dngl_txglomalign; + dlst->param = (char *)&txglomalign; dlst->param_len = sizeof(u32); } list_add(&dlst->list, &bus->sdiodev->bus_if->dcmd_list); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 83ee53a..fad77dd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -185,6 +185,10 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, ifevent->action, ifevent->ifidx, ifevent->bssidx, ifevent->flags, ifevent->role); + if (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) { + brcmf_dbg(EVENT, "event can be ignored\n"); + return; + } if (ifevent->ifidx >= BRCMF_MAX_IFS) { brcmf_err("invalid interface index: %u\n", ifevent->ifidx); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 665ef69..ecabb04 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -69,4 +69,25 @@ struct brcmf_fil_bss_enable_le { __le32 enable; }; +/** + * struct tdls_iovar - common structure for tdls iovars. + * + * @ea: ether address of peer station. + * @mode: mode value depending on specific tdls iovar. + * @chanspec: channel specification. + * @pad: unused (for future use). + */ +struct brcmf_tdls_iovar_le { + u8 ea[ETH_ALEN]; /* Station address */ + u8 mode; /* mode: depends on iovar */ + __le16 chanspec; + __le32 pad; /* future */ +}; + +enum brcmf_tdls_manual_ep_ops { + BRCMF_TDLS_MANUAL_EP_CREATE = 1, + BRCMF_TDLS_MANUAL_EP_DELETE = 3, + BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6 +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 29b1f24..82f9140 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -422,9 +422,12 @@ struct brcmf_fws_macdesc_table { struct brcmf_fws_info { struct brcmf_pub *drvr; + spinlock_t spinlock; + ulong flags; struct brcmf_fws_stats stats; struct brcmf_fws_hanger hanger; enum brcmf_fws_fcmode fcmode; + bool fw_signals; bool bcmc_credit_check; struct brcmf_fws_macdesc_table desc; struct workqueue_struct *fws_wq; @@ -483,6 +486,18 @@ static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws, } #undef BRCMF_FWS_TLV_DEF +static void brcmf_fws_lock(struct brcmf_fws_info *fws) + __acquires(&fws->spinlock) +{ + spin_lock_irqsave(&fws->spinlock, fws->flags); +} + +static void brcmf_fws_unlock(struct brcmf_fws_info *fws) + __releases(&fws->spinlock) +{ + spin_unlock_irqrestore(&fws->spinlock, fws->flags); +} + static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg) { u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); @@ -869,8 +884,11 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, skcb->state = BRCMF_FWS_SKBSTATE_TIM; bus = fws->drvr->bus_if; err = brcmf_fws_hdrpush(fws, skb); - if (err == 0) + if (err == 0) { + brcmf_fws_unlock(fws); err = brcmf_bus_txdata(bus, skb); + brcmf_fws_lock(fws); + } if (err) brcmu_pkt_buf_free_skb(skb); return true; @@ -905,26 +923,10 @@ static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) return 0; } -/* using macro so sparse checking does not complain - * about locking imbalance. - */ -#define brcmf_fws_lock(drvr, flags) \ -do { \ - flags = 0; \ - spin_lock_irqsave(&((drvr)->fws_spinlock), (flags)); \ -} while (0) - -/* using macro so sparse checking does not complain - * about locking imbalance. - */ -#define brcmf_fws_unlock(drvr, flags) \ - spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags)) - static int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) { struct brcmf_fws_mac_descriptor *entry, *existing; - ulong flags; u8 mac_handle; u8 ifidx; u8 *addr; @@ -938,10 +940,10 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) if (entry->occupied) { brcmf_dbg(TRACE, "deleting %s mac %pM\n", entry->name, addr); - brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_lock(fws); brcmf_fws_macdesc_cleanup(fws, entry, -1); brcmf_fws_macdesc_deinit(entry); - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); } else fws->stats.mac_update_failed++; return 0; @@ -950,13 +952,13 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) existing = brcmf_fws_macdesc_lookup(fws, addr); if (IS_ERR(existing)) { if (!entry->occupied) { - brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_lock(fws); entry->mac_handle = mac_handle; brcmf_fws_macdesc_init(entry, addr, ifidx); brcmf_fws_macdesc_set_name(fws, entry); brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, BRCMF_FWS_PSQ_LEN); - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr); } else { fws->stats.mac_update_failed++; @@ -964,13 +966,13 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) } else { if (entry != existing) { brcmf_dbg(TRACE, "copy mac %s\n", existing->name); - brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_lock(fws); memcpy(entry, existing, offsetof(struct brcmf_fws_mac_descriptor, psq)); entry->mac_handle = mac_handle; brcmf_fws_macdesc_deinit(existing); brcmf_fws_macdesc_set_name(fws, entry); - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name, addr); } else { @@ -986,7 +988,6 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) { struct brcmf_fws_mac_descriptor *entry; - ulong flags; u8 mac_handle; int ret; @@ -996,7 +997,7 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, fws->stats.mac_ps_update_failed++; return -ESRCH; } - brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_lock(fws); /* a state update should wipe old credits */ entry->requested_credit = 0; entry->requested_packet = 0; @@ -1011,7 +1012,7 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true); ret = BRCMF_FWS_RET_OK_NOSCHEDULE; } - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); return ret; } @@ -1019,7 +1020,6 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) { struct brcmf_fws_mac_descriptor *entry; - ulong flags; u8 ifidx; int ret; @@ -1038,7 +1038,7 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type, entry->name); - brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_lock(fws); switch (type) { case BRCMF_FWS_TYPE_INTERFACE_OPEN: entry->state = BRCMF_FWS_STATE_OPEN; @@ -1050,10 +1050,10 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, break; default: ret = -EINVAL; - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); goto fail; } - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); return ret; fail: @@ -1065,7 +1065,6 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) { struct brcmf_fws_mac_descriptor *entry; - ulong flags; entry = &fws->desc.nodes[data[1] & 0x1F]; if (!entry->occupied) { @@ -1079,14 +1078,14 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n", brcmf_fws_get_tlv_name(type), type, entry->name, data[0], data[2]); - brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_lock(fws); if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT) entry->requested_credit = data[0]; else entry->requested_packet = data[0]; entry->ac_bitmap = data[2]; - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); return BRCMF_FWS_RET_OK_SCHEDULE; } @@ -1160,7 +1159,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws) { /* only schedule dequeue when there are credits for delayed traffic */ - if (fws->fifo_credit_map & fws->fifo_delay_map) + if ((fws->fifo_credit_map & fws->fifo_delay_map) || + (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map)) queue_work(fws->fws_wq, &fws->fws_dequeue_work); } @@ -1383,7 +1383,6 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, u8 *data) { - ulong flags; int i; if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) { @@ -1392,19 +1391,18 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, } brcmf_dbg(DATA, "enter: data %pM\n", data); - brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_lock(fws); for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++) brcmf_fws_return_credits(fws, i, data[i]); brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map, fws->fifo_delay_map); - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); return BRCMF_FWS_RET_OK_SCHEDULE; } static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) { - ulong lflags; __le32 status_le; u32 status; u32 hslot; @@ -1418,9 +1416,9 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) hslot = brcmf_txstatus_get_field(status, HSLOT); genbit = brcmf_txstatus_get_field(status, GENERATION); - brcmf_fws_lock(fws->drvr, lflags); + brcmf_fws_lock(fws); brcmf_fws_txs_process(fws, flags, hslot, genbit); - brcmf_fws_unlock(fws->drvr, lflags); + brcmf_fws_unlock(fws); return BRCMF_FWS_RET_OK_NOSCHEDULE; } @@ -1440,7 +1438,6 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, { struct brcmf_fws_info *fws = ifp->drvr->fws; int i; - ulong flags; u8 *credits = data; if (e->datalen < BRCMF_FWS_FIFO_COUNT) { @@ -1453,7 +1450,7 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, fws->creditmap_received = true; brcmf_dbg(TRACE, "enter: credits %pM\n", credits); - brcmf_fws_lock(ifp->drvr, flags); + brcmf_fws_lock(fws); for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) { if (*credits) fws->fifo_credit_map |= 1 << i; @@ -1462,7 +1459,7 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, fws->fifo_credit[i] = *credits++; } brcmf_fws_schedule_deq(fws); - brcmf_fws_unlock(ifp->drvr, flags); + brcmf_fws_unlock(fws); return 0; } @@ -1471,18 +1468,18 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp, void *data) { struct brcmf_fws_info *fws = ifp->drvr->fws; - ulong flags; - brcmf_fws_lock(ifp->drvr, flags); + brcmf_fws_lock(fws); if (fws) fws->bcmc_credit_check = true; - brcmf_fws_unlock(ifp->drvr, flags); + brcmf_fws_unlock(fws); return 0; } int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, struct sk_buff *skb) { + struct brcmf_skb_reorder_data *rd; struct brcmf_fws_info *fws = drvr->fws; u8 *signal_data; s16 data_len; @@ -1497,8 +1494,10 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, WARN_ON(signal_len > skb->len); + if (!signal_len) + return 0; /* if flow control disabled, skip to packet data and leave */ - if (!signal_len || !drvr->fw_signals) { + if (!fws->fw_signals) { skb_pull(skb, signal_len); return 0; } @@ -1536,9 +1535,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, err = BRCMF_FWS_RET_OK_NOSCHEDULE; switch (type) { - case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: case BRCMF_FWS_TYPE_COMP_TXSTATUS: break; + case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: + rd = (struct brcmf_skb_reorder_data *)skb->cb; + rd->reorder = data; + break; case BRCMF_FWS_TYPE_MACDESC_ADD: case BRCMF_FWS_TYPE_MACDESC_DEL: brcmf_fws_macdesc_indicate(fws, type, data); @@ -1694,17 +1696,22 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, return PTR_ERR(entry); brcmf_fws_precommit_skb(fws, fifo, skb); + entry->transit_count++; + if (entry->suppressed) + entry->suppr_transit_count++; + brcmf_fws_unlock(fws); rc = brcmf_bus_txdata(bus, skb); + brcmf_fws_lock(fws); brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name, skcb->if_flags, skcb->htod, rc); if (rc < 0) { + entry->transit_count--; + if (entry->suppressed) + entry->suppr_transit_count--; brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); goto rollback; } - entry->transit_count++; - if (entry->suppressed) - entry->suppr_transit_count++; fws->stats.pkt2bus++; fws->stats.send_pkts[fifo]++; if (brcmf_skb_if_flags_get_field(skb, REQUESTED)) @@ -1741,11 +1748,11 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) struct brcmf_fws_info *fws = drvr->fws; struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); struct ethhdr *eh = (struct ethhdr *)(skb->data); - ulong flags; int fifo = BRCMF_FWS_FIFO_BCMC; bool multicast = is_multicast_ether_addr(eh->h_dest); bool pae = eh->h_proto == htons(ETH_P_PAE); + brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); /* determine the priority */ if (!skb->priority) skb->priority = cfg80211_classify8021d(skb); @@ -1754,14 +1761,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) if (pae) atomic_inc(&ifp->pend_8021x_cnt); - if (!brcmf_fws_fc_active(fws)) { - /* If the protocol uses a data header, apply it */ - brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb); - - /* Use bus module to send data frame */ - return brcmf_bus_txdata(drvr->bus_if, skb); - } - /* set control buffer information */ skcb->if_flags = 0; skcb->state = BRCMF_FWS_SKBSTATE_NEW; @@ -1769,7 +1768,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) if (!multicast) fifo = brcmf_fws_prio2fifo[skb->priority]; - brcmf_fws_lock(drvr, flags); + brcmf_fws_lock(fws); if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC) fws->borrow_defer_timestamp = jiffies + BRCMF_FWS_BORROW_DEFER_PERIOD; @@ -1789,7 +1788,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) } brcmu_pkt_buf_free_skb(skb); } - brcmf_fws_unlock(drvr, flags); + brcmf_fws_unlock(fws); return 0; } @@ -1809,7 +1808,7 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp) struct brcmf_fws_info *fws = ifp->drvr->fws; struct brcmf_fws_mac_descriptor *entry; - if (!ifp->ndev || !ifp->drvr->fw_signals) + if (!ifp->ndev) return; entry = &fws->desc.iface[ifp->ifidx]; @@ -1824,31 +1823,54 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp) void brcmf_fws_del_interface(struct brcmf_if *ifp) { struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; - ulong flags; if (!entry) return; - brcmf_fws_lock(ifp->drvr, flags); + brcmf_fws_lock(ifp->drvr->fws); ifp->fws_desc = NULL; brcmf_dbg(TRACE, "deleting %s\n", entry->name); brcmf_fws_macdesc_deinit(entry); brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx); - brcmf_fws_unlock(ifp->drvr, flags); + brcmf_fws_unlock(ifp->drvr->fws); } static void brcmf_fws_dequeue_worker(struct work_struct *worker) { struct brcmf_fws_info *fws; + struct brcmf_pub *drvr; struct sk_buff *skb; - ulong flags; int fifo; + u32 hslot; + u32 ifidx; + int ret; fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); + drvr = fws->drvr; - brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_lock(fws); for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked; fifo--) { + if (!brcmf_fws_fc_active(fws)) { + while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) { + hslot = brcmf_skb_htod_tag_get_field(skb, + HSLOT); + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, + &skb, true); + ifidx = brcmf_skb_if_flags_get_field(skb, + INDEX); + brcmf_proto_hdrpush(drvr, ifidx, 0, skb); + /* Use bus module to send data frame */ + brcmf_fws_unlock(fws); + ret = brcmf_bus_txdata(drvr->bus_if, skb); + brcmf_fws_lock(fws); + if (ret < 0) + brcmf_txfinalize(drvr, skb, false); + if (fws->bus_flow_blocked) + break; + } + continue; + } while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) && (fifo == BRCMF_FWS_FIFO_BCMC))) { skb = brcmf_fws_deq(fws, fifo); @@ -1876,42 +1898,43 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) } } } - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); } int brcmf_fws_init(struct brcmf_pub *drvr) { + struct brcmf_fws_info *fws; u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; int rc; - if (!drvr->fw_signals) - return 0; - - spin_lock_init(&drvr->fws_spinlock); - drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); if (!drvr->fws) { rc = -ENOMEM; goto fail; } + fws = drvr->fws; + + spin_lock_init(&fws->spinlock); + /* set linkage back */ - drvr->fws->drvr = drvr; - drvr->fws->fcmode = fcmode; + fws->drvr = drvr; + fws->fcmode = fcmode; - drvr->fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); - if (drvr->fws->fws_wq == NULL) { + fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); + if (fws->fws_wq == NULL) { brcmf_err("workqueue creation failed\n"); rc = -EBADF; goto fail; } - INIT_WORK(&drvr->fws->fws_dequeue_work, brcmf_fws_dequeue_worker); + INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker); /* enable firmware signalling if fcmode active */ - if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE) + if (fws->fcmode != BRCMF_FWS_FCMODE_NONE) tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS | BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS | - BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE; + BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE | + BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE; rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP, brcmf_fws_notify_credit_map); @@ -1927,31 +1950,33 @@ int brcmf_fws_init(struct brcmf_pub *drvr) goto fail; } - /* setting the iovar may fail if feature is unsupported + /* Setting the iovar may fail if feature is unsupported * so leave the rc as is so driver initialization can - * continue. + * continue. Set mode back to none indicating not enabled. */ + fws->fw_signals = true; if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) { brcmf_err("failed to set bdcv2 tlv signaling\n"); - goto fail_event; + fws->fcmode = BRCMF_FWS_FCMODE_NONE; + fws->fw_signals = false; } - brcmf_fws_hanger_init(&drvr->fws->hanger); - brcmf_fws_macdesc_init(&drvr->fws->desc.other, NULL, 0); - brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other); - brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, + if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1)) + brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n"); + + brcmf_fws_hanger_init(&fws->hanger); + brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0); + brcmf_fws_macdesc_set_name(fws, &fws->desc.other); + brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, BRCMF_FWS_PSQ_LEN); /* create debugfs file for statistics */ - brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); + brcmf_debugfs_create_fws_stats(drvr, &fws->stats); brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n", - drvr->fw_signals ? "enabled" : "disabled", tlv); + fws->fw_signals ? "enabled" : "disabled", tlv); return 0; -fail_event: - brcmf_fweh_unregister(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT); - brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP); fail: brcmf_fws_deinit(drvr); return rc; @@ -1960,24 +1985,18 @@ fail: void brcmf_fws_deinit(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws = drvr->fws; - ulong flags; if (!fws) return; - /* disable firmware signalling entirely - * to avoid using the workqueue. - */ - drvr->fw_signals = false; - if (drvr->fws->fws_wq) destroy_workqueue(drvr->fws->fws_wq); /* cleanup */ - brcmf_fws_lock(drvr, flags); + brcmf_fws_lock(fws); brcmf_fws_cleanup(fws, -1); drvr->fws = NULL; - brcmf_fws_unlock(drvr, flags); + brcmf_fws_unlock(fws); /* free top structure */ kfree(fws); @@ -1985,7 +2004,7 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr) bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) { - if (!fws) + if (!fws->creditmap_received) return false; return fws->fcmode != BRCMF_FWS_FCMODE_NONE; @@ -1993,17 +2012,16 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) { - ulong flags; u32 hslot; if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) { brcmu_pkt_buf_free_skb(skb); return; } - brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_lock(fws); hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0); - brcmf_fws_unlock(fws->drvr, flags); + brcmf_fws_unlock(fws); } void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 79555f0..d7a9745 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -1430,7 +1430,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, IEEE80211_BAND_5GHZ); wdev = &ifp->vif->wdev; - cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, + cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0, GFP_ATOMIC); kfree(mgmt_frame); @@ -1895,7 +1895,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ); - cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, + cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0, GFP_ATOMIC); brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n", diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 09786a5..2b5407f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -208,7 +208,7 @@ extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, */ extern int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt); + uint flags, struct sk_buff_head *pktq); extern int brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 322cadc..39e01a7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -614,7 +614,6 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) return 0; fail: - brcmf_txcomplete(dev, skb, false); return ret; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 7fa71f7..571f013 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3155,7 +3155,9 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, } #ifdef CONFIG_NL80211_TESTMODE -static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len) +static int brcmf_cfg80211_testmode(struct wiphy *wiphy, + struct wireless_dev *wdev, + void *data, int len) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); @@ -4126,6 +4128,53 @@ static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy, clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); } +static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) +{ + int ret; + + switch (oper) { + case NL80211_TDLS_DISCOVERY_REQ: + ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY; + break; + case NL80211_TDLS_SETUP: + ret = BRCMF_TDLS_MANUAL_EP_CREATE; + break; + case NL80211_TDLS_TEARDOWN: + ret = BRCMF_TDLS_MANUAL_EP_DELETE; + break; + default: + brcmf_err("unsupported operation: %d\n", oper); + ret = -EOPNOTSUPP; + } + return ret; +} + +static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, + struct net_device *ndev, u8 *peer, + enum nl80211_tdls_operation oper) +{ + struct brcmf_if *ifp; + struct brcmf_tdls_iovar_le info; + int ret = 0; + + ret = brcmf_convert_nl80211_tdls_oper(oper); + if (ret < 0) + return ret; + + ifp = netdev_priv(ndev); + memset(&info, 0, sizeof(info)); + info.mode = (u8)ret; + if (peer) + memcpy(info.ea, peer, ETH_ALEN); + + ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint", + &info, sizeof(info)); + if (ret < 0) + brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret); + + return ret; +} + static struct cfg80211_ops wl_cfg80211_ops = { .add_virtual_intf = brcmf_cfg80211_add_iface, .del_virtual_intf = brcmf_cfg80211_del_iface, @@ -4164,9 +4213,8 @@ static struct cfg80211_ops wl_cfg80211_ops = { .stop_p2p_device = brcmf_p2p_stop_device, .crit_proto_start = brcmf_cfg80211_crit_proto_start, .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, -#ifdef CONFIG_NL80211_TESTMODE - .testmode_cmd = brcmf_cfg80211_testmode -#endif + .tdls_oper = brcmf_cfg80211_tdls_oper, + CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode) }; static s32 brcmf_nl80211_iftype_to_mode(enum nl80211_iftype type) @@ -4287,7 +4335,8 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | WIPHY_FLAG_OFFCHAN_TX | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_SUPPORTS_TDLS; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; brcmf_wiphy_pno_params(wiphy); @@ -4908,6 +4957,12 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto cfg80211_p2p_attach_out; } + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); + if (err) { + brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); + wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; + } + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type); if (err) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c index e4fd1ee..5336597 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c @@ -679,27 +679,6 @@ bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode) return mode == BCMA_CLKMODE_FAST; } -void ai_pci_up(struct si_pub *sih) -{ - struct si_info *sii; - - sii = container_of(sih, struct si_info, pub); - - if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) - bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], true); -} - -/* Unconfigure and/or apply various WARs when going down */ -void ai_pci_down(struct si_pub *sih) -{ - struct si_info *sii; - - sii = container_of(sih, struct si_info, pub); - - if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) - bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], false); -} - /* Enable BT-COEX & Ex-PA for 4313 */ void ai_epa_4313war(struct si_pub *sih) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h index 89562c1..a8a267b 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h @@ -183,9 +183,6 @@ extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih); extern bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode); extern bool ai_deviceremoved(struct si_pub *sih); -extern void ai_pci_down(struct si_pub *sih); -extern void ai_pci_up(struct si_pub *sih); - /* Enable Ex-PA for 4313 */ extern void ai_epa_4313war(struct si_pub *sih); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c index bd98285..fa391e4 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c @@ -928,9 +928,9 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, } } else if (txs->phyerr) { update_rate = false; - brcms_err(wlc->hw->d11core, - "%s: ampdu tx phy error (0x%x)\n", - __func__, txs->phyerr); + brcms_dbg_ht(wlc->hw->d11core, + "%s: ampdu tx phy error (0x%x)\n", + __func__, txs->phyerr); } } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c index 1860c57..4fb9635 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c @@ -1015,9 +1015,10 @@ static bool dma64_txidle(struct dma_info *di) /* * post receive buffers - * return false is refill failed completely and ring is empty this will stall - * the rx dma and user might want to call rxfill again asap. This unlikely - * happens on memory-rich NIC, but often on memory-constrained dongle + * Return false if refill failed completely or dma mapping failed. The ring + * is empty, which will stall the rx dma and user might want to call rxfill + * again asap. This is unlikely to happen on a memory-rich NIC, but often on + * memory-constrained dongle. */ bool dma_rxfill(struct dma_pub *pub) { @@ -1078,6 +1079,8 @@ bool dma_rxfill(struct dma_pub *pub) pa = dma_map_single(di->dmadev, p->data, di->rxbufsize, DMA_FROM_DEVICE); + if (dma_mapping_error(di->dmadev, pa)) + return false; /* save the free packet pointer */ di->rxp[rxout] = p; @@ -1284,7 +1287,11 @@ static void dma_txenq(struct dma_info *di, struct sk_buff *p) /* get physical address of buffer start */ pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE); - + /* if mapping failed, free skb */ + if (dma_mapping_error(di->dmadev, pa)) { + brcmu_pkt_buf_free_skb(p); + return; + } /* With a DMA segment list, Descriptor table is filled * using the segment list instead of looping over * buffers in multi-chain DMA. Therefore, EOF for SGLIST diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 9fd6f2f..4608e0e 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -882,8 +882,8 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) mcl = le16_to_cpu(txh->MacTxControlLow); if (txs->phyerr) - brcms_err(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n", - txs->phyerr, txh->MainRates); + brcms_dbg_tx(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n", + txs->phyerr, txh->MainRates); if (txs->frameid != le16_to_cpu(txh->TxFrameID)) { brcms_err(wlc->hw->d11core, "frameid != txh->TxFrameID\n"); @@ -4652,7 +4652,9 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, wlc->band->phyrev = wlc_hw->band->phyrev; wlc->band->radioid = wlc_hw->band->radioid; wlc->band->radiorev = wlc_hw->band->radiorev; - + brcms_dbg_info(core, "wl%d: phy %u/%u radio %x/%u\n", unit, + wlc->band->phytype, wlc->band->phyrev, + wlc->band->radioid, wlc->band->radiorev); /* default contention windows size limits */ wlc_hw->band->CWmin = APHY_CWMIN; wlc_hw->band->CWmax = PHY_CWMAX; @@ -4667,7 +4669,7 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, brcms_c_coredisable(wlc_hw); /* Match driver "down" state */ - ai_pci_down(wlc_hw->sih); + bcma_core_pci_down(wlc_hw->d11core->bus); /* turn off pll and xtal to match driver "down" state */ brcms_b_xtal(wlc_hw, OFF); @@ -5010,12 +5012,12 @@ static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) */ if (brcms_b_radio_read_hwdisabled(wlc_hw)) { /* put SB PCI in down state again */ - ai_pci_down(wlc_hw->sih); + bcma_core_pci_down(wlc_hw->d11core->bus); brcms_b_xtal(wlc_hw, OFF); return -ENOMEDIUM; } - ai_pci_up(wlc_hw->sih); + bcma_core_pci_up(wlc_hw->d11core->bus); /* reset the d11 core */ brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); @@ -5212,7 +5214,7 @@ static int brcms_b_down_finish(struct brcms_hardware *wlc_hw) /* turn off primary xtal and pll */ if (!wlc_hw->noreset) { - ai_pci_down(wlc_hw->sih); + bcma_core_pci_down(wlc_hw->d11core->bus); brcms_b_xtal(wlc_hw, OFF); } } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c index 3d6b16c..b2d6d6d 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c @@ -1137,8 +1137,9 @@ wlc_lcnphy_set_rx_gain_by_distribution(struct brcms_phy *pi, gain0_15 = ((biq1 & 0xf) << 12) | ((tia & 0xf) << 8) | ((lna2 & 0x3) << 6) | - ((lna2 & - 0x3) << 4) | ((lna1 & 0x3) << 2) | ((lna1 & 0x3) << 0); + ((lna2 & 0x3) << 4) | + ((lna1 & 0x3) << 2) | + ((lna1 & 0x3) << 0); mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0); mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0); @@ -1328,6 +1329,43 @@ static u32 wlc_lcnphy_measure_digital_power(struct brcms_phy *pi, u16 nsamples) return (iq_est.i_pwr + iq_est.q_pwr) / nsamples; } +static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain, + u16 tia_gain, u16 lna2_gain) +{ + u32 i_thresh_l, q_thresh_l; + u32 i_thresh_h, q_thresh_h; + struct lcnphy_iq_est iq_est_h, iq_est_l; + + wlc_lcnphy_set_rx_gain_by_distribution(pi, 0, 0, 0, biq1_gain, tia_gain, + lna2_gain, 0); + + wlc_lcnphy_rx_gain_override_enable(pi, true); + wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0); + udelay(500); + write_radio_reg(pi, RADIO_2064_REG112, 0); + if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l)) + return false; + + wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0); + udelay(500); + write_radio_reg(pi, RADIO_2064_REG112, 0); + if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h)) + return false; + + i_thresh_l = (iq_est_l.i_pwr << 1); + i_thresh_h = (iq_est_l.i_pwr << 2) + iq_est_l.i_pwr; + + q_thresh_l = (iq_est_l.q_pwr << 1); + q_thresh_h = (iq_est_l.q_pwr << 2) + iq_est_l.q_pwr; + if ((iq_est_h.i_pwr > i_thresh_l) && + (iq_est_h.i_pwr < i_thresh_h) && + (iq_est_h.q_pwr > q_thresh_l) && + (iq_est_h.q_pwr < q_thresh_h)) + return true; + + return false; +} + static bool wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi, const struct lcnphy_rx_iqcomp *iqcomp, @@ -1342,8 +1380,8 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi, RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old, rfoverride3_old, rfoverride3val_old, rfoverride4_old, rfoverride4val_old, afectrlovr_old, afectrlovrval_old; - int tia_gain; - u32 received_power, rx_pwr_threshold; + int tia_gain, lna2_gain, biq1_gain; + bool set_gain; u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl; u16 values_to_save[11]; s16 *ptr; @@ -1368,126 +1406,125 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi, goto cal_done; } - if (module == 1) { + WARN_ON(module != 1); + tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); - tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); + for (i = 0; i < 11; i++) + values_to_save[i] = + read_radio_reg(pi, rxiq_cal_rf_reg[i]); + Core1TxControl_old = read_phy_reg(pi, 0x631); + + or_phy_reg(pi, 0x631, 0x0015); + + RFOverride0_old = read_phy_reg(pi, 0x44c); + RFOverrideVal0_old = read_phy_reg(pi, 0x44d); + rfoverride2_old = read_phy_reg(pi, 0x4b0); + rfoverride2val_old = read_phy_reg(pi, 0x4b1); + rfoverride3_old = read_phy_reg(pi, 0x4f9); + rfoverride3val_old = read_phy_reg(pi, 0x4fa); + rfoverride4_old = read_phy_reg(pi, 0x938); + rfoverride4val_old = read_phy_reg(pi, 0x939); + afectrlovr_old = read_phy_reg(pi, 0x43b); + afectrlovrval_old = read_phy_reg(pi, 0x43c); + old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); + old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db); - for (i = 0; i < 11; i++) - values_to_save[i] = - read_radio_reg(pi, rxiq_cal_rf_reg[i]); - Core1TxControl_old = read_phy_reg(pi, 0x631); - - or_phy_reg(pi, 0x631, 0x0015); - - RFOverride0_old = read_phy_reg(pi, 0x44c); - RFOverrideVal0_old = read_phy_reg(pi, 0x44d); - rfoverride2_old = read_phy_reg(pi, 0x4b0); - rfoverride2val_old = read_phy_reg(pi, 0x4b1); - rfoverride3_old = read_phy_reg(pi, 0x4f9); - rfoverride3val_old = read_phy_reg(pi, 0x4fa); - rfoverride4_old = read_phy_reg(pi, 0x938); - rfoverride4val_old = read_phy_reg(pi, 0x939); - afectrlovr_old = read_phy_reg(pi, 0x43b); - afectrlovrval_old = read_phy_reg(pi, 0x43c); - old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); - old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db); - - tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); - if (tx_gain_override_old) { - wlc_lcnphy_get_tx_gain(pi, &old_gains); - tx_gain_index_old = pi_lcn->lcnphy_current_index; - } + tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); + if (tx_gain_override_old) { + wlc_lcnphy_get_tx_gain(pi, &old_gains); + tx_gain_index_old = pi_lcn->lcnphy_current_index; + } - wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx); + wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx); - mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0); + mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0); - mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); + mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); - write_radio_reg(pi, RADIO_2064_REG116, 0x06); - write_radio_reg(pi, RADIO_2064_REG12C, 0x07); - write_radio_reg(pi, RADIO_2064_REG06A, 0xd3); - write_radio_reg(pi, RADIO_2064_REG098, 0x03); - write_radio_reg(pi, RADIO_2064_REG00B, 0x7); - mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4); - write_radio_reg(pi, RADIO_2064_REG01D, 0x01); - write_radio_reg(pi, RADIO_2064_REG114, 0x01); - write_radio_reg(pi, RADIO_2064_REG02E, 0x10); - write_radio_reg(pi, RADIO_2064_REG12A, 0x08); - - mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0); - mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2); - mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2); - mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3); - mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3); - mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5); - mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5); - - mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0); - - wlc_lcnphy_start_tx_tone(pi, 2000, 120, 0); - write_phy_reg(pi, 0x6da, 0xffff); - or_phy_reg(pi, 0x6db, 0x3); - wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch); - wlc_lcnphy_rx_gain_override_enable(pi, true); - - tia_gain = 8; - rx_pwr_threshold = 950; - while (tia_gain > 0) { - tia_gain -= 1; - wlc_lcnphy_set_rx_gain_by_distribution(pi, - 0, 0, 2, 2, - (u16) - tia_gain, 1, 0); - udelay(500); + write_radio_reg(pi, RADIO_2064_REG116, 0x06); + write_radio_reg(pi, RADIO_2064_REG12C, 0x07); + write_radio_reg(pi, RADIO_2064_REG06A, 0xd3); + write_radio_reg(pi, RADIO_2064_REG098, 0x03); + write_radio_reg(pi, RADIO_2064_REG00B, 0x7); + mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4); + write_radio_reg(pi, RADIO_2064_REG01D, 0x01); + write_radio_reg(pi, RADIO_2064_REG114, 0x01); + write_radio_reg(pi, RADIO_2064_REG02E, 0x10); + write_radio_reg(pi, RADIO_2064_REG12A, 0x08); + + mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0); + mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2); + mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2); + mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3); + mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3); + mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5); + mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5); - received_power = - wlc_lcnphy_measure_digital_power(pi, 2000); - if (received_power < rx_pwr_threshold) - break; - } - result = wlc_lcnphy_calc_rx_iq_comp(pi, 0xffff); + mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0); - wlc_lcnphy_stop_tx_tone(pi); + write_phy_reg(pi, 0x6da, 0xffff); + or_phy_reg(pi, 0x6db, 0x3); + + wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch); + for (lna2_gain = 3; lna2_gain >= 0; lna2_gain--) { + for (tia_gain = 4; tia_gain >= 0; tia_gain--) { + for (biq1_gain = 6; biq1_gain >= 0; biq1_gain--) { + set_gain = wlc_lcnphy_rx_iq_cal_gain(pi, + (u16) + biq1_gain, + (u16) + tia_gain, + (u16) + lna2_gain); + if (!set_gain) + continue; + + result = wlc_lcnphy_calc_rx_iq_comp(pi, 1024); + goto stop_tone; + } + } + } - write_phy_reg(pi, 0x631, Core1TxControl_old); +stop_tone: + wlc_lcnphy_stop_tx_tone(pi); - write_phy_reg(pi, 0x44c, RFOverrideVal0_old); - write_phy_reg(pi, 0x44d, RFOverrideVal0_old); - write_phy_reg(pi, 0x4b0, rfoverride2_old); - write_phy_reg(pi, 0x4b1, rfoverride2val_old); - write_phy_reg(pi, 0x4f9, rfoverride3_old); - write_phy_reg(pi, 0x4fa, rfoverride3val_old); - write_phy_reg(pi, 0x938, rfoverride4_old); - write_phy_reg(pi, 0x939, rfoverride4val_old); - write_phy_reg(pi, 0x43b, afectrlovr_old); - write_phy_reg(pi, 0x43c, afectrlovrval_old); - write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl); - write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl); + write_phy_reg(pi, 0x631, Core1TxControl_old); + + write_phy_reg(pi, 0x44c, RFOverrideVal0_old); + write_phy_reg(pi, 0x44d, RFOverrideVal0_old); + write_phy_reg(pi, 0x4b0, rfoverride2_old); + write_phy_reg(pi, 0x4b1, rfoverride2val_old); + write_phy_reg(pi, 0x4f9, rfoverride3_old); + write_phy_reg(pi, 0x4fa, rfoverride3val_old); + write_phy_reg(pi, 0x938, rfoverride4_old); + write_phy_reg(pi, 0x939, rfoverride4val_old); + write_phy_reg(pi, 0x43b, afectrlovr_old); + write_phy_reg(pi, 0x43c, afectrlovrval_old); + write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl); + write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl); - wlc_lcnphy_clear_trsw_override(pi); + wlc_lcnphy_clear_trsw_override(pi); - mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2); + mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2); - for (i = 0; i < 11; i++) - write_radio_reg(pi, rxiq_cal_rf_reg[i], - values_to_save[i]); + for (i = 0; i < 11; i++) + write_radio_reg(pi, rxiq_cal_rf_reg[i], + values_to_save[i]); - if (tx_gain_override_old) - wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old); - else - wlc_lcnphy_disable_tx_gain_override(pi); + if (tx_gain_override_old) + wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old); + else + wlc_lcnphy_disable_tx_gain_override(pi); - wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl); - wlc_lcnphy_rx_gain_override_enable(pi, false); - } + wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl); + wlc_lcnphy_rx_gain_override_enable(pi, false); cal_done: kfree(ptr); @@ -1789,6 +1826,19 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel) write_radio_reg(pi, RADIO_2064_REG038, 3); write_radio_reg(pi, RADIO_2064_REG091, 7); } + + if (!(pi->sh->boardflags & BFL_FEM)) { + static const u8 reg038[14] = { + 0xd, 0xe, 0xd, 0xd, 0xd, 0xc, 0xa, + 0xb, 0xb, 0x3, 0x3, 0x2, 0x0, 0x0 + }; + + write_radio_reg(pi, RADIO_2064_REG02A, 0xf); + write_radio_reg(pi, RADIO_2064_REG091, 0x3); + write_radio_reg(pi, RADIO_2064_REG038, 0x3); + + write_radio_reg(pi, RADIO_2064_REG038, reg038[channel - 1]); + } } static int @@ -1983,6 +2033,16 @@ wlc_lcnphy_set_tssi_mux(struct brcms_phy *pi, enum lcnphy_tssi_mode pos) } else { mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0x1); mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); + mod_radio_reg(pi, RADIO_2064_REG028, 0x1, 0x0); + mod_radio_reg(pi, RADIO_2064_REG11A, 0x4, 1<<2); + mod_radio_reg(pi, RADIO_2064_REG036, 0x10, 0x0); + mod_radio_reg(pi, RADIO_2064_REG11A, 0x10, 1<<4); + mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0); + mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x77); + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0xe<<1); + mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1<<7); + mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 1<<1); + mod_radio_reg(pi, RADIO_2064_REG029, 0xf0, 0<<4); } } else { mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0x1) << 2); @@ -2069,13 +2129,23 @@ static void wlc_lcnphy_pwrctrl_rssiparams(struct brcms_phy *pi) (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12)); mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5)); + mod_radio_reg(pi, RADIO_2064_REG07C, (1 << 0), (1 << 0)); } static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi) { struct phytbl_info tab; u32 rfseq, ind; + enum lcnphy_tssi_mode mode; + u8 tssi_sel; + if (pi->sh->boardflags & BFL_FEM) { + tssi_sel = 0x1; + mode = LCNPHY_TSSI_EXT; + } else { + tssi_sel = 0xe; + mode = LCNPHY_TSSI_POST_PA; + } tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_ptr = &ind; @@ -2096,7 +2166,7 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi) mod_phy_reg(pi, 0x503, (0x1 << 4), (1) << 4); - wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT); + wlc_lcnphy_set_tssi_mux(pi, mode); mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14); mod_phy_reg(pi, 0x4a4, (0x1 << 15), (1) << 15); @@ -2132,9 +2202,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi) mod_phy_reg(pi, 0x49a, (0x1ff << 0), (0xff) << 0); if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { - mod_radio_reg(pi, RADIO_2064_REG028, 0xf, 0xe); + mod_radio_reg(pi, RADIO_2064_REG028, 0xf, tssi_sel); mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); } else { + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, tssi_sel << 1); mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1); mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 1 << 3); } @@ -2181,6 +2252,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi) mod_phy_reg(pi, 0x4d7, (0xf << 8), (0) << 8); + mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x0); + mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0); + mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); + wlc_lcnphy_pwrctrl_rssiparams(pi); } @@ -2799,6 +2874,8 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi) read_radio_reg(pi, RADIO_2064_REG007) & 1; u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10; u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4; + u8 SAVE_bbmult = wlc_lcnphy_get_bbmult(pi); + idleTssi = read_phy_reg(pi, 0x4ab); suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC)); @@ -2816,6 +2893,12 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi) mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 1 << 4); mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 1 << 2); wlc_lcnphy_tssi_setup(pi); + + mod_phy_reg(pi, 0x4d7, (0x1 << 0), (1 << 0)); + mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1 << 6)); + + wlc_lcnphy_set_bbmult(pi, 0x0); + wlc_phy_do_dummy_tx(pi, true, OFF); idleTssi = ((read_phy_reg(pi, 0x4ab) & (0x1ff << 0)) >> 0); @@ -2837,6 +2920,7 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi) mod_phy_reg(pi, 0x44c, (0x1 << 12), (0) << 12); + wlc_lcnphy_set_bbmult(pi, SAVE_bbmult); wlc_lcnphy_set_tx_gain_override(pi, tx_gain_override_old); wlc_lcnphy_set_tx_gain(pi, &old_gains); wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); @@ -3050,6 +3134,11 @@ static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi) wlc_lcnphy_write_table(pi, &tab); tab.tbl_offset++; } + mod_phy_reg(pi, 0x4d0, (0x1 << 0), (0) << 0); + mod_phy_reg(pi, 0x4d3, (0xff << 0), (0) << 0); + mod_phy_reg(pi, 0x4d3, (0xff << 8), (0) << 8); + mod_phy_reg(pi, 0x4d0, (0x1 << 4), (0) << 4); + mod_phy_reg(pi, 0x4d0, (0x1 << 2), (0) << 2); mod_phy_reg(pi, 0x410, (0x1 << 7), (0) << 7); @@ -3851,7 +3940,6 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi) target_gains.pad_gain = 21; target_gains.dac_gain = 0; wlc_lcnphy_set_tx_gain(pi, &target_gains); - wlc_lcnphy_set_tx_pwr_by_index(pi, 16); if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) { @@ -3862,6 +3950,7 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi) lcnphy_recal ? LCNPHY_CAL_RECAL : LCNPHY_CAL_FULL), false); } else { + wlc_lcnphy_set_tx_pwr_by_index(pi, 16); wlc_lcnphy_tx_iqlo_soft_cal_full(pi); } @@ -4283,20 +4372,20 @@ wlc_lcnphy_load_tx_gain_table(struct brcms_phy *pi, u16 pa_gain; u16 gm_gain; - if (CHSPEC_IS5G(pi->radio_chanspec)) - pa_gain = 0x70; - else - pa_gain = 0x70; - if (pi->sh->boardflags & BFL_FEM) pa_gain = 0x10; + else + pa_gain = 0x60; tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; tab.tbl_width = 32; tab.tbl_len = 1; tab.tbl_ptr = &val; + /* fixed gm_gain value for iPA */ + gm_gain = 15; for (j = 0; j < 128; j++) { - gm_gain = gain_table[j].gm; + if (pi->sh->boardflags & BFL_FEM) + gm_gain = gain_table[j].gm; val = (((u32) pa_gain << 24) | (gain_table[j].pad << 16) | (gain_table[j].pga << 8) | gm_gain); @@ -4507,7 +4596,10 @@ static void wlc_radio_2064_init(struct brcms_phy *pi) write_phy_reg(pi, 0x4ea, 0x4688); - mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0); + if (pi->sh->boardflags & BFL_FEM) + mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0); + else + mod_phy_reg(pi, 0x4eb, (0x7 << 0), 3 << 0); mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6); @@ -4518,6 +4610,13 @@ static void wlc_radio_2064_init(struct brcms_phy *pi) wlc_lcnphy_rcal(pi); wlc_lcnphy_rc_cal(pi); + + if (!(pi->sh->boardflags & BFL_FEM)) { + write_radio_reg(pi, RADIO_2064_REG032, 0x6f); + write_radio_reg(pi, RADIO_2064_REG033, 0x19); + write_radio_reg(pi, RADIO_2064_REG039, 0xe); + } + } static void wlc_lcnphy_radio_init(struct brcms_phy *pi) @@ -4530,6 +4629,7 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi) uint idx; u8 phybw40; struct phytbl_info tab; + const struct phytbl_info *tb; u32 val; phybw40 = CHSPEC_IS40(pi->radio_chanspec); @@ -4547,22 +4647,20 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi) wlc_lcnphy_write_table(pi, &tab); } - tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; - tab.tbl_width = 16; - tab.tbl_ptr = &val; - tab.tbl_len = 1; - - val = 114; - tab.tbl_offset = 0; - wlc_lcnphy_write_table(pi, &tab); + if (!(pi->sh->boardflags & BFL_FEM)) { + tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; + tab.tbl_width = 16; + tab.tbl_ptr = &val; + tab.tbl_len = 1; - val = 130; - tab.tbl_offset = 1; - wlc_lcnphy_write_table(pi, &tab); + val = 150; + tab.tbl_offset = 0; + wlc_lcnphy_write_table(pi, &tab); - val = 6; - tab.tbl_offset = 8; - wlc_lcnphy_write_table(pi, &tab); + val = 220; + tab.tbl_offset = 1; + wlc_lcnphy_write_table(pi, &tab); + } if (CHSPEC_IS2G(pi->radio_chanspec)) { if (pi->sh->boardflags & BFL_FEM) @@ -4576,7 +4674,6 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi) } if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { - const struct phytbl_info *tb; int l; if (CHSPEC_IS2G(pi->radio_chanspec)) { @@ -4597,21 +4694,22 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi) wlc_lcnphy_write_table(pi, &tb[idx]); } - if ((pi->sh->boardflags & BFL_FEM) - && !(pi->sh->boardflags & BFL_FEM_BT)) - wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313_epa); - else if (pi->sh->boardflags & BFL_FEM_BT) { - if (pi->sh->boardrev < 0x1250) - wlc_lcnphy_write_table( - pi, - &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa); + if (pi->sh->boardflags & BFL_FEM) { + if (pi->sh->boardflags & BFL_FEM_BT) { + if (pi->sh->boardrev < 0x1250) + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa; + else + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250; + } else { + tb = &dot11lcn_sw_ctrl_tbl_info_4313_epa; + } + } else { + if (pi->sh->boardflags & BFL_FEM_BT) + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa; else - wlc_lcnphy_write_table( - pi, - &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250); - } else - wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313); - + tb = &dot11lcn_sw_ctrl_tbl_info_4313; + } + wlc_lcnphy_write_table(pi, tb); wlc_lcnphy_load_rfpower(pi); wlc_lcnphy_clear_papd_comptable(pi); @@ -4955,6 +5053,8 @@ void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec) wlc_lcnphy_load_tx_iir_filter(pi, true, 3); mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3); + if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) + wlc_lcnphy_tssi_setup(pi); } void wlc_phy_detach_lcnphy(struct brcms_phy *pi) @@ -4993,8 +5093,7 @@ bool wlc_phy_attach_lcnphy(struct brcms_phy *pi) if (!wlc_phy_txpwr_srom_read_lcnphy(pi)) return false; - if ((pi->sh->boardflags & BFL_FEM) && - (LCNREV_IS(pi->pubpi.phy_rev, 1))) { + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { if (pi_lcn->lcnphy_tempsense_option == 3) { pi->hwpwrctrl = true; pi->hwpwrctrl_capable = true; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c index 622c01c..d7fa312 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c @@ -1507,117 +1507,103 @@ static const u32 dot11lcn_gain_tbl_5G[] = { const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[] = { {&dot11lcn_gain_tbl_rev0, - sizeof(dot11lcn_gain_tbl_rev0) / sizeof(dot11lcn_gain_tbl_rev0[0]), 18, + ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_rev0, - sizeof(dot11lcn_aux_gain_idx_tbl_rev0) / - sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16} + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_rev0, - sizeof(dot11lcn_gain_idx_tbl_rev0) / - sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32} + ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32} , }; static const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev1[] = { {&dot11lcn_gain_tbl_rev1, - sizeof(dot11lcn_gain_tbl_rev1) / sizeof(dot11lcn_gain_tbl_rev1[0]), 18, + ARRAY_SIZE(dot11lcn_gain_tbl_rev1), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_rev0, - sizeof(dot11lcn_aux_gain_idx_tbl_rev0) / - sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16} + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_rev0, - sizeof(dot11lcn_gain_idx_tbl_rev0) / - sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32} + ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32} , }; const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[] = { {&dot11lcn_gain_tbl_2G, - sizeof(dot11lcn_gain_tbl_2G) / sizeof(dot11lcn_gain_tbl_2G[0]), 18, 0, + ARRAY_SIZE(dot11lcn_gain_tbl_2G), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_2G, - sizeof(dot11lcn_aux_gain_idx_tbl_2G) / - sizeof(dot11lcn_aux_gain_idx_tbl_2G[0]), 14, 0, 16} + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_2G), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_2G, - sizeof(dot11lcn_gain_idx_tbl_2G) / sizeof(dot11lcn_gain_idx_tbl_2G[0]), + ARRAY_SIZE(dot11lcn_gain_idx_tbl_2G), 13, 0, 32} , {&dot11lcn_gain_val_tbl_2G, - sizeof(dot11lcn_gain_val_tbl_2G) / sizeof(dot11lcn_gain_val_tbl_2G[0]), + ARRAY_SIZE(dot11lcn_gain_val_tbl_2G), 17, 0, 8} }; const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[] = { {&dot11lcn_gain_tbl_5G, - sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0, + ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_5G, - sizeof(dot11lcn_aux_gain_idx_tbl_5G) / - sizeof(dot11lcn_aux_gain_idx_tbl_5G[0]), 14, 0, 16} + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_5G, - sizeof(dot11lcn_gain_idx_tbl_5G) / sizeof(dot11lcn_gain_idx_tbl_5G[0]), + ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G), 13, 0, 32} , {&dot11lcn_gain_val_tbl_5G, - sizeof(dot11lcn_gain_val_tbl_5G) / sizeof(dot11lcn_gain_val_tbl_5G[0]), + ARRAY_SIZE(dot11lcn_gain_val_tbl_5G), 17, 0, 8} }; const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[] = { {&dot11lcn_gain_tbl_extlna_2G, - sizeof(dot11lcn_gain_tbl_extlna_2G) / - sizeof(dot11lcn_gain_tbl_extlna_2G[0]), 18, 0, 32} + ARRAY_SIZE(dot11lcn_gain_tbl_extlna_2G), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_extlna_2G, - sizeof(dot11lcn_aux_gain_idx_tbl_extlna_2G) / - sizeof(dot11lcn_aux_gain_idx_tbl_extlna_2G[0]), 14, 0, 16} + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_extlna_2G), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_extlna_2G, - sizeof(dot11lcn_gain_idx_tbl_extlna_2G) / - sizeof(dot11lcn_gain_idx_tbl_extlna_2G[0]), 13, 0, 32} + ARRAY_SIZE(dot11lcn_gain_idx_tbl_extlna_2G), 13, 0, 32} , {&dot11lcn_gain_val_tbl_extlna_2G, - sizeof(dot11lcn_gain_val_tbl_extlna_2G) / - sizeof(dot11lcn_gain_val_tbl_extlna_2G[0]), 17, 0, 8} + ARRAY_SIZE(dot11lcn_gain_val_tbl_extlna_2G), 17, 0, 8} }; const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[] = { {&dot11lcn_gain_tbl_5G, - sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0, + ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_5G, - sizeof(dot11lcn_aux_gain_idx_tbl_5G) / - sizeof(dot11lcn_aux_gain_idx_tbl_5G[0]), 14, 0, 16} + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16} , {&dot11lcn_gain_idx_tbl_5G, - sizeof(dot11lcn_gain_idx_tbl_5G) / sizeof(dot11lcn_gain_idx_tbl_5G[0]), + ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G), 13, 0, 32} , {&dot11lcn_gain_val_tbl_5G, - sizeof(dot11lcn_gain_val_tbl_5G) / sizeof(dot11lcn_gain_val_tbl_5G[0]), + ARRAY_SIZE(dot11lcn_gain_val_tbl_5G), 17, 0, 8} }; const u32 dot11lcnphytbl_rx_gain_info_sz_rev0 = - sizeof(dot11lcnphytbl_rx_gain_info_rev0) / - sizeof(dot11lcnphytbl_rx_gain_info_rev0[0]); + ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_rev0); const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz = - sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2) / - sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2[0]); + ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_2G_rev2); const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz = - sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2) / - sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2[0]); + ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_5G_rev2); static const u16 dot11lcn_min_sig_sq_tbl_rev0[] = { 0x014d, @@ -2058,6 +2044,73 @@ static const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = { 0x0005, }; +static const u16 dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[] = { + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, +}; + static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = { 0x0004, 0x0004, @@ -2771,89 +2824,79 @@ static const u32 dot11lcn_papd_compdelta_tbl_rev0[] = { const struct phytbl_info dot11lcnphytbl_info_rev0[] = { {&dot11lcn_min_sig_sq_tbl_rev0, - sizeof(dot11lcn_min_sig_sq_tbl_rev0) / - sizeof(dot11lcn_min_sig_sq_tbl_rev0[0]), 2, 0, 16} + ARRAY_SIZE(dot11lcn_min_sig_sq_tbl_rev0), 2, 0, 16} , {&dot11lcn_noise_scale_tbl_rev0, - sizeof(dot11lcn_noise_scale_tbl_rev0) / - sizeof(dot11lcn_noise_scale_tbl_rev0[0]), 1, 0, 16} + ARRAY_SIZE(dot11lcn_noise_scale_tbl_rev0), 1, 0, 16} , {&dot11lcn_fltr_ctrl_tbl_rev0, - sizeof(dot11lcn_fltr_ctrl_tbl_rev0) / - sizeof(dot11lcn_fltr_ctrl_tbl_rev0[0]), 11, 0, 32} + ARRAY_SIZE(dot11lcn_fltr_ctrl_tbl_rev0), 11, 0, 32} , {&dot11lcn_ps_ctrl_tbl_rev0, - sizeof(dot11lcn_ps_ctrl_tbl_rev0) / - sizeof(dot11lcn_ps_ctrl_tbl_rev0[0]), 12, 0, 32} + ARRAY_SIZE(dot11lcn_ps_ctrl_tbl_rev0), 12, 0, 32} , {&dot11lcn_gain_idx_tbl_rev0, - sizeof(dot11lcn_gain_idx_tbl_rev0) / - sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32} + ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32} , {&dot11lcn_aux_gain_idx_tbl_rev0, - sizeof(dot11lcn_aux_gain_idx_tbl_rev0) / - sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16} + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16} , {&dot11lcn_sw_ctrl_tbl_rev0, - sizeof(dot11lcn_sw_ctrl_tbl_rev0) / - sizeof(dot11lcn_sw_ctrl_tbl_rev0[0]), 15, 0, 16} + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_rev0), 15, 0, 16} , {&dot11lcn_nf_table_rev0, - sizeof(dot11lcn_nf_table_rev0) / sizeof(dot11lcn_nf_table_rev0[0]), 16, + ARRAY_SIZE(dot11lcn_nf_table_rev0), 16, 0, 8} , {&dot11lcn_gain_val_tbl_rev0, - sizeof(dot11lcn_gain_val_tbl_rev0) / - sizeof(dot11lcn_gain_val_tbl_rev0[0]), 17, 0, 8} + ARRAY_SIZE(dot11lcn_gain_val_tbl_rev0), 17, 0, 8} , {&dot11lcn_gain_tbl_rev0, - sizeof(dot11lcn_gain_tbl_rev0) / sizeof(dot11lcn_gain_tbl_rev0[0]), 18, + ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18, 0, 32} , {&dot11lcn_spur_tbl_rev0, - sizeof(dot11lcn_spur_tbl_rev0) / sizeof(dot11lcn_spur_tbl_rev0[0]), 20, + ARRAY_SIZE(dot11lcn_spur_tbl_rev0), 20, 0, 8} , {&dot11lcn_unsup_mcs_tbl_rev0, - sizeof(dot11lcn_unsup_mcs_tbl_rev0) / - sizeof(dot11lcn_unsup_mcs_tbl_rev0[0]), 23, 0, 16} + ARRAY_SIZE(dot11lcn_unsup_mcs_tbl_rev0), 23, 0, 16} , {&dot11lcn_iq_local_tbl_rev0, - sizeof(dot11lcn_iq_local_tbl_rev0) / - sizeof(dot11lcn_iq_local_tbl_rev0[0]), 0, 0, 16} + ARRAY_SIZE(dot11lcn_iq_local_tbl_rev0), 0, 0, 16} , {&dot11lcn_papd_compdelta_tbl_rev0, - sizeof(dot11lcn_papd_compdelta_tbl_rev0) / - sizeof(dot11lcn_papd_compdelta_tbl_rev0[0]), 24, 0, 32} + ARRAY_SIZE(dot11lcn_papd_compdelta_tbl_rev0), 24, 0, 32} , }; const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = { &dot11lcn_sw_ctrl_tbl_4313_rev0, - sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0) / - sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0[0]), 15, 0, 16 + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_rev0), 15, 0, 16 +}; + +const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa = { + &dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo, + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo), 15, 0, 16 }; const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa = { &dot11lcn_sw_ctrl_tbl_4313_epa_rev0, - sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0) / - sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0[0]), 15, 0, 16 + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0), 15, 0, 16 }; const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa = { &dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo, - sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo) / - sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[0]), 15, 0, 16 + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo), 15, 0, 16 }; const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250 = { &dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0, - sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0) / - sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[0]), 15, 0, 16 + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0), 15, 0, 16 }; const u32 dot11lcnphytbl_info_sz_rev0 = - sizeof(dot11lcnphytbl_info_rev0) / sizeof(dot11lcnphytbl_info_rev0[0]); + ARRAY_SIZE(dot11lcnphytbl_info_rev0); const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = { @@ -2988,134 +3031,134 @@ dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = { }; const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[128] = { - {7, 0, 31, 0, 72}, - {7, 0, 31, 0, 70}, - {7, 0, 31, 0, 68}, - {7, 0, 30, 0, 67}, - {7, 0, 29, 0, 68}, - {7, 0, 28, 0, 68}, - {7, 0, 27, 0, 69}, - {7, 0, 26, 0, 70}, - {7, 0, 25, 0, 70}, - {7, 0, 24, 0, 71}, - {7, 0, 23, 0, 72}, - {7, 0, 23, 0, 70}, - {7, 0, 22, 0, 71}, - {7, 0, 21, 0, 72}, - {7, 0, 21, 0, 70}, - {7, 0, 21, 0, 68}, - {7, 0, 21, 0, 66}, - {7, 0, 21, 0, 64}, - {7, 0, 21, 0, 63}, - {7, 0, 20, 0, 64}, - {7, 0, 19, 0, 65}, - {7, 0, 19, 0, 64}, - {7, 0, 18, 0, 65}, - {7, 0, 18, 0, 64}, - {7, 0, 17, 0, 65}, - {7, 0, 17, 0, 64}, - {7, 0, 16, 0, 65}, - {7, 0, 16, 0, 64}, - {7, 0, 16, 0, 62}, - {7, 0, 16, 0, 60}, - {7, 0, 16, 0, 58}, - {7, 0, 15, 0, 61}, - {7, 0, 15, 0, 59}, - {7, 0, 14, 0, 61}, - {7, 0, 14, 0, 60}, - {7, 0, 14, 0, 58}, - {7, 0, 13, 0, 60}, - {7, 0, 13, 0, 59}, - {7, 0, 12, 0, 62}, - {7, 0, 12, 0, 60}, - {7, 0, 12, 0, 58}, - {7, 0, 11, 0, 62}, - {7, 0, 11, 0, 60}, - {7, 0, 11, 0, 59}, - {7, 0, 11, 0, 57}, - {7, 0, 10, 0, 61}, - {7, 0, 10, 0, 59}, - {7, 0, 10, 0, 57}, - {7, 0, 9, 0, 62}, - {7, 0, 9, 0, 60}, - {7, 0, 9, 0, 58}, - {7, 0, 9, 0, 57}, - {7, 0, 8, 0, 62}, - {7, 0, 8, 0, 60}, - {7, 0, 8, 0, 58}, - {7, 0, 8, 0, 57}, - {7, 0, 8, 0, 55}, - {7, 0, 7, 0, 61}, + {15, 0, 31, 0, 72}, + {15, 0, 31, 0, 70}, + {15, 0, 31, 0, 68}, + {15, 0, 30, 0, 68}, + {15, 0, 29, 0, 69}, + {15, 0, 28, 0, 69}, + {15, 0, 27, 0, 70}, + {15, 0, 26, 0, 70}, + {15, 0, 25, 0, 71}, + {15, 0, 24, 0, 72}, + {15, 0, 23, 0, 73}, + {15, 0, 23, 0, 71}, + {15, 0, 22, 0, 72}, + {15, 0, 21, 0, 73}, + {15, 0, 21, 0, 71}, + {15, 0, 21, 0, 69}, + {15, 0, 21, 0, 67}, + {15, 0, 21, 0, 65}, + {15, 0, 21, 0, 63}, + {15, 0, 20, 0, 65}, + {15, 0, 19, 0, 66}, + {15, 0, 19, 0, 64}, + {15, 0, 18, 0, 66}, + {15, 0, 18, 0, 64}, + {15, 0, 17, 0, 66}, + {15, 0, 17, 0, 64}, + {15, 0, 16, 0, 66}, + {15, 0, 16, 0, 64}, + {15, 0, 16, 0, 62}, + {15, 0, 16, 0, 61}, + {15, 0, 16, 0, 59}, + {15, 0, 15, 0, 61}, + {15, 0, 15, 0, 59}, + {15, 0, 14, 0, 62}, + {15, 0, 14, 0, 60}, + {15, 0, 14, 0, 58}, + {15, 0, 13, 0, 61}, + {15, 0, 13, 0, 59}, + {15, 0, 12, 0, 62}, + {15, 0, 12, 0, 61}, + {15, 0, 12, 0, 59}, + {15, 0, 11, 0, 62}, + {15, 0, 11, 0, 61}, + {15, 0, 11, 0, 59}, + {15, 0, 11, 0, 57}, + {15, 0, 10, 0, 61}, + {15, 0, 10, 0, 59}, + {15, 0, 10, 0, 58}, + {15, 0, 9, 0, 62}, + {15, 0, 9, 0, 61}, + {15, 0, 9, 0, 59}, + {15, 0, 9, 0, 57}, + {15, 0, 8, 0, 62}, + {15, 0, 8, 0, 61}, + {15, 0, 8, 0, 59}, + {15, 0, 8, 0, 57}, + {15, 0, 8, 0, 56}, + {15, 0, 8, 0, 54}, + {15, 0, 8, 0, 53}, + {15, 0, 8, 0, 51}, + {15, 0, 8, 0, 50}, + {7, 0, 7, 0, 69}, + {7, 0, 7, 0, 67}, + {7, 0, 7, 0, 65}, + {7, 0, 7, 0, 64}, + {7, 0, 7, 0, 62}, {7, 0, 7, 0, 60}, {7, 0, 7, 0, 58}, - {7, 0, 7, 0, 56}, + {7, 0, 7, 0, 57}, {7, 0, 7, 0, 55}, {7, 0, 6, 0, 62}, - {7, 0, 6, 0, 60}, - {7, 0, 6, 0, 58}, + {7, 0, 6, 0, 61}, + {7, 0, 6, 0, 59}, {7, 0, 6, 0, 57}, - {7, 0, 6, 0, 55}, + {7, 0, 6, 0, 56}, {7, 0, 6, 0, 54}, - {7, 0, 6, 0, 52}, + {7, 0, 6, 0, 53}, {7, 0, 5, 0, 61}, - {7, 0, 5, 0, 59}, - {7, 0, 5, 0, 57}, + {7, 0, 5, 0, 60}, + {7, 0, 5, 0, 58}, {7, 0, 5, 0, 56}, - {7, 0, 5, 0, 54}, + {7, 0, 5, 0, 55}, {7, 0, 5, 0, 53}, - {7, 0, 5, 0, 51}, - {7, 0, 4, 0, 62}, - {7, 0, 4, 0, 60}, - {7, 0, 4, 0, 58}, + {7, 0, 5, 0, 52}, + {7, 0, 5, 0, 50}, + {7, 0, 5, 0, 49}, + {7, 0, 5, 0, 47}, {7, 0, 4, 0, 57}, - {7, 0, 4, 0, 55}, + {7, 0, 4, 0, 56}, {7, 0, 4, 0, 54}, - {7, 0, 4, 0, 52}, + {7, 0, 4, 0, 53}, {7, 0, 4, 0, 51}, - {7, 0, 4, 0, 49}, + {7, 0, 4, 0, 50}, {7, 0, 4, 0, 48}, + {7, 0, 4, 0, 47}, {7, 0, 4, 0, 46}, - {7, 0, 3, 0, 60}, - {7, 0, 3, 0, 58}, - {7, 0, 3, 0, 57}, - {7, 0, 3, 0, 55}, - {7, 0, 3, 0, 54}, - {7, 0, 3, 0, 52}, + {7, 0, 4, 0, 44}, + {7, 0, 4, 0, 43}, + {7, 0, 4, 0, 42}, + {7, 0, 4, 0, 41}, + {7, 0, 4, 0, 40}, {7, 0, 3, 0, 51}, - {7, 0, 3, 0, 49}, + {7, 0, 3, 0, 50}, {7, 0, 3, 0, 48}, + {7, 0, 3, 0, 47}, {7, 0, 3, 0, 46}, - {7, 0, 3, 0, 45}, {7, 0, 3, 0, 44}, {7, 0, 3, 0, 43}, + {7, 0, 3, 0, 42}, {7, 0, 3, 0, 41}, - {7, 0, 2, 0, 61}, - {7, 0, 2, 0, 59}, - {7, 0, 2, 0, 57}, - {7, 0, 2, 0, 56}, - {7, 0, 2, 0, 54}, - {7, 0, 2, 0, 53}, - {7, 0, 2, 0, 51}, - {7, 0, 2, 0, 50}, - {7, 0, 2, 0, 48}, - {7, 0, 2, 0, 47}, - {7, 0, 2, 0, 46}, - {7, 0, 2, 0, 44}, - {7, 0, 2, 0, 43}, - {7, 0, 2, 0, 42}, - {7, 0, 2, 0, 41}, - {7, 0, 2, 0, 39}, - {7, 0, 2, 0, 38}, - {7, 0, 2, 0, 37}, - {7, 0, 2, 0, 36}, - {7, 0, 2, 0, 35}, - {7, 0, 2, 0, 34}, - {7, 0, 2, 0, 33}, - {7, 0, 2, 0, 32}, - {7, 0, 1, 0, 63}, - {7, 0, 1, 0, 61}, - {7, 0, 1, 0, 59}, - {7, 0, 1, 0, 57}, + {3, 0, 3, 0, 56}, + {3, 0, 3, 0, 54}, + {3, 0, 3, 0, 53}, + {3, 0, 3, 0, 51}, + {3, 0, 3, 0, 50}, + {3, 0, 3, 0, 48}, + {3, 0, 3, 0, 47}, + {3, 0, 3, 0, 46}, + {3, 0, 3, 0, 44}, + {3, 0, 3, 0, 43}, + {3, 0, 3, 0, 42}, + {3, 0, 3, 0, 41}, + {3, 0, 3, 0, 39}, + {3, 0, 3, 0, 38}, + {3, 0, 3, 0, 37}, + {3, 0, 3, 0, 36}, + {3, 0, 3, 0, 35}, + {3, 0, 3, 0, 34}, }; const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[128] = { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h index 5f75e16..489422a 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h @@ -20,6 +20,7 @@ extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[]; extern const u32 dot11lcnphytbl_rx_gain_info_sz_rev0; extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313; +extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa; extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa; extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa_combo; extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa; diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c index c1ec2a4..92d299a 100644 --- a/drivers/net/wireless/cw1200/bh.c +++ b/drivers/net/wireless/cw1200/bh.c @@ -465,8 +465,8 @@ static int cw1200_bh(void *arg) (rx || tx || term || suspend || priv->bh_error); }), status); - pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n", - rx, tx, term, suspend, status); + pr_debug("[BH] - rx: %d, tx: %d, term: %d, bh_err: %d, suspend: %d, status: %ld\n", + rx, tx, term, suspend, priv->bh_error, status); /* Did an error occur? */ if ((status < 0 && status != -ERESTARTSYS) || diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 3724e73..090f015 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -507,7 +507,7 @@ u32 cw1200_dpll_from_clk(u16 clk_khz) case 0xCB20: /* 52000 KHz */ return 0x07627091; default: - pr_err("Unknown Refclk freq (0x%04x), using 2600KHz\n", + pr_err("Unknown Refclk freq (0x%04x), using 26000KHz\n", clk_khz); return 0x0EC4F121; } diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h index 7afc613..48086e8 100644 --- a/drivers/net/wireless/cw1200/wsm.h +++ b/drivers/net/wireless/cw1200/wsm.h @@ -832,7 +832,7 @@ struct wsm_tx { /* the MSDU shall be terminated. Overrides the global */ /* dot11MaxTransmitMsduLifeTime setting [optional] */ /* Device will set the default value if this is 0. */ - u32 expire_time; + __le32 expire_time; /* WSM_HT_TX_... */ __le32 ht_tx_parameters; diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index 6307a4e..c275dc1 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -1425,7 +1425,7 @@ static int prism2_hw_init2(struct net_device *dev, int initial) } list_for_each(ptr, &local->hostap_interfaces) { iface = list_entry(ptr, struct hostap_interface, list); - memcpy(iface->dev->dev_addr, dev->dev_addr, ETH_ALEN); + eth_hw_addr_inherit(iface->dev, dev); } } else if (local->fw_ap) prism2_check_sta_fw_version(local); diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index 15f0fad..a1257c9 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -66,7 +66,7 @@ struct net_device * hostap_add_interface(struct local_info *local, list_add(&iface->list, &local->hostap_interfaces); mdev = local->dev; - memcpy(dev->dev_addr, mdev->dev_addr, ETH_ALEN); + eth_hw_addr_inherit(dev, mdev); dev->base_addr = mdev->base_addr; dev->irq = mdev->irq; dev->mem_start = mdev->mem_start; @@ -667,7 +667,7 @@ static int prism2_open(struct net_device *dev) if (local->no_pri) { printk(KERN_DEBUG "%s: could not set interface UP - no PRI " "f/w\n", dev->name); - return 1; + return -ENODEV; } if ((local->func->card_present && !local->func->card_present(local)) || @@ -682,7 +682,7 @@ static int prism2_open(struct net_device *dev) printk(KERN_WARNING "%s: could not enable MAC port\n", dev->name); prism2_close(dev); - return 1; + return -ENODEV; } if (!local->dev_enabled) prism2_callback(local, PRISM2_CALLBACK_ENABLE); diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c index fe31590..aea667b 100644 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/iwlegacy/3945-rs.c @@ -887,6 +887,7 @@ il3945_remove_debugfs(void *il, void *il_sta) */ static void il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *il_sta) { } diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c index c092033..f09e257 100644 --- a/drivers/net/wireless/iwlegacy/3945.c +++ b/drivers/net/wireless/iwlegacy/3945.c @@ -475,6 +475,8 @@ il3945_is_network_packet(struct il_priv *il, struct ieee80211_hdr *header) } } +#define SMALL_PACKET_SIZE 256 + static void il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, struct ieee80211_rx_status *stats) @@ -483,14 +485,13 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IL_RX_DATA(pkt); struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt); struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt); - u16 len = le16_to_cpu(rx_hdr->len); + u32 len = le16_to_cpu(rx_hdr->len); struct sk_buff *skb; __le16 fc = hdr->frame_control; + u32 fraglen = PAGE_SIZE << il->hw_params.rx_page_order; /* We received data from the HW, so stop the watchdog */ - if (unlikely - (len + IL39_RX_FRAME_SIZE > - PAGE_SIZE << il->hw_params.rx_page_order)) { + if (unlikely(len + IL39_RX_FRAME_SIZE > fraglen)) { D_DROP("Corruption detected!\n"); return; } @@ -506,26 +507,32 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, D_INFO("Woke queues - frame received on passive channel\n"); } - skb = dev_alloc_skb(128); + skb = dev_alloc_skb(SMALL_PACKET_SIZE); if (!skb) { IL_ERR("dev_alloc_skb failed\n"); return; } if (!il3945_mod_params.sw_crypto) - il_set_decrypted_flag(il, (struct ieee80211_hdr *)rxb_addr(rxb), + il_set_decrypted_flag(il, (struct ieee80211_hdr *)pkt, le32_to_cpu(rx_end->status), stats); - skb_add_rx_frag(skb, 0, rxb->page, - (void *)rx_hdr->payload - (void *)pkt, len, - len); - + /* If frame is small enough to fit into skb->head, copy it + * and do not consume a full page + */ + if (len <= SMALL_PACKET_SIZE) { + memcpy(skb_put(skb, len), rx_hdr->payload, len); + } else { + skb_add_rx_frag(skb, 0, rxb->page, + (void *)rx_hdr->payload - (void *)pkt, len, + fraglen); + il->alloc_rxb_page--; + rxb->page = NULL; + } il_update_stats(il, false, fc, len); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); ieee80211_rx(il->hw, skb); - il->alloc_rxb_page--; - rxb->page = NULL; } #define IL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 7acf5ee..5ab50a5 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -574,9 +574,11 @@ il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in) return decrypt_out; } +#define SMALL_PACKET_SIZE 256 + static void il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, - u16 len, u32 ampdu_status, struct il_rx_buf *rxb, + u32 len, u32 ampdu_status, struct il_rx_buf *rxb, struct ieee80211_rx_status *stats) { struct sk_buff *skb; @@ -598,21 +600,25 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, il_set_decrypted_flag(il, hdr, ampdu_status, stats)) return; - skb = dev_alloc_skb(128); + skb = dev_alloc_skb(SMALL_PACKET_SIZE); if (!skb) { IL_ERR("dev_alloc_skb failed\n"); return; } - skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len, - len); + if (len <= SMALL_PACKET_SIZE) { + memcpy(skb_put(skb, len), hdr, len); + } else { + skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), + len, PAGE_SIZE << il->hw_params.rx_page_order); + il->alloc_rxb_page--; + rxb->page = NULL; + } il_update_stats(il, false, fc, len); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); ieee80211_rx(il->hw, skb); - il->alloc_rxb_page--; - rxb->page = NULL; } /* Called for N_RX (legacy ABG frames), or diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c index ed3c42a..3ccbaf7 100644 --- a/drivers/net/wireless/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/iwlegacy/4965-rs.c @@ -2803,6 +2803,7 @@ il4965_rs_remove_debugfs(void *il, void *il_sta) */ static void il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *il_sta) { } diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index cbaa5c2..3eb2102 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -22,6 +22,8 @@ config IWLWIFI Intel Wireless WiFi Link 6150BGN 2 Adapter Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN) Intel 2000 Series Wi-Fi Adapters + Intel 7260 Wi-Fi Adapter + Intel 3160 Wi-Fi Adapter This driver uses the kernel's mac80211 subsystem. @@ -46,17 +48,16 @@ config IWLDVM depends on IWLWIFI default IWLWIFI help - This is the driver supporting the DVM firmware which is - currently the only firmware available for existing devices. + This is the driver that supports the DVM firmware which is + used by most existing devices (with the exception of 7260 + and 3160). config IWLMVM tristate "Intel Wireless WiFi MVM Firmware support" depends on IWLWIFI help - This is the driver supporting the MVM firmware which is - currently only available for 7000 series devices. - - Say yes if you have such a device. + This is the driver that supports the MVM firmware which is + currently only available for 7260 and 3160 devices. # don't call it _MODULE -- will confuse Kconfig/fixdep/... config IWLWIFI_OPMODE_MODULAR @@ -127,20 +128,3 @@ config IWLWIFI_DEVICE_TRACING If unsure, say Y so we can help you better when problems occur. endmenu - -config IWLWIFI_P2P - def_bool y - bool "iwlwifi experimental P2P support" - depends on IWLWIFI - help - This option enables experimental P2P support for some devices - based on microcode support. Since P2P support is still under - development, this option may even enable it for some devices - now that turn out to not support it in the future due to - microcode restrictions. - - To determine if your microcode supports the experimental P2P - offered by this option, check if the driver advertises AP - support when it is loaded. - - Say Y only if you want to experiment with P2P. diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 1835511..f2a86ff 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -106,7 +106,6 @@ extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg; #define STATUS_CHANNEL_SWITCH_PENDING 11 #define STATUS_SCAN_COMPLETE 12 #define STATUS_POWER_PMI 13 -#define STATUS_SCAN_ROC_EXPIRED 14 struct iwl_ucode_capabilities; @@ -250,7 +249,6 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid); /* scan */ void iwlagn_post_scan(struct iwl_priv *priv); -void iwlagn_disable_roc(struct iwl_priv *priv); int iwl_force_rf_reset(struct iwl_priv *priv, bool external); void iwl_init_scan_params(struct iwl_priv *priv); int iwl_scan_cancel(struct iwl_priv *priv); @@ -265,10 +263,6 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv, enum iwl_scan_type scan_type, enum ieee80211_band band); -void iwl_scan_roc_expired(struct iwl_priv *priv); -void iwl_scan_offchannel_skb(struct iwl_priv *priv); -void iwl_scan_offchannel_skb_status(struct iwl_priv *priv); - /* For faster active scanning, scan will move to the next channel if fewer than * PLCP_QUIET_THRESH packets are heard on this channel within * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c index d532948..d94f8ab 100644 --- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c @@ -69,19 +69,7 @@ } while (0) /* file operation */ -#define DEBUGFS_READ_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ - char __user *user_buf, \ - size_t count, loff_t *ppos); - -#define DEBUGFS_WRITE_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ - const char __user *user_buf, \ - size_t count, loff_t *ppos); - - #define DEBUGFS_READ_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ .open = simple_open, \ @@ -89,7 +77,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ }; #define DEBUGFS_WRITE_FILE_OPS(name) \ - DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .open = simple_open, \ @@ -98,8 +85,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ #define DEBUGFS_READ_WRITE_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ - DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .read = iwl_dbgfs_##name##_read, \ diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 60a4e0d..a79fdd1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -540,7 +540,6 @@ struct iwl_rxon_context { enum iwl_scan_type { IWL_SCAN_NORMAL, IWL_SCAN_RADIO_RESET, - IWL_SCAN_ROC, }; /** @@ -825,12 +824,6 @@ struct iwl_priv { struct reply_tx_error_statistics reply_tx_stats; struct reply_agg_tx_error_statistics reply_agg_tx_stats; - /* remain-on-channel offload support */ - struct ieee80211_channel *hw_roc_channel; - struct delayed_work hw_roc_disable_work; - int hw_roc_duration; - bool hw_roc_setup, hw_roc_start_notified; - /* bt coex */ u8 bt_enable_flag; u8 bt_status; diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 3193872..cae4d31 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -76,29 +76,6 @@ static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = { }, }; -static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = { - { - .max = 1, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_AP), - }, -}; - -static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = { - { - .max = 2, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_CLIENT), - }, -}; - static const struct ieee80211_iface_combination iwlagn_iface_combinations_dualmode[] = { { .num_different_channels = 1, @@ -114,21 +91,6 @@ iwlagn_iface_combinations_dualmode[] = { }, }; -static const struct ieee80211_iface_combination -iwlagn_iface_combinations_p2p[] = { - { .num_different_channels = 1, - .max_interfaces = 2, - .beacon_int_infra_match = true, - .limits = iwlagn_p2p_sta_go_limits, - .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits), - }, - { .num_different_channels = 1, - .max_interfaces = 2, - .limits = iwlagn_p2p_2sta_limits, - .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits), - }, -}; - /* * Not a mac80211 entry point function, but it fits in with all the * other mac80211 functions grouped here. @@ -186,19 +148,13 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) { - hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(iwlagn_iface_combinations_p2p); - } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { + if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { hw->wiphy->iface_combinations = iwlagn_iface_combinations_dualmode; hw->wiphy->n_iface_combinations = ARRAY_SIZE(iwlagn_iface_combinations_dualmode); } - hw->wiphy->max_remain_on_channel_duration = 500; - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | WIPHY_FLAG_IBSS_RSN; @@ -1159,126 +1115,6 @@ done: IWL_DEBUG_MAC80211(priv, "leave\n"); } -static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_channel *channel, - int duration, - enum ieee80211_roc_type type) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; - int err = 0; - - if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) - return -EOPNOTSUPP; - - if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT))) - return -EOPNOTSUPP; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - if (test_bit(STATUS_SCAN_HW, &priv->status)) { - /* mac80211 should not scan while ROC or ROC while scanning */ - if (WARN_ON_ONCE(priv->scan_type != IWL_SCAN_RADIO_RESET)) { - err = -EBUSY; - goto out; - } - - iwl_scan_cancel_timeout(priv, 100); - - if (test_bit(STATUS_SCAN_HW, &priv->status)) { - err = -EBUSY; - goto out; - } - } - - priv->hw_roc_channel = channel; - /* convert from ms to TU */ - priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024); - priv->hw_roc_start_notified = false; - cancel_delayed_work(&priv->hw_roc_disable_work); - - if (!ctx->is_active) { - static const struct iwl_qos_info default_qos_data = { - .def_qos_parm = { - .ac[0] = { - .cw_min = cpu_to_le16(3), - .cw_max = cpu_to_le16(7), - .aifsn = 2, - .edca_txop = cpu_to_le16(1504), - }, - .ac[1] = { - .cw_min = cpu_to_le16(7), - .cw_max = cpu_to_le16(15), - .aifsn = 2, - .edca_txop = cpu_to_le16(3008), - }, - .ac[2] = { - .cw_min = cpu_to_le16(15), - .cw_max = cpu_to_le16(1023), - .aifsn = 3, - }, - .ac[3] = { - .cw_min = cpu_to_le16(15), - .cw_max = cpu_to_le16(1023), - .aifsn = 7, - }, - }, - }; - - ctx->is_active = true; - ctx->qos_data = default_qos_data; - ctx->staging.dev_type = RXON_DEV_TYPE_P2P; - memcpy(ctx->staging.node_addr, - priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, - ETH_ALEN); - memcpy(ctx->staging.bssid_addr, - priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, - ETH_ALEN); - err = iwlagn_commit_rxon(priv, ctx); - if (err) - goto out; - ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK | - RXON_FILTER_PROMISC_MSK | - RXON_FILTER_CTL2HOST_MSK; - - err = iwlagn_commit_rxon(priv, ctx); - if (err) { - iwlagn_disable_roc(priv); - goto out; - } - priv->hw_roc_setup = true; - } - - err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band); - if (err) - iwlagn_disable_roc(priv); - - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return err; -} - -static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) - return -EOPNOTSUPP; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - iwl_scan_cancel_timeout(priv, priv->hw_roc_duration); - iwlagn_disable_roc(priv); - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return 0; -} - static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event) @@ -1434,12 +1270,8 @@ static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", viftype, vif->addr); - cancel_delayed_work_sync(&priv->hw_roc_disable_work); - mutex_lock(&priv->mutex); - iwlagn_disable_roc(priv); - if (!iwl_is_ready_rf(priv)) { IWL_WARN(priv, "Try to add interface when device not ready\n"); err = -EINVAL; @@ -1766,8 +1598,6 @@ struct ieee80211_ops iwlagn_hw_ops = { .channel_switch = iwlagn_mac_channel_switch, .flush = iwlagn_mac_flush, .tx_last_beacon = iwlagn_mac_tx_last_beacon, - .remain_on_channel = iwlagn_mac_remain_on_channel, - .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel, .rssi_callback = iwlagn_mac_rssi_callback, .set_tim = iwlagn_mac_set_tim, }; diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 1531a4f..7aad766 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -587,11 +587,6 @@ static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) priv->contexts[IWL_RXON_CTX_PAN].interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); - if (ucode_flags & IWL_UCODE_TLV_FLAGS_P2P) - priv->contexts[IWL_RXON_CTX_PAN].interface_modes |= - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO); - priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP; priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA; priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P; @@ -854,14 +849,6 @@ void iwl_down(struct iwl_priv *priv) iwl_scan_cancel_timeout(priv, 200); - /* - * If active, scanning won't cancel it, so say it expired. - * No race since we hold the mutex here and a new one - * can't come in at this time. - */ - if (priv->ucode_loaded && priv->cur_ucode != IWL_UCODE_INIT) - ieee80211_remain_on_channel_expired(priv->hw); - exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); @@ -1002,41 +989,6 @@ static void iwl_bg_restart(struct work_struct *data) } } - - - -void iwlagn_disable_roc(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; - - lockdep_assert_held(&priv->mutex); - - if (!priv->hw_roc_setup) - return; - - ctx->staging.dev_type = RXON_DEV_TYPE_P2P; - ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - - priv->hw_roc_channel = NULL; - - memset(ctx->staging.node_addr, 0, ETH_ALEN); - - iwlagn_commit_rxon(priv, ctx); - - ctx->is_active = false; - priv->hw_roc_setup = false; -} - -static void iwlagn_disable_roc_work(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, - hw_roc_disable_work.work); - - mutex_lock(&priv->mutex); - iwlagn_disable_roc(priv); - mutex_unlock(&priv->mutex); -} - /***************************************************************************** * * driver setup and teardown @@ -1053,8 +1005,6 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush); INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency); INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config); - INIT_DELAYED_WORK(&priv->hw_roc_disable_work, - iwlagn_disable_roc_work); iwl_setup_scan_deferred_work(priv); @@ -1082,7 +1032,6 @@ void iwl_cancel_deferred_work(struct iwl_priv *priv) cancel_work_sync(&priv->bt_full_concurrency); cancel_work_sync(&priv->bt_runtime_config); - cancel_delayed_work_sync(&priv->hw_roc_disable_work); del_timer_sync(&priv->statistics_periodic); del_timer_sync(&priv->ucode_trace); @@ -1169,12 +1118,6 @@ static void iwl_option_config(struct iwl_priv *priv) #else IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n"); #endif - -#ifdef CONFIG_IWLWIFI_P2P - IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_P2P disabled\n"); -#endif } static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) @@ -1315,10 +1258,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, ucode_flags = fw->ucode_capa.flags; -#ifndef CONFIG_IWLWIFI_P2P - ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P; -#endif - if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) { priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN; trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM; @@ -1413,7 +1352,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, * if not PAN, then don't support P2P -- might be a uCode * packaging bug or due to the eeprom check above */ - ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P; priv->sta_key_max_num = STA_KEY_MAX_NUM; trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index 1b693944..b647e50 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -2826,9 +2826,6 @@ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_i lq_sta->flush_timer = 0; lq_sta->supp_rates = sta->supp_rates[sband->band]; - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n", sta_id); @@ -3319,7 +3316,8 @@ static void rs_remove_debugfs(void *priv, void *priv_sta) * station is added we ignore it. */ static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta) + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *priv_sta) { } static struct rate_control_ops rs_ops = { diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c index cd1ad001..d7ce2f1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c @@ -564,11 +564,7 @@ int iwlagn_set_pan_params(struct iwl_priv *priv) cmd.slots[0].type = 0; /* BSS */ cmd.slots[1].type = 1; /* PAN */ - if (priv->hw_roc_setup) { - /* both contexts must be used for this to happen */ - slot1 = IWL_MIN_SLOT_TIME; - slot0 = 3000; - } else if (ctx_bss->vif && ctx_pan->vif) { + if (ctx_bss->vif && ctx_pan->vif) { int bcnint = ctx_pan->beacon_int; int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1; diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index 8c686a5..35e0ee8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -100,9 +100,6 @@ static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) ieee80211_scan_completed(priv->hw, aborted); } - if (priv->scan_type == IWL_SCAN_ROC) - iwl_scan_roc_expired(priv); - priv->scan_type = IWL_SCAN_NORMAL; priv->scan_vif = NULL; priv->scan_request = NULL; @@ -130,9 +127,6 @@ static void iwl_process_scan_complete(struct iwl_priv *priv) goto out_settings; } - if (priv->scan_type == IWL_SCAN_ROC) - iwl_scan_roc_expired(priv); - if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { int err; @@ -284,12 +278,6 @@ static int iwl_rx_scan_start_notif(struct iwl_priv *priv, le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer); - if (priv->scan_type == IWL_SCAN_ROC && - !priv->hw_roc_start_notified) { - ieee80211_ready_on_channel(priv->hw); - priv->hw_roc_start_notified = true; - } - return 0; } @@ -697,8 +685,7 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; scan->quiet_time = IWL_ACTIVE_QUIET_TIME; - if (priv->scan_type != IWL_SCAN_ROC && - iwl_is_any_associated(priv)) { + if (iwl_is_any_associated(priv)) { u16 interval = 0; u32 extra; u32 suspend_time = 100; @@ -706,9 +693,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); switch (priv->scan_type) { - case IWL_SCAN_ROC: - WARN_ON(1); - break; case IWL_SCAN_RADIO_RESET: interval = 0; break; @@ -728,11 +712,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) scan->suspend_time = cpu_to_le32(scan_suspend_time); IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", scan_suspend_time, interval); - } else if (priv->scan_type == IWL_SCAN_ROC) { - scan->suspend_time = 0; - scan->max_out_time = 0; - scan->quiet_time = 0; - scan->quiet_plcp_th = 0; } switch (priv->scan_type) { @@ -774,9 +753,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) } else IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); break; - case IWL_SCAN_ROC: - IWL_DEBUG_SCAN(priv, "Start ROC scan.\n"); - break; } scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; @@ -898,7 +874,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) scan_cmd_size - sizeof(*scan)); break; case IWL_SCAN_RADIO_RESET: - case IWL_SCAN_ROC: /* use bcast addr, will not be transmitted but must be valid */ cmd_len = iwl_fill_probe_req( (struct ieee80211_mgmt *)scan->data, @@ -926,46 +901,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) is_active, n_probes, (void *)&scan->data[cmd_len]); break; - case IWL_SCAN_ROC: { - struct iwl_scan_channel *scan_ch; - int n_chan, i; - u16 dwell; - - dwell = iwl_limit_dwell(priv, priv->hw_roc_duration); - n_chan = DIV_ROUND_UP(priv->hw_roc_duration, dwell); - - scan->channel_count = n_chan; - - scan_ch = (void *)&scan->data[cmd_len]; - - for (i = 0; i < n_chan; i++) { - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - scan_ch->channel = - cpu_to_le16(priv->hw_roc_channel->hw_value); - - if (i == n_chan - 1) - dwell = priv->hw_roc_duration - i * dwell; - - scan_ch->active_dwell = - scan_ch->passive_dwell = cpu_to_le16(dwell); - - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - - /* NOTE: if we were doing 6Mb OFDM for scans we'd use - * power level: - * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; - */ - if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - - scan_ch++; - } - } - - break; } if (scan->channel_count == 0) { @@ -1035,7 +970,6 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv, IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", scan_type == IWL_SCAN_NORMAL ? "" : - scan_type == IWL_SCAN_ROC ? "remain-on-channel " : "internal short "); set_bit(STATUS_SCANNING, &priv->status); @@ -1149,40 +1083,3 @@ void iwl_cancel_scan_deferred_work(struct iwl_priv *priv) mutex_unlock(&priv->mutex); } } - -void iwl_scan_roc_expired(struct iwl_priv *priv) -{ - /* - * The status bit should be set here, to prevent a race - * where the atomic_read returns 1, but before the execution continues - * iwl_scan_offchannel_skb_status() checks if the status bit is set - */ - set_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); - - if (atomic_read(&priv->num_aux_in_flight) == 0) { - ieee80211_remain_on_channel_expired(priv->hw); - priv->hw_roc_channel = NULL; - schedule_delayed_work(&priv->hw_roc_disable_work, - 10 * HZ); - - clear_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); - } else { - IWL_DEBUG_SCAN(priv, "ROC done with %d frames in aux\n", - atomic_read(&priv->num_aux_in_flight)); - } -} - -void iwl_scan_offchannel_skb(struct iwl_priv *priv) -{ - WARN_ON(!priv->hw_roc_start_notified); - atomic_inc(&priv->num_aux_in_flight); -} - -void iwl_scan_offchannel_skb_status(struct iwl_priv *priv) -{ - if (atomic_dec_return(&priv->num_aux_in_flight) == 0 && - test_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status)) { - IWL_DEBUG_SCAN(priv, "0 aux frames. Calling ROC expired\n"); - iwl_scan_roc_expired(priv); - } -} diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 5ee983f..da442b8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -87,7 +87,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, priv->lib->bt_params->advanced_bt_coexist && (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) || - skb->protocol == cpu_to_be16(ETH_P_PAE))) + info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) tx_flags |= TX_CMD_FLG_IGNORE_BT; @@ -478,9 +478,6 @@ int iwlagn_tx_skb(struct iwl_priv *priv, if (sta_priv && sta_priv->client && !is_agg) atomic_inc(&sta_priv->pending_frames); - if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) - iwl_scan_offchannel_skb(priv); - return 0; drop_unlock_sta: @@ -1158,7 +1155,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct sk_buff *skb; struct iwl_rxon_context *ctx; bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); - bool is_offchannel_skb; tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> IWLAGN_TX_RES_TID_POS; @@ -1178,8 +1174,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, __skb_queue_head_init(&skbs); - is_offchannel_skb = false; - if (tx_resp->frame_count == 1) { u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10); @@ -1256,8 +1250,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); - is_offchannel_skb = - (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); freed++; } @@ -1271,14 +1263,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (!is_agg && freed != 1) IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed); - /* - * An offchannel frame can be send only on the AUX queue, where - * there is no aggregation (and reordering) so it only is single - * skb is expected to be processed. - */ - if (is_offchannel_skb && freed != 1) - IWL_ERR(priv, "OFFCHANNEL SKB freed %d\n", freed); - IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id, iwl_get_tx_fail_reason(status), status); @@ -1298,9 +1282,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, ieee80211_tx_status_ni(priv->hw, skb); } - if (is_offchannel_skb) - iwl_scan_offchannel_skb_status(priv); - return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 22b7fa5..76e14c0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -99,6 +99,7 @@ static const struct iwl_base_params iwl7000_base_params = { .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = true, + .pcie_l1_allowed = true, }; static const struct iwl_ht_params iwl7000_ht_params = { @@ -126,6 +127,16 @@ const struct iwl_cfg iwl7260_2ac_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, }; +const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { + .name = "Intel(R) Dual Band Wireless AC 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, + .high_temp = true, +}; + const struct iwl_cfg iwl7260_2n_cfg = { .name = "Intel(R) Dual Band Wireless N 7260", .fw_name_pre = IWL7260_FW_PRE, diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 83b9ff6..e4d370b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -152,6 +152,7 @@ struct iwl_base_params { unsigned int wd_timeout; u32 max_event_log_size; const bool shadow_reg_enable; + const bool pcie_l1_allowed; }; /* @@ -205,6 +206,7 @@ struct iwl_eeprom_params { * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) * @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @internal_wimax_coex: internal wifi/wimax combo device + * @high_temp: Is this NIC is designated to be in high temperature. * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -233,6 +235,7 @@ struct iwl_cfg { enum iwl_led_mode led_mode; const bool rx_with_siso_diversity; const bool internal_wimax_coex; + bool high_temp; }; /* @@ -283,6 +286,7 @@ extern const struct iwl_cfg iwl135_bgn_cfg; #endif /* CONFIG_IWLDVM */ #if IS_ENABLED(CONFIG_IWLMVM) extern const struct iwl_cfg iwl7260_2ac_cfg; +extern const struct iwl_cfg iwl7260_2ac_cfg_high_temp; extern const struct iwl_cfg iwl7260_2n_cfg; extern const struct iwl_cfg iwl7260_n_cfg; extern const struct iwl_cfg iwl3160_2ac_cfg; diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 7edb851..b2bb32a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -145,6 +145,7 @@ do { \ #define IWL_DL_RX 0x01000000 #define IWL_DL_ISR 0x02000000 #define IWL_DL_HT 0x04000000 +#define IWL_DL_EXTERNAL 0x08000000 /* 0xF0000000 - 0x10000000 */ #define IWL_DL_11H 0x10000000 #define IWL_DL_STATS 0x20000000 @@ -153,6 +154,7 @@ do { \ #define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a) #define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a) +#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a) #define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a) #define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a) #define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a) diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 4491c1c..684c416 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -33,10 +33,11 @@ static inline bool iwl_trace_data(struct sk_buff *skb) { struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - if (ieee80211_is_data(hdr->frame_control)) - return skb->protocol != cpu_to_be16(ETH_P_PAE); - return false; + if (!ieee80211_is_data(hdr->frame_control)) + return false; + return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO); } static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans, diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index d0162d4..99e1da3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -843,7 +843,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) int i; bool load_module = false; - fw->ucode_capa.max_probe_length = 200; + fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; fw->ucode_capa.standard_phy_calibration_size = IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; @@ -1032,8 +1032,10 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, int ret; drv = kzalloc(sizeof(*drv), GFP_KERNEL); - if (!drv) - return NULL; + if (!drv) { + ret = -ENOMEM; + goto err; + } drv->trans = trans; drv->dev = trans->dev; @@ -1078,7 +1080,7 @@ err_free_dbgfs: err_free_drv: #endif kfree(drv); - +err: return ERR_PTR(ret); } diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index f844d5c..a122368 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -74,13 +74,24 @@ * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS + * @IWL_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD + * @IWL_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api + * @IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API. + * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six + * (rather than two) IPv6 addresses + * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API */ enum iwl_ucode_tlv_flag { - IWL_UCODE_TLV_FLAGS_PAN = BIT(0), - IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1), - IWL_UCODE_TLV_FLAGS_MFP = BIT(2), - IWL_UCODE_TLV_FLAGS_P2P = BIT(3), - IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4), + IWL_UCODE_TLV_FLAGS_PAN = BIT(0), + IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1), + IWL_UCODE_TLV_FLAGS_MFP = BIT(2), + IWL_UCODE_TLV_FLAGS_P2P = BIT(3), + IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4), + IWL_UCODE_TLV_FLAGS_UAPSD = BIT(6), + IWL_UCODE_TLV_FLAGS_RX_ENERGY_API = BIT(8), + IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2 = BIT(9), + IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10), + IWL_UCODE_TLV_FLAGS_BF_UPDATED = BIT(11), }; /* The default calibrate table size if not specified by firmware file */ @@ -88,6 +99,9 @@ enum iwl_ucode_tlv_flag { #define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19 #define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253 +/* The default max probe length if not specified by the firmware file */ +#define IWL_DEFAULT_MAX_PROBE_LENGTH 200 + /** * enum iwl_ucode_type * diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 305c81f..dfa4d2e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -33,6 +33,8 @@ #include "iwl-io.h" #include "iwl-csr.h" #include "iwl-debug.h" +#include "iwl-fh.h" +#include "iwl-csr.h" #define IWL_POLL_INTERVAL 10 /* microseconds */ @@ -166,3 +168,68 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) } } IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); + +static const char *get_fh_string(int cmd) +{ +#define IWL_CMD(x) case x: return #x + switch (cmd) { + IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); + IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); + IWL_CMD(FH_RSCSR_CHNL0_WPTR); + IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); + IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); + IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); + IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); + IWL_CMD(FH_TSSR_TX_STATUS_REG); + IWL_CMD(FH_TSSR_TX_ERROR_REG); + default: + return "UNKNOWN"; + } +#undef IWL_CMD +} + +int iwl_dump_fh(struct iwl_trans *trans, char **buf) +{ + int i; + static const u32 fh_tbl[] = { + FH_RSCSR_CHNL0_STTS_WPTR_REG, + FH_RSCSR_CHNL0_RBDCB_BASE_REG, + FH_RSCSR_CHNL0_WPTR, + FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_MEM_RSSR_SHARED_CTRL_REG, + FH_MEM_RSSR_RX_STATUS_REG, + FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, + FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_ERROR_REG + }; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (buf) { + int pos = 0; + size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; + + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + + pos += scnprintf(*buf + pos, bufsz - pos, + "FH register values:\n"); + + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) + pos += scnprintf(*buf + pos, bufsz - pos, + " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(trans, fh_tbl[i])); + + return pos; + } +#endif + + IWL_ERR(trans, "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) + IWL_ERR(trans, " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(trans, fh_tbl[i])); + + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index fd9f5b9..63d10ec0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -77,4 +77,7 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, u32 bits, u32 mask); void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); +/* Error handling */ +int iwl_dump_fh(struct iwl_trans *trans, char **buf); + #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index acd2665..b76a9a8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -118,6 +118,7 @@ static const u8 iwl_nvm_channels[] = { #define LAST_2GHZ_HT_PLUS 9 #define LAST_5GHZ_HT 161 +#define DEFAULT_MAX_TX_POWER 16 /* rate data (static) */ static struct ieee80211_rate iwl_cfg80211_rates[] = { @@ -232,8 +233,11 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, /* Initialize regulatory-based run-time data */ - /* TODO: read the real value from the NVM */ - channel->max_power = 0; + /* + * Default value - highest tx power value. max_power + * is not used in mvm, and is used for backwards compatibility + */ + channel->max_power = DEFAULT_MAX_TX_POWER; is_5ghz = channel->band == IEEE80211_BAND_5GHZ; IWL_DEBUG_EEPROM(dev, "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index 98c7aa7..976448a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -93,7 +93,7 @@ struct iwl_cfg; * 1) The driver layer (iwl-drv.c) chooses the op_mode based on the * capabilities advertized by the fw file (in TLV format). * 2) The driver layer starts the op_mode (ops->start) - * 3) The op_mode registers registers mac80211 + * 3) The op_mode registers mac80211 * 4) The op_mode is governed by mac80211 * 5) The driver layer stops the op_mode */ @@ -112,7 +112,7 @@ struct iwl_cfg; * @stop: stop the op_mode. Must free all the memory allocated. * May sleep * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the - * HCMD the this Rx responds to. + * HCMD this Rx responds to. * This callback may sleep, it is called from a threaded IRQ handler. * @queue_full: notifies that a HW queue is full. * Must be atomic and called with BH disabled. diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 8d91422c..dd57a36 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -180,7 +180,7 @@ struct iwl_rx_packet { * enum CMD_MODE - how to send the host commands ? * * @CMD_SYNC: The caller will be stalled until the fw responds to the command - * @CMD_ASYNC: Return right away and don't want for the response + * @CMD_ASYNC: Return right away and don't wait for the response * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the * response. The caller needs to call iwl_free_resp when done. */ @@ -218,7 +218,7 @@ struct iwl_device_cmd { * * @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's * ring. The transport layer doesn't map the command's buffer to DMA, but - * rather copies it to an previously allocated DMA buffer. This flag tells + * rather copies it to a previously allocated DMA buffer. This flag tells * the transport layer not to copy the command, but to map the existing * buffer (that is passed in) instead. This saves the memcpy and allows * commands that are bigger than the fixed buffer to be submitted. @@ -243,7 +243,7 @@ enum iwl_hcmd_dataflag { * @handler_status: return value of the handler of the command * (put in setup_rx_handlers) - valid for SYNC mode only * @flags: can be CMD_* - * @len: array of the lenths of the chunks in data + * @len: array of the lengths of the chunks in data * @dataflags: IWL_HCMD_DFL_* * @id: id of the host command */ @@ -396,8 +396,6 @@ struct iwl_trans; * May sleep * @dbgfs_register: add the dbgfs files under this directory. Files will be * automatically deleted. - * @suspend: stop the device unless WoWLAN is configured - * @resume: resume activity of the device * @write8: write a u8 to a register at offset ofs from the BAR * @write32: write a u32 to a register at offset ofs from the BAR * @read32: read a u32 register at offset ofs from the BAR @@ -443,10 +441,7 @@ struct iwl_trans_ops { int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); int (*wait_tx_queue_empty)(struct iwl_trans *trans); -#ifdef CONFIG_PM_SLEEP - int (*suspend)(struct iwl_trans *trans); - int (*resume)(struct iwl_trans *trans); -#endif + void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val); void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val); u32 (*read32)(struct iwl_trans *trans, u32 ofs); @@ -700,18 +695,6 @@ static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, return trans->ops->dbgfs_register(trans, dir); } -#ifdef CONFIG_PM_SLEEP -static inline int iwl_trans_suspend(struct iwl_trans *trans) -{ - return trans->ops->suspend(trans); -} - -static inline int iwl_trans_resume(struct iwl_trans *trans) -{ - return trans->ops->resume(trans); -} -#endif - static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val) { trans->ops->write8(trans, ofs, val); diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index ff856e5..6d73817 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_IWLMVM) += iwlmvm.o iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o iwlmvm-y += scan.o time-event.o rs.o -iwlmvm-y += power.o bt-coex.o +iwlmvm-y += power.o power_legacy.o bt-coex.o iwlmvm-y += led.o tt.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c index dbd622a..0fad98b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c @@ -220,66 +220,87 @@ static const __le32 iwl_single_shared_ant_lookup[BT_COEX_LUT_SIZE] = { int iwl_send_bt_init_conf(struct iwl_mvm *mvm) { - struct iwl_bt_coex_cmd cmd = { - .max_kill = 5, - .bt3_time_t7_value = 1, - .bt3_prio_sample_time = 2, - .bt3_timer_t2_value = 0xc, + struct iwl_bt_coex_cmd *bt_cmd; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + .flags = CMD_SYNC, }; int ret; - cmd.flags = iwlwifi_mod_params.bt_coex_active ? + /* go to CALIB state in internal BT-Coex state machine */ + ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + + ret = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + + bt_cmd->max_kill = 5; + bt_cmd->bt3_time_t7_value = 1; + bt_cmd->bt3_prio_sample_time = 2; + bt_cmd->bt3_timer_t2_value = 0xc; + + bt_cmd->flags = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE; - cmd.flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE; + bt_cmd->flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE; - cmd.valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE | - BT_VALID_BT_PRIO_BOOST | - BT_VALID_MAX_KILL | - BT_VALID_3W_TMRS | - BT_VALID_KILL_ACK | - BT_VALID_KILL_CTS | - BT_VALID_REDUCED_TX_POWER | - BT_VALID_LUT); + bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE | + BT_VALID_BT_PRIO_BOOST | + BT_VALID_MAX_KILL | + BT_VALID_3W_TMRS | + BT_VALID_KILL_ACK | + BT_VALID_KILL_CTS | + BT_VALID_REDUCED_TX_POWER | + BT_VALID_LUT); if (mvm->cfg->bt_shared_single_ant) - memcpy(&cmd.decision_lut, iwl_single_shared_ant_lookup, + memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant_lookup, sizeof(iwl_single_shared_ant_lookup)); else if (is_loose_coex()) - memcpy(&cmd.decision_lut, iwl_loose_lookup, + memcpy(&bt_cmd->decision_lut, iwl_loose_lookup, sizeof(iwl_tight_lookup)); else - memcpy(&cmd.decision_lut, iwl_tight_lookup, + memcpy(&bt_cmd->decision_lut, iwl_tight_lookup, sizeof(iwl_tight_lookup)); - cmd.bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST); - cmd.kill_ack_msk = + bt_cmd->bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST); + bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]); - cmd.kill_cts_msk = + bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); - /* go to CALIB state in internal BT-Coex state machine */ - ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - - ret = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; + ret = iwl_mvm_send_cmd(mvm, &cmd); - return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC, - sizeof(cmd), &cmd); + kfree(bt_cmd); + return ret; } static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, bool reduced_tx_power) { enum iwl_bt_kill_msk bt_kill_msk; - struct iwl_bt_coex_cmd cmd = {}; + struct iwl_bt_coex_cmd *bt_cmd; struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .data[0] = &bt_cmd, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + .flags = CMD_SYNC, + }; + int ret = 0; lockdep_assert_held(&mvm->mutex); @@ -308,24 +329,40 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, return 0; mvm->bt_kill_msk = bt_kill_msk; - cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); - cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); - cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS); + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + + bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); + bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); + bt_cmd->valid_bit_msk = + cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS); IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk); - return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC, - sizeof(cmd), &cmd); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; } static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable) { - struct iwl_bt_coex_cmd cmd = { - .valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER), - .bt_reduced_tx_power = sta_id, + struct iwl_bt_coex_cmd *bt_cmd; + /* Send ASYNC since this can be sent from an atomic context */ + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_DUP, }, + .flags = CMD_ASYNC, }; + struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; + int ret; /* This can happen if the station has been removed right now */ if (sta_id == IWL_MVM_STATION_COUNT) @@ -339,17 +376,26 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, if (mvmsta->bt_reduced_txpower == enable) return 0; + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + + bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER), + bt_cmd->bt_reduced_tx_power = sta_id; + if (enable) - cmd.bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT; + bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT; IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", enable ? "en" : "dis", sta_id); mvmsta->bt_reduced_txpower = enable; - /* Send ASYNC since this can be sent from an atomic context */ - return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_ASYNC, - sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; } struct iwl_bt_iterator_data { @@ -384,6 +430,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, smps_mode = IEEE80211_SMPS_AUTOMATIC; + /* non associated BSSes aren't to be considered */ + if (!vif->bss_conf.assoc) + return; + if (band != IEEE80211_BAND_2GHZ) { iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); @@ -523,6 +573,8 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, lockdep_is_held(&mvm->mutex)); mvmsta = (void *)sta->drv_priv; + data->num_bss_ifaces++; + /* * This interface doesn't support reduced Tx power (because of low * RSSI probably), then set bt_kill_msk to default values. @@ -588,23 +640,5 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - struct ieee80211_chanctx_conf *chanctx_conf; - enum ieee80211_band band; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - if (chanctx_conf && chanctx_conf->def.chan) - band = chanctx_conf->def.chan->band; - else - band = -1; - rcu_read_unlock(); - - /* if we are in 2GHz we will get a notification from the fw */ - if (band == IEEE80211_BAND_2GHZ) - return; - - /* else, we can remove all the constraints */ - memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); - iwl_mvm_bt_coex_notif_handle(mvm); } diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h new file mode 100644 index 0000000..2bf29f7 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __MVM_CONSTANTS_H +#define __MVM_CONSTANTS_H + +#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC) +#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC) +#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC) +#define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC) +#define IWL_MVM_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC) +#define IWL_MVM_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC) +#define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS 20 +#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 20 +#define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT 50 +#define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT 50 +#define IWL_MVM_PS_SNOOZE_INTERVAL 25 +#define IWL_MVM_PS_SNOOZE_WINDOW 50 +#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25 + +#endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 83da884..417639f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -105,7 +105,7 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, list_for_each_entry(ifa, &idev->addr_list, if_list) { mvmvif->target_ipv6_addrs[idx] = ifa->addr; idx++; - if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS) + if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) break; } read_unlock_bh(&idev->lock); @@ -378,36 +378,68 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - struct iwl_proto_offload_cmd cmd = {}; + union { + struct iwl_proto_offload_cmd_v1 v1; + struct iwl_proto_offload_cmd_v2 v2; + } cmd = {}; + struct iwl_proto_offload_cmd_common *common; + u32 enabled = 0, size; #if IS_ENABLED(CONFIG_IPV6) struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int i; - if (mvmvif->num_target_ipv6_addrs) { - cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_NS); - memcpy(cmd.ndp_mac_addr, vif->addr, ETH_ALEN); - } + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { + if (mvmvif->num_target_ipv6_addrs) { + enabled |= IWL_D3_PROTO_OFFLOAD_NS; + memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); + } + + BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != + sizeof(mvmvif->target_ipv6_addrs[0])); - BUILD_BUG_ON(sizeof(cmd.target_ipv6_addr[i]) != - sizeof(mvmvif->target_ipv6_addrs[i])); + for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, + IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) + memcpy(cmd.v2.target_ipv6_addr[i], + &mvmvif->target_ipv6_addrs[i], + sizeof(cmd.v2.target_ipv6_addr[i])); + } else { + if (mvmvif->num_target_ipv6_addrs) { + enabled |= IWL_D3_PROTO_OFFLOAD_NS; + memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); + } - for (i = 0; i < mvmvif->num_target_ipv6_addrs; i++) - memcpy(cmd.target_ipv6_addr[i], - &mvmvif->target_ipv6_addrs[i], - sizeof(cmd.target_ipv6_addr[i])); + BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != + sizeof(mvmvif->target_ipv6_addrs[0])); + + for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, + IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) + memcpy(cmd.v1.target_ipv6_addr[i], + &mvmvif->target_ipv6_addrs[i], + sizeof(cmd.v1.target_ipv6_addr[i])); + } #endif + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { + common = &cmd.v2.common; + size = sizeof(cmd.v2); + } else { + common = &cmd.v1.common; + size = sizeof(cmd.v1); + } + if (vif->bss_conf.arp_addr_cnt) { - cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_ARP); - cmd.host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; - memcpy(cmd.arp_mac_addr, vif->addr, ETH_ALEN); + enabled |= IWL_D3_PROTO_OFFLOAD_ARP; + common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; + memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); } - if (!cmd.enabled) + if (!enabled) return 0; + common->enabled = cpu_to_le32(enabled); + return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC, - sizeof(cmd), &cmd); + size, &cmd); } enum iwl_mvm_tcp_packet_type { @@ -1077,73 +1109,16 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return __iwl_mvm_suspend(hw, wowlan, false); } -static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_wowlan_status *status) { - u32 base = mvm->error_event_table; - struct error_table_start { - /* cf. struct iwl_error_event_table */ - u32 valid; - u32 error_id; - } err_info; + struct sk_buff *pkt = NULL; struct cfg80211_wowlan_wakeup wakeup = { .pattern_idx = -1, }; struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; - struct iwl_host_cmd cmd = { - .id = WOWLAN_GET_STATUSES, - .flags = CMD_SYNC | CMD_WANT_SKB, - }; - struct iwl_wowlan_status *status; - u32 reasons; - int ret, len; - struct sk_buff *pkt = NULL; - - iwl_trans_read_mem_bytes(mvm->trans, base, - &err_info, sizeof(err_info)); - - if (err_info.valid) { - IWL_INFO(mvm, "error table is valid (%d)\n", - err_info.valid); - if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) { - wakeup.rfkill_release = true; - ieee80211_report_wowlan_wakeup(vif, &wakeup, - GFP_KERNEL); - } - return; - } - - /* only for tracing for now */ - ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL); - if (ret) - IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) { - IWL_ERR(mvm, "failed to query status (%d)\n", ret); - return; - } - - /* RF-kill already asserted again... */ - if (!cmd.resp_pkt) - return; - - len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) { - IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - goto out; - } - - status = (void *)cmd.resp_pkt->data; - - if (len - sizeof(struct iwl_cmd_header) != - sizeof(*status) + - ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) { - IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - goto out; - } - - reasons = le32_to_cpu(status->wakeup_reasons); + u32 reasons = le32_to_cpu(status->wakeup_reasons); if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { wakeup_report = NULL; @@ -1206,6 +1181,12 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, pktsize -= hdrlen; if (ieee80211_has_protected(hdr->frame_control)) { + /* + * This is unlocked and using gtk_i(c)vlen, + * but since everything is under RTNL still + * that's not really a problem - changing + * it would be difficult. + */ if (is_multicast_ether_addr(hdr->addr1)) { ivlen = mvm->gtk_ivlen; icvlen += mvm->gtk_icvlen; @@ -1256,9 +1237,82 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, report: ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); kfree_skb(pkt); +} - out: +/* releases the MVM mutex */ +static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + u32 base = mvm->error_event_table; + struct error_table_start { + /* cf. struct iwl_error_event_table */ + u32 valid; + u32 error_id; + } err_info; + struct iwl_host_cmd cmd = { + .id = WOWLAN_GET_STATUSES, + .flags = CMD_SYNC | CMD_WANT_SKB, + }; + struct iwl_wowlan_status *status; + int ret, len; + + iwl_trans_read_mem_bytes(mvm->trans, base, + &err_info, sizeof(err_info)); + + if (err_info.valid) { + IWL_INFO(mvm, "error table is valid (%d)\n", + err_info.valid); + if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) { + struct cfg80211_wowlan_wakeup wakeup = { + .rfkill_release = true, + }; + ieee80211_report_wowlan_wakeup(vif, &wakeup, + GFP_KERNEL); + } + goto out_unlock; + } + + /* only for tracing for now */ + ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL); + if (ret) + IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) { + IWL_ERR(mvm, "failed to query status (%d)\n", ret); + goto out_unlock; + } + + /* RF-kill already asserted again... */ + if (!cmd.resp_pkt) + goto out_unlock; + + len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) { + IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); + goto out_free_resp; + } + + status = (void *)cmd.resp_pkt->data; + + if (len - sizeof(struct iwl_cmd_header) != + sizeof(*status) + + ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) { + IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); + goto out_free_resp; + } + + /* now we have all the data we need, unlock to avoid mac80211 issues */ + mutex_unlock(&mvm->mutex); + + iwl_mvm_report_wakeup_reasons(mvm, vif, status); + iwl_free_resp(&cmd); + return; + + out_free_resp: iwl_free_resp(&cmd); + out_unlock: + mutex_unlock(&mvm->mutex); } static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) @@ -1315,10 +1369,13 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) iwl_mvm_read_d3_sram(mvm); iwl_mvm_query_wakeup_reasons(mvm, vif); + /* has unlocked the mutex, so skip that */ + goto out; out_unlock: mutex_unlock(&mvm->mutex); + out: if (!test && vif) ieee80211_resume_disconnect(vif); diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index c24a744..aac81b8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -352,6 +352,10 @@ static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); dbgfs_pm->lprx_rssi_threshold = val; break; + case MVM_DEBUGFS_PM_SNOOZE_ENABLE: + IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); + dbgfs_pm->snooze_ena = val; + break; } } @@ -405,6 +409,10 @@ static ssize_t iwl_dbgfs_pm_params_write(struct file *file, POWER_LPRX_RSSI_THRESHOLD_MIN) return -EINVAL; param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; + } else if (!strncmp("snooze_enable=", buf, 14)) { + if (sscanf(buf + 14, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; } else { return -EINVAL; } @@ -424,40 +432,11 @@ static ssize_t iwl_dbgfs_pm_params_read(struct file *file, struct ieee80211_vif *vif = file->private_data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->dbgfs_data; - struct iwl_powertable_cmd cmd = {}; - char buf[256]; + char buf[512]; int bufsz = sizeof(buf); - int pos = 0; + int pos; - iwl_mvm_power_build_cmd(mvm, vif, &cmd); - - pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? - 0 : 1); - pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", - le32_to_cpu(cmd.skip_dtim_periods)); - pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", - iwlmvm_mod_params.power_scheme); - pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", - le16_to_cpu(cmd.flags)); - pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", - cmd.keep_alive_seconds); - - if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { - pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? - 1 : 0); - pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", - le32_to_cpu(cmd.rx_data_timeout)); - pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", - le32_to_cpu(cmd.tx_data_timeout)); - if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - pos += scnprintf(buf+pos, bufsz-pos, - "lprx_rssi_threshold = %d\n", - le32_to_cpu(cmd.lprx_rssi_threshold)); - } + pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } @@ -621,25 +600,160 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, } #undef BT_MBOX_PRINT +#define PRINT_STATS_LE32(_str, _val) \ + pos += scnprintf(buf + pos, bufsz - pos, \ + fmt_table, _str, \ + le32_to_cpu(_val)) + +static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + static const char *fmt_table = "\t%-30s %10u\n"; + static const char *fmt_header = "%-32s\n"; + int pos = 0; + char *buf; + int ret; + int bufsz = sizeof(struct mvm_statistics_rx_phy) * 20 + + sizeof(struct mvm_statistics_rx_non_phy) * 10 + + sizeof(struct mvm_statistics_rx_ht_phy) * 10 + 200; + struct mvm_statistics_rx_phy *ofdm; + struct mvm_statistics_rx_phy *cck; + struct mvm_statistics_rx_non_phy *general; + struct mvm_statistics_rx_ht_phy *ht; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + + ofdm = &mvm->rx_stats.ofdm; + cck = &mvm->rx_stats.cck; + general = &mvm->rx_stats.general; + ht = &mvm->rx_stats.ofdm_ht; + + pos += scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - OFDM"); + PRINT_STATS_LE32("ina_cnt", ofdm->ina_cnt); + PRINT_STATS_LE32("fina_cnt", ofdm->fina_cnt); + PRINT_STATS_LE32("plcp_err", ofdm->plcp_err); + PRINT_STATS_LE32("crc32_err", ofdm->crc32_err); + PRINT_STATS_LE32("overrun_err", ofdm->overrun_err); + PRINT_STATS_LE32("early_overrun_err", ofdm->early_overrun_err); + PRINT_STATS_LE32("crc32_good", ofdm->crc32_good); + PRINT_STATS_LE32("false_alarm_cnt", ofdm->false_alarm_cnt); + PRINT_STATS_LE32("fina_sync_err_cnt", ofdm->fina_sync_err_cnt); + PRINT_STATS_LE32("sfd_timeout", ofdm->sfd_timeout); + PRINT_STATS_LE32("fina_timeout", ofdm->fina_timeout); + PRINT_STATS_LE32("unresponded_rts", ofdm->unresponded_rts); + PRINT_STATS_LE32("rxe_frame_lmt_overrun", + ofdm->rxe_frame_limit_overrun); + PRINT_STATS_LE32("sent_ack_cnt", ofdm->sent_ack_cnt); + PRINT_STATS_LE32("sent_cts_cnt", ofdm->sent_cts_cnt); + PRINT_STATS_LE32("sent_ba_rsp_cnt", ofdm->sent_ba_rsp_cnt); + PRINT_STATS_LE32("dsp_self_kill", ofdm->dsp_self_kill); + PRINT_STATS_LE32("mh_format_err", ofdm->mh_format_err); + PRINT_STATS_LE32("re_acq_main_rssi_sum", ofdm->re_acq_main_rssi_sum); + PRINT_STATS_LE32("reserved", ofdm->reserved); + + pos += scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - CCK"); + PRINT_STATS_LE32("ina_cnt", cck->ina_cnt); + PRINT_STATS_LE32("fina_cnt", cck->fina_cnt); + PRINT_STATS_LE32("plcp_err", cck->plcp_err); + PRINT_STATS_LE32("crc32_err", cck->crc32_err); + PRINT_STATS_LE32("overrun_err", cck->overrun_err); + PRINT_STATS_LE32("early_overrun_err", cck->early_overrun_err); + PRINT_STATS_LE32("crc32_good", cck->crc32_good); + PRINT_STATS_LE32("false_alarm_cnt", cck->false_alarm_cnt); + PRINT_STATS_LE32("fina_sync_err_cnt", cck->fina_sync_err_cnt); + PRINT_STATS_LE32("sfd_timeout", cck->sfd_timeout); + PRINT_STATS_LE32("fina_timeout", cck->fina_timeout); + PRINT_STATS_LE32("unresponded_rts", cck->unresponded_rts); + PRINT_STATS_LE32("rxe_frame_lmt_overrun", + cck->rxe_frame_limit_overrun); + PRINT_STATS_LE32("sent_ack_cnt", cck->sent_ack_cnt); + PRINT_STATS_LE32("sent_cts_cnt", cck->sent_cts_cnt); + PRINT_STATS_LE32("sent_ba_rsp_cnt", cck->sent_ba_rsp_cnt); + PRINT_STATS_LE32("dsp_self_kill", cck->dsp_self_kill); + PRINT_STATS_LE32("mh_format_err", cck->mh_format_err); + PRINT_STATS_LE32("re_acq_main_rssi_sum", cck->re_acq_main_rssi_sum); + PRINT_STATS_LE32("reserved", cck->reserved); + + pos += scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - GENERAL"); + PRINT_STATS_LE32("bogus_cts", general->bogus_cts); + PRINT_STATS_LE32("bogus_ack", general->bogus_ack); + PRINT_STATS_LE32("non_bssid_frames", general->non_bssid_frames); + PRINT_STATS_LE32("filtered_frames", general->filtered_frames); + PRINT_STATS_LE32("non_channel_beacons", general->non_channel_beacons); + PRINT_STATS_LE32("channel_beacons", general->channel_beacons); + PRINT_STATS_LE32("num_missed_bcon", general->num_missed_bcon); + PRINT_STATS_LE32("adc_rx_saturation_time", + general->adc_rx_saturation_time); + PRINT_STATS_LE32("ina_detection_search_time", + general->ina_detection_search_time); + PRINT_STATS_LE32("beacon_silence_rssi_a", + general->beacon_silence_rssi_a); + PRINT_STATS_LE32("beacon_silence_rssi_b", + general->beacon_silence_rssi_b); + PRINT_STATS_LE32("beacon_silence_rssi_c", + general->beacon_silence_rssi_c); + PRINT_STATS_LE32("interference_data_flag", + general->interference_data_flag); + PRINT_STATS_LE32("channel_load", general->channel_load); + PRINT_STATS_LE32("dsp_false_alarms", general->dsp_false_alarms); + PRINT_STATS_LE32("beacon_rssi_a", general->beacon_rssi_a); + PRINT_STATS_LE32("beacon_rssi_b", general->beacon_rssi_b); + PRINT_STATS_LE32("beacon_rssi_c", general->beacon_rssi_c); + PRINT_STATS_LE32("beacon_energy_a", general->beacon_energy_a); + PRINT_STATS_LE32("beacon_energy_b", general->beacon_energy_b); + PRINT_STATS_LE32("beacon_energy_c", general->beacon_energy_c); + PRINT_STATS_LE32("num_bt_kills", general->num_bt_kills); + PRINT_STATS_LE32("directed_data_mpdu", general->directed_data_mpdu); + + pos += scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - HT"); + PRINT_STATS_LE32("plcp_err", ht->plcp_err); + PRINT_STATS_LE32("overrun_err", ht->overrun_err); + PRINT_STATS_LE32("early_overrun_err", ht->early_overrun_err); + PRINT_STATS_LE32("crc32_good", ht->crc32_good); + PRINT_STATS_LE32("crc32_err", ht->crc32_err); + PRINT_STATS_LE32("mh_format_err", ht->mh_format_err); + PRINT_STATS_LE32("agg_crc32_good", ht->agg_crc32_good); + PRINT_STATS_LE32("agg_mpdu_cnt", ht->agg_mpdu_cnt); + PRINT_STATS_LE32("agg_cnt", ht->agg_cnt); + PRINT_STATS_LE32("unsupport_mcs", ht->unsupport_mcs); + + mutex_unlock(&mvm->mutex); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + + return ret; +} +#undef PRINT_STAT_LE32 + static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - bool restart_fw = iwlwifi_mod_params.restart_fw; int ret; - iwlwifi_mod_params.restart_fw = true; - mutex_lock(&mvm->mutex); + /* allow one more restart that we're provoking here */ + if (mvm->restart_fw >= 0) + mvm->restart_fw++; + /* take the return value to make compiler happy - it will fail anyway */ ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL); mutex_unlock(&mvm->mutex); - iwlwifi_mod_params.restart_fw = restart_fw; - return count; } @@ -661,8 +775,14 @@ static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, case MVM_DEBUGFS_BF_ROAMING_STATE: dbgfs_bf->bf_roaming_state = value; break; - case MVM_DEBUGFS_BF_TEMPERATURE_DELTA: - dbgfs_bf->bf_temperature_delta = value; + case MVM_DEBUGFS_BF_TEMP_THRESHOLD: + dbgfs_bf->bf_temp_threshold = value; + break; + case MVM_DEBUGFS_BF_TEMP_FAST_FILTER: + dbgfs_bf->bf_temp_fast_filter = value; + break; + case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER: + dbgfs_bf->bf_temp_slow_filter = value; break; case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: dbgfs_bf->bf_enable_beacon_filter = value; @@ -721,13 +841,27 @@ static ssize_t iwl_dbgfs_bf_params_write(struct file *file, value > IWL_BF_ROAMING_STATE_MAX) return -EINVAL; param = MVM_DEBUGFS_BF_ROAMING_STATE; - } else if (!strncmp("bf_temperature_delta=", buf, 21)) { - if (sscanf(buf+21, "%d", &value) != 1) + } else if (!strncmp("bf_temp_threshold=", buf, 18)) { + if (sscanf(buf+18, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_THRESHOLD_MIN || + value > IWL_BF_TEMP_THRESHOLD_MAX) return -EINVAL; - if (value < IWL_BF_TEMPERATURE_DELTA_MIN || - value > IWL_BF_TEMPERATURE_DELTA_MAX) + param = MVM_DEBUGFS_BF_TEMP_THRESHOLD; + } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) { + if (sscanf(buf+20, "%d", &value) != 1) return -EINVAL; - param = MVM_DEBUGFS_BF_TEMPERATURE_DELTA; + if (value < IWL_BF_TEMP_FAST_FILTER_MIN || + value > IWL_BF_TEMP_FAST_FILTER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER; + } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) { + if (sscanf(buf+20, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_SLOW_FILTER_MIN || + value > IWL_BF_TEMP_SLOW_FILTER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER; } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { if (sscanf(buf+24, "%d", &value) != 1) return -EINVAL; @@ -769,10 +903,7 @@ static ssize_t iwl_dbgfs_bf_params_write(struct file *file, if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) { ret = iwl_mvm_disable_beacon_filter(mvm, vif); } else { - if (mvmvif->bf_enabled) - ret = iwl_mvm_enable_beacon_filter(mvm, vif); - else - ret = iwl_mvm_disable_beacon_filter(mvm, vif); + ret = iwl_mvm_enable_beacon_filter(mvm, vif); } mutex_unlock(&mvm->mutex); @@ -789,41 +920,41 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file, int pos = 0; const size_t bufsz = sizeof(buf); struct iwl_beacon_filter_cmd cmd = { - .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, - .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, - .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, - .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, - .bf_enable_beacon_filter = IWL_BF_ENABLE_BEACON_FILTER_DEFAULT, - .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, - .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), - .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT), - .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT, + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = + cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT), + .ba_enable_beacon_abort = + cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT), }; iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); - if (mvmvif->bf_enabled) - cmd.bf_enable_beacon_filter = 1; + if (mvmvif->bf_data.bf_enabled) + cmd.bf_enable_beacon_filter = cpu_to_le32(1); else cmd.bf_enable_beacon_filter = 0; pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", - cmd.bf_energy_delta); + le32_to_cpu(cmd.bf_energy_delta)); pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", - cmd.bf_roaming_energy_delta); + le32_to_cpu(cmd.bf_roaming_energy_delta)); pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", - cmd.bf_roaming_state); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temperature_delta = %d\n", - cmd.bf_temperature_delta); + le32_to_cpu(cmd.bf_roaming_state)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n", + le32_to_cpu(cmd.bf_temp_threshold)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n", + le32_to_cpu(cmd.bf_temp_fast_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n", + le32_to_cpu(cmd.bf_temp_slow_filter)); pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", - cmd.bf_enable_beacon_filter); + le32_to_cpu(cmd.bf_enable_beacon_filter)); pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", - cmd.bf_debug_flag); + le32_to_cpu(cmd.bf_debug_flag)); pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", - cmd.bf_escape_timer); + le32_to_cpu(cmd.bf_escape_timer)); pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", - cmd.ba_escape_timer); + le32_to_cpu(cmd.ba_escape_timer)); pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", - cmd.ba_enable_beacon_abort); + le32_to_cpu(cmd.ba_enable_beacon_abort)); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } @@ -934,6 +1065,7 @@ MVM_DEBUGFS_READ_FILE_OPS(stations); MVM_DEBUGFS_READ_FILE_OPS(bt_notif); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow); +MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart); #ifdef CONFIG_PM_SLEEP MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram); @@ -957,6 +1089,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); #ifdef CONFIG_PM_SLEEP MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index 6f8b2c1..df72fcdf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -98,34 +98,63 @@ enum iwl_proto_offloads { IWL_D3_PROTO_OFFLOAD_NS = BIT(1), }; -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS 2 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 6 /** - * struct iwl_proto_offload_cmd - ARP/NS offload configuration + * struct iwl_proto_offload_cmd_common - ARP/NS offload common part * @enabled: enable flags * @remote_ipv4_addr: remote address to answer to (or zero if all) * @host_ipv4_addr: our IPv4 address to respond to queries for * @arp_mac_addr: our MAC address for ARP responses - * @remote_ipv6_addr: remote address to answer to (or zero if all) - * @solicited_node_ipv6_addr: broken -- solicited node address exists - * for each target address - * @target_ipv6_addr: our target addresses - * @ndp_mac_addr: neighbor soliciation response MAC address + * @reserved: unused */ -struct iwl_proto_offload_cmd { +struct iwl_proto_offload_cmd_common { __le32 enabled; __be32 remote_ipv4_addr; __be32 host_ipv4_addr; u8 arp_mac_addr[ETH_ALEN]; - __le16 reserved1; + __le16 reserved; +} __packed; +/** + * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @remote_ipv6_addr: remote address to answer to (or zero if all) + * @solicited_node_ipv6_addr: broken -- solicited node address exists + * for each target address + * @target_ipv6_addr: our target addresses + * @ndp_mac_addr: neighbor soliciation response MAC address + */ +struct iwl_proto_offload_cmd_v1 { + struct iwl_proto_offload_cmd_common common; u8 remote_ipv6_addr[16]; u8 solicited_node_ipv6_addr[16]; - u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS][16]; + u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16]; u8 ndp_mac_addr[ETH_ALEN]; __le16 reserved2; } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */ +/** + * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @remote_ipv6_addr: remote address to answer to (or zero if all) + * @solicited_node_ipv6_addr: broken -- solicited node address exists + * for each target address + * @target_ipv6_addr: our target addresses + * @ndp_mac_addr: neighbor soliciation response MAC address + */ +struct iwl_proto_offload_cmd_v2 { + struct iwl_proto_offload_cmd_common common; + u8 remote_ipv6_addr[16]; + u8 solicited_node_ipv6_addr[16]; + u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16]; + u8 ndp_mac_addr[ETH_ALEN]; + u8 numValidIPv6Addresses; + u8 reserved2[3]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ + /* * WOWLAN_PATTERNS diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index a6da359..8e7ab41 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -79,6 +79,10 @@ * '1' Driver enables PM (use rest of parameters) * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM, * '1' PM could sleep over DTIM till listen Interval. + * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all + * access categories are both delivery and trigger enabled. + * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and + * PBW Snoozing enabled * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. */ @@ -86,6 +90,8 @@ enum iwl_power_flags { POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1), POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2), + POWER_FLAGS_SNOOZE_ENA_MSK = BIT(5), + POWER_FLAGS_BT_SCO_ENA = BIT(8), POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), POWER_FLAGS_LPRX_ENA_MSK = BIT(11), }; @@ -93,7 +99,8 @@ enum iwl_power_flags { #define IWL_POWER_VEC_SIZE 5 /** - * struct iwl_powertable_cmd - Power Table Command + * struct iwl_powertable_cmd - legacy power command. Beside old API support this + * is used also with a new power API for device wide power settings. * POWER_TABLE_CMD = 0x77 (command, has simple generic response) * * @flags: Power table command flags from POWER_FLAGS_* @@ -125,6 +132,76 @@ struct iwl_powertable_cmd { } __packed; /** + * struct iwl_mac_power_cmd - New power command containing uAPSD support + * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) + * @id_and_color: MAC contex identifier + * @flags: Power table command flags from POWER_FLAGS_* + * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. + * Minimum allowed:- 3 * DTIM. Keep alive period must be + * set regardless of power scheme or current power state. + * FW use this value also when PM is disabled. + * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to + * PSM transition - legacy PM + * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to + * PSM transition - legacy PM + * @sleep_interval: not in use + * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag + * is set. For example, if it is required to skip over + * one DTIM, this value need to be set to 2 (DTIM periods). + * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to + * PSM transition - uAPSD + * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to + * PSM transition - uAPSD + * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. + * Default: 80dbm + * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set + * @snooze_interval: Maximum time between attempts to retrieve buffered data + * from the AP [msec] + * @snooze_window: A window of time in which PBW snoozing insures that all + * packets received. It is also the minimum time from last + * received unicast RX packet, before client stops snoozing + * for data. [msec] + * @snooze_step: TBD + * @qndp_tid: TID client shall use for uAPSD QNDP triggers + * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for + * each corresponding AC. + * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values. + * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct + * values. + * @heavy_tx_thld_packets: TX threshold measured in number of packets + * @heavy_rx_thld_packets: RX threshold measured in number of packets + * @heavy_tx_thld_percentage: TX threshold measured in load's percentage + * @heavy_rx_thld_percentage: RX threshold measured in load's percentage + * @limited_ps_threshold: +*/ +struct iwl_mac_power_cmd { + /* CONTEXT_DESC_API_T_VER_1 */ + __le32 id_and_color; + + /* CLIENT_PM_POWER_TABLE_S_VER_1 */ + __le16 flags; + __le16 keep_alive_seconds; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 rx_data_timeout_uapsd; + __le32 tx_data_timeout_uapsd; + u8 lprx_rssi_threshold; + u8 skip_dtim_periods; + __le16 snooze_interval; + __le16 snooze_window; + u8 snooze_step; + u8 qndp_tid; + u8 uapsd_ac_flags; + u8 uapsd_max_sp; + u8 heavy_tx_thld_packets; + u8 heavy_rx_thld_packets; + u8 heavy_tx_thld_percentage; + u8 heavy_rx_thld_percentage; + u8 limited_ps_threshold; + u8 reserved; +} __packed; + +/** * struct iwl_beacon_filter_cmd * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) * @id_and_color: MAC contex identifier @@ -143,11 +220,21 @@ struct iwl_powertable_cmd { * calculated for current beacon is less than the threshold, use * Roaming Energy Delta Threshold, otherwise use normal Energy Delta * Threshold. Typical energy threshold is -72dBm. - * @bf_temperature_delta: Send Beacon to driver if delta in temperature values - * calculated for this and the last passed beacon is greater than this - * threshold. Zero value means that the temperature changeis ignored for + * @bf_temp_threshold: This threshold determines the type of temperature + * filtering (Slow or Fast) that is selected (Units are in Celsuis): + * If the current temperature is above this threshold - Fast filter + * will be used, If the current temperature is below this threshold - + * Slow filter will be used. + * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values + * calculated for this and the last passed beacon is greater than this + * threshold. Zero value means that the temperature change is ignored for * beacon filtering; beacons will not be forced to be sent to driver * regardless of whether its temerature has been changed. + * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values + * calculated for this and the last passed beacon is greater than this + * threshold. Zero value means that the temperature change is ignored for + * beacon filtering; beacons will not be forced to be sent to driver + * regardless of whether its temerature has been changed. * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed * for a specific period of time. Units: Beacons. @@ -156,17 +243,17 @@ struct iwl_powertable_cmd { * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. */ struct iwl_beacon_filter_cmd { - u8 bf_energy_delta; - u8 bf_roaming_energy_delta; - u8 bf_roaming_state; - u8 bf_temperature_delta; - u8 bf_enable_beacon_filter; - u8 bf_debug_flag; - __le16 reserved1; + __le32 bf_energy_delta; + __le32 bf_roaming_energy_delta; + __le32 bf_roaming_state; + __le32 bf_temp_threshold; + __le32 bf_temp_fast_filter; + __le32 bf_temp_slow_filter; + __le32 bf_enable_beacon_filter; + __le32 bf_debug_flag; __le32 bf_escape_timer; __le32 ba_escape_timer; - u8 ba_enable_beacon_abort; - u8 reserved2[3]; + __le32 ba_enable_beacon_abort; } __packed; /* Beacon filtering and beacon abort */ @@ -182,9 +269,17 @@ struct iwl_beacon_filter_cmd { #define IWL_BF_ROAMING_STATE_MAX 255 #define IWL_BF_ROAMING_STATE_MIN 0 -#define IWL_BF_TEMPERATURE_DELTA_DEFAULT 5 -#define IWL_BF_TEMPERATURE_DELTA_MAX 255 -#define IWL_BF_TEMPERATURE_DELTA_MIN 0 +#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112 +#define IWL_BF_TEMP_THRESHOLD_MAX 255 +#define IWL_BF_TEMP_THRESHOLD_MIN 0 + +#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1 +#define IWL_BF_TEMP_FAST_FILTER_MAX 255 +#define IWL_BF_TEMP_FAST_FILTER_MIN 0 + +#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5 +#define IWL_BF_TEMP_SLOW_FILTER_MAX 255 +#define IWL_BF_TEMP_SLOW_FILTER_MIN 0 #define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 @@ -194,19 +289,23 @@ struct iwl_beacon_filter_cmd { #define IWL_BF_ESCAPE_TIMER_MAX 1024 #define IWL_BF_ESCAPE_TIMER_MIN 0 -#define IWL_BA_ESCAPE_TIMER_DEFAULT 3 +#define IWL_BA_ESCAPE_TIMER_DEFAULT 6 +#define IWL_BA_ESCAPE_TIMER_D3 6 #define IWL_BA_ESCAPE_TIMER_MAX 1024 #define IWL_BA_ESCAPE_TIMER_MIN 0 #define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 -#define IWL_BF_CMD_CONFIG_DEFAULTS \ - .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, \ - .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, \ - .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, \ - .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, \ - .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, \ - .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \ +#define IWL_BF_CMD_CONFIG_DEFAULTS \ + .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA_DEFAULT), \ + .bf_roaming_energy_delta = \ + cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT), \ + .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE_DEFAULT), \ + .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD_DEFAULT), \ + .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER_DEFAULT), \ + .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER_DEFAULT), \ + .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG_DEFAULT), \ + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \ .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT) #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 365095a..83cb9b9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -137,6 +137,8 @@ struct iwl_ssid_ie { *@SCAN_FLAGS_DELAYED_SCAN_LOWBAND: *@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND: *@SCAN_FLAGS_FRAGMENTED_SCAN: + *@SCAN_FLAGS_PASSIVE2ACTIVE: use active scan on channels that was active + * in the past hour, even if they are marked as passive. */ enum iwl_scan_flags { SCAN_FLAGS_PERIODIC_SCAN = BIT(0), @@ -144,6 +146,7 @@ enum iwl_scan_flags { SCAN_FLAGS_DELAYED_SCAN_LOWBAND = BIT(2), SCAN_FLAGS_DELAYED_SCAN_HIGHBAND = BIT(3), SCAN_FLAGS_FRAGMENTED_SCAN = BIT(4), + SCAN_FLAGS_PASSIVE2ACTIVE = BIT(5), }; /** @@ -178,7 +181,7 @@ enum iwl_scan_type { * @quiet_time: in msecs, dwell this time for active scan on quiet channels * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than * this number of packets were received (typically 1) - * @passive2active: is auto switching from passive to active allowed (0 or 1) + * @passive2active: is auto switching from passive to active during scan allowed * @rxchain_sel_flags: RXON_RX_CHAIN_* * @max_out_time: in usecs, max out of serving channel time * @suspend_time: how long to pause scan when returning to service channel: diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index 700cce7..d606197 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -91,7 +91,6 @@ * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation - * @TX_CMD_FLG_CTS_ONLY: send CTS only, no data after that * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped * @TX_CMD_FLG_EXEC_PAPD: execute PAPD @@ -120,7 +119,6 @@ enum iwl_tx_flags { TX_CMD_FLG_RESP_TO_DRV = BIT(21), TX_CMD_FLG_CCMP_AGG = BIT(22), TX_CMD_FLG_TKIP_MIC_DONE = BIT(23), - TX_CMD_FLG_CTS_ONLY = BIT(24), TX_CMD_FLG_DUR = BIT(25), TX_CMD_FLG_FW_DROP = BIT(26), TX_CMD_FLG_EXEC_PAPD = BIT(27), diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index cbfb3be..66264cc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -136,7 +136,7 @@ enum { CALIB_RES_NOTIF_PHY_DB = 0x6b, /* PHY_DB_CMD = 0x6c, */ - /* Power */ + /* Power - legacy power table command */ POWER_TABLE_CMD = 0x77, /* Thermal Throttling*/ @@ -159,6 +159,7 @@ enum { TX_ANT_CONFIGURATION_CMD = 0x98, BT_CONFIG = 0x9b, STATISTICS_NOTIFICATION = 0x9d, + REDUCE_TX_POWER_CMD = 0x9f, /* RF-KILL commands and notifications */ CARD_STATE_CMD = 0xa0, @@ -166,6 +167,9 @@ enum { MISSED_BEACONS_NOTIFICATION = 0xa2, + /* Power - new power table command */ + MAC_PM_POWER_TABLE = 0xa9, + REPLY_RX_PHY_CMD = 0xc0, REPLY_RX_MPDU_CMD = 0xc1, BA_NOTIF = 0xc5, @@ -223,6 +227,19 @@ struct iwl_tx_ant_cfg_cmd { __le32 valid; } __packed; +/** + * struct iwl_reduce_tx_power_cmd - TX power reduction command + * REDUCE_TX_POWER_CMD = 0x9f + * @flags: (reserved for future implementation) + * @mac_context_id: id of the mac ctx for which we are reducing TX power. + * @pwr_restriction: TX power restriction in dBms. + */ +struct iwl_reduce_tx_power_cmd { + u8 flags; + u8 mac_context_id; + __le16 pwr_restriction; +} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */ + /* * Calibration control struct. * Sent as part of the phy configuration command. @@ -482,71 +499,199 @@ enum iwl_time_event_type { TE_MAX }; /* MAC_EVENT_TYPE_API_E_VER_1 */ + + +/* Time event - defines for command API v1 */ + +/* + * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed. + * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only + * the first fragment is scheduled. + * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only + * the first 2 fragments are scheduled. + * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any + * number of fragments are valid. + * + * Other than the constant defined above, specifying a fragmentation value 'x' + * means that the event can be fragmented but only the first 'x' will be + * scheduled. + */ +enum { + TE_V1_FRAG_NONE = 0, + TE_V1_FRAG_SINGLE = 1, + TE_V1_FRAG_DUAL = 2, + TE_V1_FRAG_ENDLESS = 0xffffffff +}; + +/* If a Time Event can be fragmented, this is the max number of fragments */ +#define TE_V1_FRAG_MAX_MSK 0x0fffffff +/* Repeat the time event endlessly (until removed) */ +#define TE_V1_REPEAT_ENDLESS 0xffffffff +/* If a Time Event has bounded repetitions, this is the maximal value */ +#define TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff + /* Time Event dependencies: none, on another TE, or in a specific time */ enum { - TE_INDEPENDENT = 0, - TE_DEP_OTHER = 1, - TE_DEP_TSF = 2, - TE_EVENT_SOCIOPATHIC = 4, + TE_V1_INDEPENDENT = 0, + TE_V1_DEP_OTHER = BIT(0), + TE_V1_DEP_TSF = BIT(1), + TE_V1_EVENT_SOCIOPATHIC = BIT(2), }; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */ + /* + * @TE_V1_NOTIF_NONE: no notifications + * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start + * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end + * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use + * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use. + * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start + * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end + * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use. + * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use. + * * Supported Time event notifications configuration. * A notification (both event and fragment) includes a status indicating weather * the FW was able to schedule the event or not. For fragment start/end * notification the status is always success. There is no start/end fragment * notification for monolithic events. - * - * @TE_NOTIF_NONE: no notifications - * @TE_NOTIF_HOST_EVENT_START: request/receive notification on event start - * @TE_NOTIF_HOST_EVENT_END:request/receive notification on event end - * @TE_NOTIF_INTERNAL_EVENT_START: internal FW use - * @TE_NOTIF_INTERNAL_EVENT_END: internal FW use. - * @TE_NOTIF_HOST_FRAG_START: request/receive notification on frag start - * @TE_NOTIF_HOST_FRAG_END:request/receive notification on frag end - * @TE_NOTIF_INTERNAL_FRAG_START: internal FW use. - * @TE_NOTIF_INTERNAL_FRAG_END: internal FW use. */ enum { - TE_NOTIF_NONE = 0, - TE_NOTIF_HOST_EVENT_START = 0x1, - TE_NOTIF_HOST_EVENT_END = 0x2, - TE_NOTIF_INTERNAL_EVENT_START = 0x4, - TE_NOTIF_INTERNAL_EVENT_END = 0x8, - TE_NOTIF_HOST_FRAG_START = 0x10, - TE_NOTIF_HOST_FRAG_END = 0x20, - TE_NOTIF_INTERNAL_FRAG_START = 0x40, - TE_NOTIF_INTERNAL_FRAG_END = 0x80 + TE_V1_NOTIF_NONE = 0, + TE_V1_NOTIF_HOST_EVENT_START = BIT(0), + TE_V1_NOTIF_HOST_EVENT_END = BIT(1), + TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2), + TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3), + TE_V1_NOTIF_HOST_FRAG_START = BIT(4), + TE_V1_NOTIF_HOST_FRAG_END = BIT(5), + TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6), + TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7), }; /* MAC_EVENT_ACTION_API_E_VER_2 */ + +/** + * struct iwl_time_event_cmd_api_v1 - configuring Time Events + * with struct MAC_TIME_EVENT_DATA_API_S_VER_1 (see also + * with version 2. determined by IWL_UCODE_TLV_FLAGS) + * ( TIME_EVENT_CMD = 0x29 ) + * @id_and_color: ID and color of the relevant MAC + * @action: action to perform, one of FW_CTXT_ACTION_* + * @id: this field has two meanings, depending on the action: + * If the action is ADD, then it means the type of event to add. + * For all other actions it is the unique event ID assigned when the + * event was added by the FW. + * @apply_time: When to start the Time Event (in GP2) + * @max_delay: maximum delay to event's start (apply time), in TU + * @depends_on: the unique ID of the event we depend on (if any) + * @interval: interval between repetitions, in TU + * @interval_reciprocal: 2^32 / interval + * @duration: duration of event in TU + * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS + * @dep_policy: one of TE_V1_INDEPENDENT, TE_V1_DEP_OTHER, TE_V1_DEP_TSF + * and TE_V1_EVENT_SOCIOPATHIC + * @is_present: 0 or 1, are we present or absent during the Time Event + * @max_frags: maximal number of fragments the Time Event can be divided to + * @notify: notifications using TE_V1_NOTIF_* (whom to notify when) + */ +struct iwl_time_event_cmd_v1 { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + __le32 id; + /* MAC_TIME_EVENT_DATA_API_S_VER_1 */ + __le32 apply_time; + __le32 max_delay; + __le32 dep_policy; + __le32 depends_on; + __le32 is_present; + __le32 max_frags; + __le32 interval; + __le32 interval_reciprocal; + __le32 duration; + __le32 repeat; + __le32 notify; +} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */ + + +/* Time event - defines for command API v2 */ + /* - * @TE_FRAG_NONE: fragmentation of the time event is NOT allowed. - * @TE_FRAG_SINGLE: fragmentation of the time event is allowed, but only + * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed. + * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only * the first fragment is scheduled. - * @TE_FRAG_DUAL: fragmentation of the time event is allowed, but only + * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only * the first 2 fragments are scheduled. - * @TE_FRAG_ENDLESS: fragmentation of the time event is allowed, and any number - * of fragments are valid. + * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any + * number of fragments are valid. * * Other than the constant defined above, specifying a fragmentation value 'x' * means that the event can be fragmented but only the first 'x' will be * scheduled. */ enum { - TE_FRAG_NONE = 0, - TE_FRAG_SINGLE = 1, - TE_FRAG_DUAL = 2, - TE_FRAG_ENDLESS = 0xffffffff + TE_V2_FRAG_NONE = 0, + TE_V2_FRAG_SINGLE = 1, + TE_V2_FRAG_DUAL = 2, + TE_V2_FRAG_MAX = 0xfe, + TE_V2_FRAG_ENDLESS = 0xff }; /* Repeat the time event endlessly (until removed) */ -#define TE_REPEAT_ENDLESS (0xffffffff) +#define TE_V2_REPEAT_ENDLESS 0xff /* If a Time Event has bounded repetitions, this is the maximal value */ -#define TE_REPEAT_MAX_MSK (0x0fffffff) -/* If a Time Event can be fragmented, this is the max number of fragments */ -#define TE_FRAG_MAX_MSK (0x0fffffff) +#define TE_V2_REPEAT_MAX 0xfe + +#define TE_V2_PLACEMENT_POS 12 +#define TE_V2_ABSENCE_POS 15 + +/* Time event policy values (for time event cmd api v2) + * A notification (both event and fragment) includes a status indicating weather + * the FW was able to schedule the event or not. For fragment start/end + * notification the status is always success. There is no start/end fragment + * notification for monolithic events. + * + * @TE_V2_DEFAULT_POLICY: independent, social, present, unoticable + * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start + * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end + * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use + * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use. + * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start + * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end + * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use. + * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use. + * @TE_V2_DEP_OTHER: depends on another time event + * @TE_V2_DEP_TSF: depends on a specific time + * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC + * @TE_V2_ABSENCE: are we present or absent during the Time Event. + */ +enum { + TE_V2_DEFAULT_POLICY = 0x0, + + /* notifications (event start/stop, fragment start/stop) */ + TE_V2_NOTIF_HOST_EVENT_START = BIT(0), + TE_V2_NOTIF_HOST_EVENT_END = BIT(1), + TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2), + TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3), + + TE_V2_NOTIF_HOST_FRAG_START = BIT(4), + TE_V2_NOTIF_HOST_FRAG_END = BIT(5), + TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6), + TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7), + + TE_V2_NOTIF_MSK = 0xff, + + /* placement characteristics */ + TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS), + TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1), + TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2), + + /* are we present or absent during the Time Event. */ + TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS), +}; /** - * struct iwl_time_event_cmd - configuring Time Events + * struct iwl_time_event_cmd_api_v2 - configuring Time Events + * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also + * with version 1. determined by IWL_UCODE_TLV_FLAGS) * ( TIME_EVENT_CMD = 0x29 ) * @id_and_color: ID and color of the relevant MAC * @action: action to perform, one of FW_CTXT_ACTION_* @@ -558,32 +703,30 @@ enum { * @max_delay: maximum delay to event's start (apply time), in TU * @depends_on: the unique ID of the event we depend on (if any) * @interval: interval between repetitions, in TU - * @interval_reciprocal: 2^32 / interval * @duration: duration of event in TU * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS - * @dep_policy: one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF - * @is_present: 0 or 1, are we present or absent during the Time Event * @max_frags: maximal number of fragments the Time Event can be divided to - * @notify: notifications using TE_NOTIF_* (whom to notify when) + * @policy: defines whether uCode shall notify the host or other uCode modules + * on event and/or fragment start and/or end + * using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF + * TE_EVENT_SOCIOPATHIC + * using TE_ABSENCE and using TE_NOTIF_* */ -struct iwl_time_event_cmd { +struct iwl_time_event_cmd_v2 { /* COMMON_INDEX_HDR_API_S_VER_1 */ __le32 id_and_color; __le32 action; __le32 id; - /* MAC_TIME_EVENT_DATA_API_S_VER_1 */ + /* MAC_TIME_EVENT_DATA_API_S_VER_2 */ __le32 apply_time; __le32 max_delay; - __le32 dep_policy; __le32 depends_on; - __le32 is_present; - __le32 max_frags; __le32 interval; - __le32 interval_reciprocal; __le32 duration; - __le32 repeat; - __le32 notify; -} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */ + u8 repeat; + u8 max_frags; + __le16 policy; +} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */ /** * struct iwl_time_event_resp - response structure to iwl_time_event_cmd @@ -765,6 +908,14 @@ struct iwl_phy_context_cmd { } __packed; /* PHY_CONTEXT_CMD_API_VER_1 */ #define IWL_RX_INFO_PHY_CNT 8 +#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1 +#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff +#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00 +#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000 +#define IWL_RX_INFO_ENERGY_ANT_A_POS 0 +#define IWL_RX_INFO_ENERGY_ANT_B_POS 8 +#define IWL_RX_INFO_ENERGY_ANT_C_POS 16 + #define IWL_RX_INFO_AGC_IDX 1 #define IWL_RX_INFO_RSSI_AB_IDX 2 #define IWL_OFDM_AGC_A_MSK 0x0000007f @@ -1170,7 +1321,7 @@ struct mvm_statistics_general { struct mvm_statistics_general_common common; __le32 beacon_filtered; __le32 missed_beacons; - __s8 beacon_filter_everage_energy; + __s8 beacon_filter_average_energy; __s8 beacon_filter_reason; __s8 beacon_filter_current_energy; __s8 beacon_filter_reserved; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index cd7c003..c76299a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -78,22 +78,6 @@ #define UCODE_VALID_OK cpu_to_le32(0x1) -/* Default calibration values for WkP - set to INIT image w/o running */ -static const u8 wkp_calib_values_rx_iq_skew[] = { 0x00, 0x00, 0x01, 0x00 }; -static const u8 wkp_calib_values_tx_iq_skew[] = { 0x01, 0x00, 0x00, 0x00 }; - -struct iwl_calib_default_data { - u16 size; - void *data; -}; - -#define CALIB_SIZE_N_DATA(_buf) {.size = sizeof(_buf), .data = &_buf} - -static const struct iwl_calib_default_data wkp_calib_default_data[12] = { - [9] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq_skew), - [11] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq_skew), -}; - struct iwl_mvm_alive_data { bool valid; u32 scd_base_addr; @@ -248,40 +232,6 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) sizeof(phy_cfg_cmd), &phy_cfg_cmd); } -static int iwl_set_default_calibrations(struct iwl_mvm *mvm) -{ - u8 cmd_raw[16]; /* holds the variable size commands */ - struct iwl_set_calib_default_cmd *cmd = - (struct iwl_set_calib_default_cmd *)cmd_raw; - int ret, i; - - /* Setting default values for calibrations we don't run */ - for (i = 0; i < ARRAY_SIZE(wkp_calib_default_data); i++) { - u16 cmd_len; - - if (wkp_calib_default_data[i].size == 0) - continue; - - memset(cmd_raw, 0, sizeof(cmd_raw)); - cmd_len = wkp_calib_default_data[i].size + sizeof(cmd); - cmd->calib_index = cpu_to_le16(i); - cmd->length = cpu_to_le16(wkp_calib_default_data[i].size); - if (WARN_ONCE(cmd_len > sizeof(cmd_raw), - "Need to enlarge cmd_raw to %d\n", cmd_len)) - break; - memcpy(cmd->data, wkp_calib_default_data[i].data, - wkp_calib_default_data[i].size); - ret = iwl_mvm_send_cmd_pdu(mvm, SET_CALIB_DEFAULT_CMD, 0, - sizeof(*cmd) + - wkp_calib_default_data[i].size, - cmd); - if (ret) - return ret; - } - - return 0; -} - int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) { struct iwl_notification_wait calib_wait; @@ -342,11 +292,6 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) if (ret) goto error; - /* need to set default values */ - ret = iwl_set_default_calibrations(mvm); - if (ret) - goto error; - /* * Send phy configurations command to init uCode * to start the 16.0 uCode init image internal calibrations. diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 94aae9c..5fe23a5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -264,7 +264,8 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, return 0; /* Therefore, in recovery, we can't get here */ - WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)); + if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))) + return -EBUSY; mvmvif->id = find_first_bit(data.available_mac_ids, NUM_MAC_INDEX_DRIVER); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index f19baf0..9833cdf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -153,7 +153,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TIMING_BEACON_ONLY | - IEEE80211_HW_CONNECTION_MONITOR; + IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | + IEEE80211_HW_SUPPORTS_STATIC_SMPS | + IEEE80211_HW_SUPPORTS_UAPSD; hw->queues = IWL_MVM_FIRST_AGG_QUEUE; hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; @@ -188,6 +191,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_remain_on_channel_duration = 10000; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; + hw->uapsd_queues = IWL_UAPSD_AC_INFO; + hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; /* Extract MAC address */ memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); @@ -506,7 +511,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); - /* Allocate resources for the MAC context, and add it the the fw */ + /* Allocate resources for the MAC context, and add it to the fw */ ret = iwl_mvm_mac_ctxt_init(mvm, vif); if (ret) goto out_unlock; @@ -552,6 +557,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, goto out_release; } + iwl_mvm_vif_dbgfs_register(mvm, vif); goto out_unlock; } @@ -566,16 +572,18 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, iwl_mvm_power_update_mode(mvm, vif); /* beacon filtering */ + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + if (ret) + goto out_remove_mac; + if (!mvm->bf_allowed_vif && - vif->type == NL80211_IFTYPE_STATION && !vif->p2p){ + vif->type == NL80211_IFTYPE_STATION && !vif->p2p && + mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){ mvm->bf_allowed_vif = mvmvif; - vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; } - ret = iwl_mvm_disable_beacon_filter(mvm, vif); - if (ret) - goto out_release; - /* * P2P_DEVICE interface does not have a channel context assigned to it, * so a dedicated PHY context is allocated to it and the corresponding @@ -586,7 +594,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); if (!mvmvif->phy_ctxt) { ret = -ENOSPC; - goto out_remove_mac; + goto out_free_bf; } iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); @@ -610,6 +618,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, iwl_mvm_binding_remove_vif(mvm, vif); out_unref_phy: iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + out_free_bf: + if (mvm->bf_allowed_vif == mvmvif) { + mvm->bf_allowed_vif = NULL; + vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI); + } out_remove_mac: mvmvif->phy_ctxt = NULL; iwl_mvm_mac_ctxt_remove(mvm, vif); @@ -674,7 +688,8 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, if (mvm->bf_allowed_vif == mvmvif) { mvm->bf_allowed_vif = NULL; - vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; + vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI); } iwl_mvm_vif_dbgfs_clean(mvm, vif); @@ -719,6 +734,20 @@ out_release: mutex_unlock(&mvm->mutex); } +static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + s8 tx_power) +{ + /* FW is in charge of regulatory enforcement */ + struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = { + .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id, + .pwr_restriction = cpu_to_le16(tx_power), + }; + + return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC, + sizeof(reduce_txpwr_cmd), + &reduce_txpwr_cmd); +} + static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) { return 0; @@ -766,7 +795,6 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, IWL_ERR(mvm, "failed to update quotas\n"); return; } - iwl_mvm_bt_coex_vif_assoc(mvm, vif); iwl_mvm_configure_mcast_filter(mvm, vif); } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { /* remove AP station now that the MAC is unassoc */ @@ -779,9 +807,19 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (ret) IWL_ERR(mvm, "failed to update quotas\n"); } - ret = iwl_mvm_power_update_mode(mvm, vif); - if (ret) - IWL_ERR(mvm, "failed to update power mode\n"); + + /* reset rssi values */ + mvmvif->bf_data.ave_beacon_signal = 0; + + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) { + /* Workaround for FW bug, otherwise FW disables device + * power save upon disassociation + */ + ret = iwl_mvm_power_update_mode(mvm, vif); + if (ret) + IWL_ERR(mvm, "failed to update power mode\n"); + } + iwl_mvm_bt_coex_vif_assoc(mvm, vif); } else if (changes & BSS_CHANGED_BEACON_INFO) { /* * We received a beacon _after_ association so @@ -789,11 +827,25 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, */ iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); - } else if (changes & BSS_CHANGED_PS) { + } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_QOS)) { ret = iwl_mvm_power_update_mode(mvm, vif); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); } + if (changes & BSS_CHANGED_TXPOWER) { + IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n", + bss_conf->txpower); + iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); + } + + if (changes & BSS_CHANGED_CQM) { + IWL_DEBUG_MAC80211(mvm, "cqm info_changed"); + /* reset cqm events tracking */ + mvmvif->bf_data.last_cqm_event = 0; + ret = iwl_mvm_update_beacon_filter(mvm, vif); + if (ret) + IWL_ERR(mvm, "failed to update CQM thresholds\n"); + } } static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 420e82d..b038927 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -76,6 +76,7 @@ #include "iwl-trans.h" #include "sta.h" #include "fw-api.h" +#include "constants.h" #define IWL_INVALID_MAC80211_QUEUE 0xff #define IWL_MVM_MAX_ADDRESSES 5 @@ -91,6 +92,9 @@ enum iwl_mvm_tx_fifo { }; extern struct ieee80211_ops iwl_mvm_hw_ops; +extern const struct iwl_mvm_power_ops pm_legacy_ops; +extern const struct iwl_mvm_power_ops pm_mac_ops; + /** * struct iwl_mvm_mod_params - module parameters for iwlmvm * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted. @@ -149,6 +153,22 @@ enum iwl_power_scheme { }; #define IWL_CONN_MAX_LISTEN_INTERVAL 70 +#define IWL_UAPSD_AC_INFO (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\ + IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |\ + IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |\ + IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) +#define IWL_UAPSD_MAX_SP IEEE80211_WMM_IE_STA_QOSINFO_SP_2 + +struct iwl_mvm_power_ops { + int (*power_update_mode)(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); + int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +#ifdef CONFIG_IWLWIFI_DEBUGFS + int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + char *buf, int bufsz); +#endif +}; + #ifdef CONFIG_IWLWIFI_DEBUGFS enum iwl_dbgfs_pm_mask { @@ -160,10 +180,11 @@ enum iwl_dbgfs_pm_mask { MVM_DEBUGFS_PM_DISABLE_POWER_OFF = BIT(5), MVM_DEBUGFS_PM_LPRX_ENA = BIT(6), MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7), + MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8), }; struct iwl_dbgfs_pm { - u8 keep_alive_seconds; + u16 keep_alive_seconds; u32 rx_data_timeout; u32 tx_data_timeout; bool skip_over_dtim; @@ -171,6 +192,7 @@ struct iwl_dbgfs_pm { bool disable_power_off; bool lprx_ena; u32 lprx_rssi_threshold; + bool snooze_ena; int mask; }; @@ -180,24 +202,28 @@ enum iwl_dbgfs_bf_mask { MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0), MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1), MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2), - MVM_DEBUGFS_BF_TEMPERATURE_DELTA = BIT(3), - MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(4), - MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(5), - MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(6), - MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(7), - MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(8), + MVM_DEBUGFS_BF_TEMP_THRESHOLD = BIT(3), + MVM_DEBUGFS_BF_TEMP_FAST_FILTER = BIT(4), + MVM_DEBUGFS_BF_TEMP_SLOW_FILTER = BIT(5), + MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(6), + MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(7), + MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(8), + MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(9), + MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(10), }; struct iwl_dbgfs_bf { - u8 bf_energy_delta; - u8 bf_roaming_energy_delta; - u8 bf_roaming_state; - u8 bf_temperature_delta; - u8 bf_enable_beacon_filter; - u8 bf_debug_flag; + u32 bf_energy_delta; + u32 bf_roaming_energy_delta; + u32 bf_roaming_state; + u32 bf_temp_threshold; + u32 bf_temp_fast_filter; + u32 bf_temp_slow_filter; + u32 bf_enable_beacon_filter; + u32 bf_debug_flag; u32 bf_escape_timer; u32 ba_escape_timer; - u8 ba_enable_beacon_abort; + u32 ba_enable_beacon_abort; int mask; }; #endif @@ -209,6 +235,21 @@ enum iwl_mvm_smps_type_request { }; /** +* struct iwl_mvm_vif_bf_data - beacon filtering related data +* @bf_enabled: indicates if beacon filtering is enabled +* @ba_enabled: indicated if beacon abort is enabled +* @last_beacon_signal: last beacon rssi signal in dbm +* @ave_beacon_signal: average beacon signal +* @last_cqm_event: rssi of the last cqm event +*/ +struct iwl_mvm_vif_bf_data { + bool bf_enabled; + bool ba_enabled; + s8 ave_beacon_signal; + s8 last_cqm_event; +}; + +/** * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context * @id: between 0 and 3 * @color: to solve races upon MAC addition and removal @@ -233,8 +274,7 @@ struct iwl_mvm_vif { bool uploaded; bool ap_active; bool monitor_active; - /* indicate whether beacon filtering is enabled */ - bool bf_enabled; + struct iwl_mvm_vif_bf_data bf_data; u32 ap_beacon_time; @@ -268,7 +308,7 @@ struct iwl_mvm_vif { #if IS_ENABLED(CONFIG_IPV6) /* IPv6 addresses for WoWLAN */ - struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS]; + struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; int num_target_ipv6_addrs; #endif #endif @@ -402,6 +442,8 @@ struct iwl_mvm { struct iwl_notif_wait_data notif_wait; + struct mvm_statistics_rx rx_stats; + unsigned long transport_queue_stop; u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; atomic_t queue_stop_count[IWL_MAX_HW_QUEUES]; @@ -459,6 +501,9 @@ struct iwl_mvm { */ u8 vif_count; + /* -1 for always, 0 for never, >0 for that many times */ + s8 restart_fw; + struct led_classdev led; struct ieee80211_vif *p2p_device_vif; @@ -482,6 +527,8 @@ struct iwl_mvm { /* Thermal Throttling and CTkill */ struct iwl_mvm_tt_mgmt thermal_throttle; s32 temperature; /* Celsius */ + + const struct iwl_mvm_power_ops *pm_ops; }; /* Extract MVM priv from op_mode and _hw */ @@ -525,6 +572,7 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx); void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); +void iwl_mvm_dump_sram(struct iwl_mvm *mvm); u8 first_antenna(u8 mask); u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); @@ -660,10 +708,26 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, u8 flags, bool init); /* power managment */ -int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_powertable_cmd *cmd); +static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + return mvm->pm_ops->power_update_mode(mvm, vif); +} + +static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + return mvm->pm_ops->power_disable(mvm, vif); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + char *buf, int bufsz) +{ + return mvm->pm_ops->power_dbgfs_read(mvm, vif, buf, bufsz); +} +#endif int iwl_mvm_leds_init(struct iwl_mvm *mvm); void iwl_mvm_leds_exit(struct iwl_mvm *mvm); @@ -707,6 +771,12 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, + struct iwl_beacon_filter_cmd *cmd); +int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, bool enable); +int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); /* SMPS */ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index af79a14..2fcc8ef 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -275,6 +275,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(BEACON_NOTIFICATION), CMD(BEACON_TEMPLATE_CMD), CMD(STATISTICS_NOTIFICATION), + CMD(REDUCE_TX_POWER_CMD), CMD(TX_ANT_CONFIGURATION_CMD), CMD(D3_CONFIG_CMD), CMD(PROT_OFFLOAD_CONFIG_CMD), @@ -301,6 +302,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(MCAST_FILTER_CMD), CMD(REPLY_BEACON_FILTERING_CMD), CMD(REPLY_THERMAL_MNG_BACKOFF), + CMD(MAC_PM_POWER_TABLE), }; #undef CMD @@ -340,6 +342,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->fw = fw; mvm->hw = hw; + mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; + mutex_init(&mvm->mutex); spin_lock_init(&mvm->async_handlers_lock); INIT_LIST_HEAD(&mvm->time_event_list); @@ -431,6 +435,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (err) goto out_unregister; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD) + mvm->pm_ops = &pm_mac_ops; + else + mvm->pm_ops = &pm_legacy_ops; + + memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); + return op_mode; out_unregister: @@ -638,6 +649,22 @@ static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) ieee80211_free_txskb(mvm->hw, skb); } +struct iwl_mvm_reprobe { + struct device *dev; + struct work_struct work; +}; + +static void iwl_mvm_reprobe_wk(struct work_struct *wk) +{ + struct iwl_mvm_reprobe *reprobe; + + reprobe = container_of(wk, struct iwl_mvm_reprobe, work); + if (device_reprobe(reprobe->dev)) + dev_err(reprobe->dev, "reprobe failed!\n"); + kfree(reprobe); + module_put(THIS_MODULE); +} + static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) { iwl_abort_notification_waits(&mvm->notif_wait); @@ -649,9 +676,30 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) * can't recover this since we're already half suspended. */ if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - IWL_ERR(mvm, "Firmware error during reconfiguration! Abort.\n"); - } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && - iwlwifi_mod_params.restart_fw) { + struct iwl_mvm_reprobe *reprobe; + + IWL_ERR(mvm, + "Firmware error during reconfiguration - reprobe!\n"); + + /* + * get a module reference to avoid doing this while unloading + * anyway and to avoid scheduling a work with code that's + * being removed. + */ + if (!try_module_get(THIS_MODULE)) { + IWL_ERR(mvm, "Module is being unloaded - abort\n"); + return; + } + + reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC); + if (!reprobe) { + module_put(THIS_MODULE); + return; + } + reprobe->dev = mvm->trans->dev; + INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); + schedule_work(&reprobe->work); + } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) { /* * This is a bit racy, but worst case we tell mac80211 about * a stopped/aborted (sched) scan when that was already done @@ -669,6 +717,8 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) break; } + if (mvm->restart_fw > 0) + mvm->restart_fw--; ieee80211_restart_hw(mvm->hw); } } @@ -678,6 +728,8 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); iwl_mvm_dump_nic_error_log(mvm); + if (!mvm->restart_fw) + iwl_mvm_dump_sram(mvm); iwl_mvm_nic_restart(mvm); } diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index e7ca965..21407a3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -75,8 +75,8 @@ #define POWER_KEEP_ALIVE_PERIOD_SEC 25 -static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, - struct iwl_beacon_filter_cmd *cmd) +int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, + struct iwl_beacon_filter_cmd *cmd) { int ret; @@ -85,69 +85,110 @@ static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, if (!ret) { IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", - cmd->ba_enable_beacon_abort); + le32_to_cpu(cmd->ba_enable_beacon_abort)); IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", - cmd->ba_escape_timer); + le32_to_cpu(cmd->ba_escape_timer)); IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", - cmd->bf_debug_flag); + le32_to_cpu(cmd->bf_debug_flag)); IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", - cmd->bf_enable_beacon_filter); + le32_to_cpu(cmd->bf_enable_beacon_filter)); IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", - cmd->bf_energy_delta); + le32_to_cpu(cmd->bf_energy_delta)); IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", - cmd->bf_escape_timer); + le32_to_cpu(cmd->bf_escape_timer)); IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", - cmd->bf_roaming_energy_delta); + le32_to_cpu(cmd->bf_roaming_energy_delta)); IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", - cmd->bf_roaming_state); - IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n", - cmd->bf_temperature_delta); + le32_to_cpu(cmd->bf_roaming_state)); + IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n", + le32_to_cpu(cmd->bf_temp_threshold)); + IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n", + le32_to_cpu(cmd->bf_temp_fast_filter)); + IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n", + le32_to_cpu(cmd->bf_temp_slow_filter)); } return ret; } -static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, bool enable) +static +void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif->bss_conf.cqm_rssi_thold) { + cmd->bf_energy_delta = + cpu_to_le32(vif->bss_conf.cqm_rssi_hyst); + /* fw uses an absolute value for this */ + cmd->bf_roaming_state = + cpu_to_le32(-vif->bss_conf.cqm_rssi_thold); + } + cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled); +} + +int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, bool enable) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_beacon_filter_cmd cmd = { IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = 1, - .ba_enable_beacon_abort = enable, + .bf_enable_beacon_filter = cpu_to_le32(1), + .ba_enable_beacon_abort = cpu_to_le32(enable), }; - if (!mvmvif->bf_enabled) + if (!mvmvif->bf_data.bf_enabled) return 0; + if (mvm->cur_ucode == IWL_UCODE_WOWLAN) + cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); + + mvmvif->bf_data.ba_enabled = enable; + iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd); iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); } static void iwl_mvm_power_log(struct iwl_mvm *mvm, - struct iwl_powertable_cmd *cmd) + struct iwl_mac_power_cmd *cmd) { IWL_DEBUG_POWER(mvm, - "Sending power table command for power level %d, flags = 0x%X\n", - iwlmvm_mod_params.power_scheme, + "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n", + cmd->id_and_color, iwlmvm_mod_params.power_scheme, le16_to_cpu(cmd->flags)); - IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds); - - if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { - IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n", - le32_to_cpu(cmd->rx_data_timeout)); - IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n", - le32_to_cpu(cmd->tx_data_timeout)); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) - IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", - le32_to_cpu(cmd->skip_dtim_periods)); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", - le32_to_cpu(cmd->lprx_rssi_threshold)); + IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", + le16_to_cpu(cmd->keep_alive_seconds)); + + if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) { + IWL_DEBUG_POWER(mvm, "Disable power management\n"); + return; + } + + IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n", + le32_to_cpu(cmd->rx_data_timeout)); + IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n", + le32_to_cpu(cmd->tx_data_timeout)); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) + IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", + cmd->skip_dtim_periods); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", + cmd->lprx_rssi_threshold); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { + IWL_DEBUG_POWER(mvm, "uAPSD enabled\n"); + IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n", + le32_to_cpu(cmd->rx_data_timeout_uapsd)); + IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n", + le32_to_cpu(cmd->tx_data_timeout_uapsd)); + IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid); + IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags); + IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp); } } -void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_powertable_cmd *cmd) +static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd) { struct ieee80211_hw *hw = mvm->hw; struct ieee80211_chanctx_conf *chanctx_conf; @@ -157,20 +198,29 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool radar_detect = false; struct iwl_mvm_vif *mvmvif __maybe_unused = iwl_mvm_vif_from_mac80211(vif); + enum ieee80211_ac_numbers ac; + bool tid_found = false; + + cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)); + dtimper = hw->conf.ps_dtim_period ?: 1; /* * Regardless of power management state the driver must set * keep alive period. FW will use it for sending keep alive NDPs - * immediately after association. + * immediately after association. Check that keep alive period + * is at least 3 * DTIM */ - cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC; + dtimper_msec = dtimper * vif->bss_conf.beacon_int; + keep_alive = max_t(int, 3 * dtimper_msec, + MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC); + keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC); + cmd->keep_alive_seconds = cpu_to_le16(keep_alive); if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); - if (!vif->bss_conf.assoc) - cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); #ifdef CONFIG_IWLWIFI_DEBUGFS if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && @@ -186,12 +236,9 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, (vif->bss_conf.beacon_rate->bitrate == 10 || vif->bss_conf.beacon_rate->bitrate == 60)) { cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); - cmd->lprx_rssi_threshold = - cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD); + cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD; } - dtimper = hw->conf.ps_dtim_period ?: 1; - /* Check if radar detection is required on current channel */ rcu_read_lock(); chanctx_conf = rcu_dereference(vif->chanctx_conf); @@ -207,27 +254,82 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP || mvm->cur_ucode == IWL_UCODE_WOWLAN)) { cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); - cmd->skip_dtim_periods = cpu_to_le32(3); + cmd->skip_dtim_periods = 3; } - /* Check that keep alive period is at least 3 * DTIM */ - dtimper_msec = dtimper * vif->bss_conf.beacon_int; - keep_alive = max_t(int, 3 * dtimper_msec, - MSEC_PER_SEC * cmd->keep_alive_seconds); - keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC); - cmd->keep_alive_seconds = keep_alive; - if (mvm->cur_ucode != IWL_UCODE_WOWLAN) { - cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); - cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + cmd->rx_data_timeout = + cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT); } else { - cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); - cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); + cmd->rx_data_timeout = + cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); + } + + for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { + if (!mvmvif->queue_params[ac].uapsd) + continue; + + cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); + cmd->uapsd_ac_flags |= BIT(ac); + + /* QNDP TID - the highest TID with no admission control */ + if (!tid_found && !mvmvif->queue_params[ac].acm) { + tid_found = true; + switch (ac) { + case IEEE80211_AC_VO: + cmd->qndp_tid = 6; + break; + case IEEE80211_AC_VI: + cmd->qndp_tid = 5; + break; + case IEEE80211_AC_BE: + cmd->qndp_tid = 0; + break; + case IEEE80211_AC_BK: + cmd->qndp_tid = 1; + break; + } + } + } + + if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); + + if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | + BIT(IEEE80211_AC_VI) | + BIT(IEEE80211_AC_BE) | + BIT(IEEE80211_AC_BK))) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); + cmd->snooze_interval = + cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); + cmd->snooze_window = + (mvm->cur_ucode == IWL_UCODE_WOWLAN) ? + cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : + cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); + } + + cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP; + cmd->heavy_tx_thld_packets = + IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; + cmd->heavy_rx_thld_packets = + IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; + cmd->heavy_tx_thld_percentage = + IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; + cmd->heavy_rx_thld_percentage = + IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; } #ifdef CONFIG_IWLWIFI_DEBUGFS if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) - cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds; + cmd->keep_alive_seconds = + cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds); if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { if (mvmvif->dbgfs_pm.skip_over_dtim) cmd->flags |= @@ -243,8 +345,7 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cmd->tx_data_timeout = cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) - cmd->skip_dtim_periods = - cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods); + cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods; if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) { if (mvmvif->dbgfs_pm.lprx_ena) cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); @@ -252,16 +353,24 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK); } if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD) - cmd->lprx_rssi_threshold = - cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold); + cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold; + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) { + if (mvmvif->dbgfs_pm.snooze_ena) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); + else + cmd->flags &= + cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK); + } #endif /* CONFIG_IWLWIFI_DEBUGFS */ } -int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { int ret; bool ba_enable; - struct iwl_powertable_cmd cmd = {}; + struct iwl_mac_power_cmd cmd = {}; if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; @@ -280,7 +389,7 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) iwl_mvm_power_build_cmd(mvm, vif, &cmd); iwl_mvm_power_log(mvm, &cmd); - ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC, sizeof(cmd), &cmd); if (ret) return ret; @@ -291,15 +400,19 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable); } -int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { - struct iwl_powertable_cmd cmd = {}; + struct iwl_mac_power_cmd cmd = {}; struct iwl_mvm_vif *mvmvif __maybe_unused = iwl_mvm_vif_from_mac80211(vif); if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; + cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)); + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); @@ -310,11 +423,98 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) #endif iwl_mvm_power_log(mvm, &cmd); - return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC, + return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC, sizeof(cmd), &cmd); } #ifdef CONFIG_IWLWIFI_DEBUGFS +static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, char *buf, + int bufsz) +{ + struct iwl_mac_power_cmd cmd = {}; + int pos = 0; + + iwl_mvm_power_build_cmd(mvm, vif, &cmd); + + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? + 0 : 1); + pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", + iwlmvm_mod_params.power_scheme); + pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", + le16_to_cpu(cmd.flags)); + pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", + le16_to_cpu(cmd.keep_alive_seconds)); + + if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { + pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? + 1 : 0); + pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", + cmd.skip_dtim_periods); + if (!(cmd.flags & + cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { + pos += scnprintf(buf+pos, bufsz-pos, + "rx_data_timeout = %d\n", + le32_to_cpu(cmd.rx_data_timeout)); + pos += scnprintf(buf+pos, bufsz-pos, + "tx_data_timeout = %d\n", + le32_to_cpu(cmd.tx_data_timeout)); + } + if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + pos += scnprintf(buf+pos, bufsz-pos, + "lprx_rssi_threshold = %d\n", + cmd.lprx_rssi_threshold); + if (cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { + pos += + scnprintf(buf+pos, bufsz-pos, + "rx_data_timeout_uapsd = %d\n", + le32_to_cpu(cmd.rx_data_timeout_uapsd)); + pos += + scnprintf(buf+pos, bufsz-pos, + "tx_data_timeout_uapsd = %d\n", + le32_to_cpu(cmd.tx_data_timeout_uapsd)); + pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", + cmd.qndp_tid); + pos += scnprintf(buf+pos, bufsz-pos, + "uapsd_ac_flags = 0x%x\n", + cmd.uapsd_ac_flags); + pos += scnprintf(buf+pos, bufsz-pos, + "uapsd_max_sp = %d\n", + cmd.uapsd_max_sp); + pos += scnprintf(buf+pos, bufsz-pos, + "heavy_tx_thld_packets = %d\n", + cmd.heavy_tx_thld_packets); + pos += scnprintf(buf+pos, bufsz-pos, + "heavy_rx_thld_packets = %d\n", + cmd.heavy_rx_thld_packets); + pos += scnprintf(buf+pos, bufsz-pos, + "heavy_tx_thld_percentage = %d\n", + cmd.heavy_tx_thld_percentage); + pos += scnprintf(buf+pos, bufsz-pos, + "heavy_rx_thld_percentage = %d\n", + cmd.heavy_rx_thld_percentage); + pos += + scnprintf(buf+pos, bufsz-pos, "snooze_enable = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) ? + 1 : 0); + } + if (cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { + pos += scnprintf(buf+pos, bufsz-pos, + "snooze_interval = %d\n", + cmd.snooze_interval); + pos += scnprintf(buf+pos, bufsz-pos, + "snooze_window = %d\n", + cmd.snooze_window); + } + } + return pos; +} + void iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, struct iwl_beacon_filter_cmd *cmd) @@ -323,22 +523,30 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) - cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta; + cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) cmd->bf_roaming_energy_delta = - dbgfs_bf->bf_roaming_energy_delta; + cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) - cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state; - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA) - cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta; + cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD) + cmd->bf_temp_threshold = + cpu_to_le32(dbgfs_bf->bf_temp_threshold); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER) + cmd->bf_temp_fast_filter = + cpu_to_le32(dbgfs_bf->bf_temp_fast_filter); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER) + cmd->bf_temp_slow_filter = + cpu_to_le32(dbgfs_bf->bf_temp_slow_filter); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) - cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag; + cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) - cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort; + cmd->ba_enable_beacon_abort = + cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort); } #endif @@ -348,7 +556,7 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_beacon_filter_cmd cmd = { IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = 1, + .bf_enable_beacon_filter = cpu_to_le32(1), }; int ret; @@ -356,11 +564,12 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; + iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd); iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); if (!ret) - mvmvif->bf_enabled = true; + mvmvif->bf_data.bf_enabled = true; return ret; } @@ -372,13 +581,33 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED) || + vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); if (!ret) - mvmvif->bf_enabled = false; + mvmvif->bf_data.bf_enabled = false; return ret; } + +int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!mvmvif->bf_data.bf_enabled) + return 0; + + return iwl_mvm_enable_beacon_filter(mvm, vif); +} + +const struct iwl_mvm_power_ops pm_mac_ops = { + .power_update_mode = iwl_mvm_power_mac_update_mode, + .power_disable = iwl_mvm_power_mac_disable, +#ifdef CONFIG_IWLWIFI_DEBUGFS + .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read, +#endif +}; diff --git a/drivers/net/wireless/iwlwifi/mvm/power_legacy.c b/drivers/net/wireless/iwlwifi/mvm/power_legacy.c new file mode 100644 index 0000000..2ce79ba --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/power_legacy.c @@ -0,0 +1,319 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> + +#include <net/mac80211.h> + +#include "iwl-debug.h" +#include "mvm.h" +#include "iwl-modparams.h" +#include "fw-api-power.h" + +#define POWER_KEEP_ALIVE_PERIOD_SEC 25 + +static void iwl_mvm_power_log(struct iwl_mvm *mvm, + struct iwl_powertable_cmd *cmd) +{ + IWL_DEBUG_POWER(mvm, + "Sending power table command for power level %d, flags = 0x%X\n", + iwlmvm_mod_params.power_scheme, + le16_to_cpu(cmd->flags)); + IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds); + + if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { + IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n", + le32_to_cpu(cmd->rx_data_timeout)); + IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n", + le32_to_cpu(cmd->tx_data_timeout)); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) + IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", + le32_to_cpu(cmd->skip_dtim_periods)); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", + le32_to_cpu(cmd->lprx_rssi_threshold)); + } +} + +static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_powertable_cmd *cmd) +{ + struct ieee80211_hw *hw = mvm->hw; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *chan; + int dtimper, dtimper_msec; + int keep_alive; + bool radar_detect = false; + struct iwl_mvm_vif *mvmvif __maybe_unused = + iwl_mvm_vif_from_mac80211(vif); + + /* + * Regardless of power management state the driver must set + * keep alive period. FW will use it for sending keep alive NDPs + * immediately after association. + */ + cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC; + + if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) + return; + + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); + if (!vif->bss_conf.assoc) + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && + mvmvif->dbgfs_pm.disable_power_off) + cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif + if (!vif->bss_conf.ps) + return; + + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); + + if (vif->bss_conf.beacon_rate && + (vif->bss_conf.beacon_rate->bitrate == 10 || + vif->bss_conf.beacon_rate->bitrate == 60)) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); + cmd->lprx_rssi_threshold = + cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD); + } + + dtimper = hw->conf.ps_dtim_period ?: 1; + + /* Check if radar detection is required on current channel */ + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + WARN_ON(!chanctx_conf); + if (chanctx_conf) { + chan = chanctx_conf->def.chan; + radar_detect = chan->flags & IEEE80211_CHAN_RADAR; + } + rcu_read_unlock(); + + /* Check skip over DTIM conditions */ + if (!radar_detect && (dtimper <= 10) && + (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP || + mvm->cur_ucode == IWL_UCODE_WOWLAN)) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + cmd->skip_dtim_periods = cpu_to_le32(3); + } + + /* Check that keep alive period is at least 3 * DTIM */ + dtimper_msec = dtimper * vif->bss_conf.beacon_int; + keep_alive = max_t(int, 3 * dtimper_msec, + MSEC_PER_SEC * cmd->keep_alive_seconds); + keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC); + cmd->keep_alive_seconds = keep_alive; + + if (mvm->cur_ucode != IWL_UCODE_WOWLAN) { + cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + } else { + cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); + cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); + } + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) + cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds; + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { + if (mvmvif->dbgfs_pm.skip_over_dtim) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + else + cmd->flags &= + cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK); + } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT) + cmd->rx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT) + cmd->tx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) + cmd->skip_dtim_periods = + cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) { + if (mvmvif->dbgfs_pm.lprx_ena) + cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); + else + cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK); + } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD) + cmd->lprx_rssi_threshold = + cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold); +#endif /* CONFIG_IWLWIFI_DEBUGFS */ +} + +static int iwl_mvm_power_legacy_update_mode(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + int ret; + bool ba_enable; + struct iwl_powertable_cmd cmd = {}; + + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + /* + * TODO: The following vif_count verification is temporary condition. + * Avoid power mode update if more than one interface is currently + * active. Remove this condition when FW will support power management + * on multiple MACs. + */ + IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n", + mvm->vif_count); + if (mvm->vif_count > 1) + return 0; + + iwl_mvm_power_build_cmd(mvm, vif, &cmd); + iwl_mvm_power_log(mvm, &cmd); + + ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, + sizeof(cmd), &cmd); + if (ret) + return ret; + + ba_enable = !!(cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)); + + return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable); +} + +static int iwl_mvm_power_legacy_disable(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_powertable_cmd cmd = {}; + struct iwl_mvm_vif *mvmvif __maybe_unused = + iwl_mvm_vif_from_mac80211(vif); + + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) + cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && + mvmvif->dbgfs_pm.disable_power_off) + cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif + iwl_mvm_power_log(mvm, &cmd); + + return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC, + sizeof(cmd), &cmd); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static int iwl_mvm_power_legacy_dbgfs_read(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, char *buf, + int bufsz) +{ + struct iwl_powertable_cmd cmd = {}; + int pos = 0; + + iwl_mvm_power_build_cmd(mvm, vif, &cmd); + + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? + 0 : 1); + pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", + le32_to_cpu(cmd.skip_dtim_periods)); + pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", + iwlmvm_mod_params.power_scheme); + pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", + le16_to_cpu(cmd.flags)); + pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", + cmd.keep_alive_seconds); + + if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { + pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? + 1 : 0); + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", + le32_to_cpu(cmd.rx_data_timeout)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", + le32_to_cpu(cmd.tx_data_timeout)); + if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + pos += scnprintf(buf+pos, bufsz-pos, + "lprx_rssi_threshold = %d\n", + le32_to_cpu(cmd.lprx_rssi_threshold)); + } + return pos; +} +#endif + +const struct iwl_mvm_power_ops pm_legacy_ops = { + .power_update_mode = iwl_mvm_power_legacy_update_mode, + .power_disable = iwl_mvm_power_legacy_disable, +#ifdef CONFIG_IWLWIFI_DEBUGFS + .power_dbgfs_read = iwl_mvm_power_legacy_dbgfs_read, +#endif +}; diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 29d49cf..5c6ae16 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -131,23 +131,22 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) { - struct iwl_time_quota_cmd cmd; - int i, idx, ret, num_active_bindings, quota, quota_rem; + struct iwl_time_quota_cmd cmd = {}; + int i, idx, ret, num_active_macs, quota, quota_rem; struct iwl_mvm_quota_iterator_data data = { .n_interfaces = {}, .colors = { -1, -1, -1, -1 }, .new_vif = newvif, }; + lockdep_assert_held(&mvm->mutex); + /* update all upon completion */ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) return 0; - BUILD_BUG_ON(data.colors[MAX_BINDINGS - 1] != -1); - - lockdep_assert_held(&mvm->mutex); - - memset(&cmd, 0, sizeof(cmd)); + /* iterator data above must match */ + BUILD_BUG_ON(MAX_BINDINGS != 4); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, @@ -162,18 +161,17 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) * IWL_MVM_MAX_QUOTA fragments. Divide these fragments * equally between all the bindings that require quota */ - num_active_bindings = 0; + num_active_macs = 0; for (i = 0; i < MAX_BINDINGS; i++) { cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID); - if (data.n_interfaces[i] > 0) - num_active_bindings++; + num_active_macs += data.n_interfaces[i]; } quota = 0; quota_rem = 0; - if (num_active_bindings) { - quota = IWL_MVM_MAX_QUOTA / num_active_bindings; - quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings; + if (num_active_macs) { + quota = IWL_MVM_MAX_QUOTA / num_active_macs; + quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs; } for (idx = 0, i = 0; i < MAX_BINDINGS; i++) { @@ -187,7 +185,8 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) cmd.quotas[idx].quota = cpu_to_le32(0); cmd.quotas[idx].max_duration = cpu_to_le32(0); } else { - cmd.quotas[idx].quota = cpu_to_le32(quota); + cmd.quotas[idx].quota = + cpu_to_le32(quota * data.n_interfaces[i]); cmd.quotas[idx].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA); } diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index b328a98..4ffaa3f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -56,61 +56,61 @@ #define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) static u8 rs_ht_to_legacy[] = { - IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX + [IWL_RATE_1M_INDEX] = IWL_RATE_6M_INDEX, + [IWL_RATE_2M_INDEX] = IWL_RATE_6M_INDEX, + [IWL_RATE_5M_INDEX] = IWL_RATE_6M_INDEX, + [IWL_RATE_11M_INDEX] = IWL_RATE_6M_INDEX, + [IWL_RATE_6M_INDEX] = IWL_RATE_6M_INDEX, + [IWL_RATE_9M_INDEX] = IWL_RATE_6M_INDEX, + [IWL_RATE_12M_INDEX] = IWL_RATE_9M_INDEX, + [IWL_RATE_18M_INDEX] = IWL_RATE_12M_INDEX, + [IWL_RATE_24M_INDEX] = IWL_RATE_18M_INDEX, + [IWL_RATE_36M_INDEX] = IWL_RATE_24M_INDEX, + [IWL_RATE_48M_INDEX] = IWL_RATE_36M_INDEX, + [IWL_RATE_54M_INDEX] = IWL_RATE_48M_INDEX, + [IWL_RATE_60M_INDEX] = IWL_RATE_54M_INDEX, }; static const u8 ant_toggle_lookup[] = { - /*ANT_NONE -> */ ANT_NONE, - /*ANT_A -> */ ANT_B, - /*ANT_B -> */ ANT_C, - /*ANT_AB -> */ ANT_BC, - /*ANT_C -> */ ANT_A, - /*ANT_AC -> */ ANT_AB, - /*ANT_BC -> */ ANT_AC, - /*ANT_ABC -> */ ANT_ABC, + [ANT_NONE] = ANT_NONE, + [ANT_A] = ANT_B, + [ANT_B] = ANT_C, + [ANT_AB] = ANT_BC, + [ANT_C] = ANT_A, + [ANT_AC] = ANT_AB, + [ANT_BC] = ANT_AC, + [ANT_ABC] = ANT_ABC, }; -#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ +#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ IWL_RATE_SISO_##s##M_PLCP, \ IWL_RATE_MIMO2_##s##M_PLCP,\ - IWL_RATE_MIMO3_##s##M_PLCP,\ - IWL_RATE_##r##M_IEEE, \ - IWL_RATE_##ip##M_INDEX, \ - IWL_RATE_##in##M_INDEX, \ IWL_RATE_##rp##M_INDEX, \ - IWL_RATE_##rn##M_INDEX, \ - IWL_RATE_##pp##M_INDEX, \ - IWL_RATE_##np##M_INDEX } + IWL_RATE_##rn##M_INDEX } /* * Parameter order: - * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate + * rate, ht rate, prev rate, next rate * * If there isn't a valid next or previous rate then INV is used which * maps to IWL_RATE_INVALID * */ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = { - IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ - IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ - IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ - IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ - IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ - IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ - IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ - IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ - IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ - IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ - IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ - IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ - IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ + IWL_DECLARE_RATE_INFO(1, INV, INV, 2), /* 1mbps */ + IWL_DECLARE_RATE_INFO(2, INV, 1, 5), /* 2mbps */ + IWL_DECLARE_RATE_INFO(5, INV, 2, 11), /*5.5mbps */ + IWL_DECLARE_RATE_INFO(11, INV, 9, 12), /* 11mbps */ + IWL_DECLARE_RATE_INFO(6, 6, 5, 11), /* 6mbps */ + IWL_DECLARE_RATE_INFO(9, 6, 6, 11), /* 9mbps */ + IWL_DECLARE_RATE_INFO(12, 12, 11, 18), /* 12mbps */ + IWL_DECLARE_RATE_INFO(18, 18, 12, 24), /* 18mbps */ + IWL_DECLARE_RATE_INFO(24, 24, 18, 36), /* 24mbps */ + IWL_DECLARE_RATE_INFO(36, 36, 24, 48), /* 36mbps */ + IWL_DECLARE_RATE_INFO(48, 48, 36, 54), /* 48mbps */ + IWL_DECLARE_RATE_INFO(54, 54, 48, INV), /* 54mbps */ + IWL_DECLARE_RATE_INFO(60, 60, 48, INV), /* 60mbps */ /* FIXME:RS: ^^ should be INV (legacy) */ }; @@ -128,9 +128,8 @@ static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) if (rate_n_flags & RATE_MCS_HT_MSK) { idx = rs_extract_rate(rate_n_flags); - if (idx >= IWL_RATE_MIMO3_6M_PLCP) - idx = idx - IWL_RATE_MIMO3_6M_PLCP; - else if (idx >= IWL_RATE_MIMO2_6M_PLCP) + WARN_ON_ONCE(idx >= IWL_RATE_MIMO3_6M_PLCP); + if (idx >= IWL_RATE_MIMO2_6M_PLCP) idx = idx - IWL_RATE_MIMO2_6M_PLCP; idx += IWL_FIRST_OFDM_RATE; @@ -162,10 +161,10 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); #ifdef CONFIG_MAC80211_DEBUGFS static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags, int index); + u32 *rate_n_flags); #else static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags, int index) + u32 *rate_n_flags) {} #endif @@ -212,20 +211,6 @@ static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ }; -static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */ - {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */ - {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */ - {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */ -}; - -static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */ - {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */ - {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */ - {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */ -}; - /* mbps, mcs */ static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { { "1", "BPSK DSSS"}, @@ -260,82 +245,6 @@ static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) return (ant_type & valid_antenna) == ant_type; } -/* - * removes the old data from the statistics. All data that is older than - * TID_MAX_TIME_DIFF, will be deleted. - */ -static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time) -{ - /* The oldest age we want to keep */ - u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; - - while (tl->queue_count && - (tl->time_stamp < oldest_time)) { - tl->total -= tl->packet_count[tl->head]; - tl->packet_count[tl->head] = 0; - tl->time_stamp += TID_QUEUE_CELL_SPACING; - tl->queue_count--; - tl->head++; - if (tl->head >= TID_QUEUE_MAX_SIZE) - tl->head = 0; - } -} - -/* - * increment traffic load value for tid and also remove - * any old values if passed the certain time period - */ -static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, - struct ieee80211_hdr *hdr) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 index; - struct iwl_traffic_load *tl = NULL; - u8 tid; - - if (ieee80211_is_data_qos(hdr->frame_control)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; - } else { - return IWL_MAX_TID_COUNT; - } - - if (unlikely(tid >= IWL_MAX_TID_COUNT)) - return IWL_MAX_TID_COUNT; - - tl = &lq_data->load[tid]; - - curr_time -= curr_time % TID_ROUND_VALUE; - - /* Happens only for the first packet. Initialize the data */ - if (!(tl->queue_count)) { - tl->total = 1; - tl->time_stamp = curr_time; - tl->queue_count = 1; - tl->head = 0; - tl->packet_count[0] = 1; - return IWL_MAX_TID_COUNT; - } - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - index = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (index >= TID_QUEUE_MAX_SIZE) - rs_tl_rm_old_stats(tl, curr_time); - - index = (tl->head + index) % TID_QUEUE_MAX_SIZE; - tl->packet_count[index] = tl->packet_count[index] + 1; - tl->total = tl->total + 1; - - if ((index + 1) > tl->queue_count) - tl->queue_count = index + 1; - - return tid; -} - #ifdef CONFIG_MAC80211_DEBUGFS /** * Program the device to use fixed rate for frame transmit @@ -349,7 +258,6 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm, lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); @@ -361,45 +269,11 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm, } #endif -/* - get the traffic load value for tid -*/ -static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 index; - struct iwl_traffic_load *tl = NULL; - - if (tid >= IWL_MAX_TID_COUNT) - return 0; - - tl = &(lq_data->load[tid]); - - curr_time -= curr_time % TID_ROUND_VALUE; - - if (!(tl->queue_count)) - return 0; - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - index = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (index >= TID_QUEUE_MAX_SIZE) - rs_tl_rm_old_stats(tl, curr_time); - - return tl->total; -} - static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_data, u8 tid, struct ieee80211_sta *sta) { int ret = -EAGAIN; - u32 load; - - load = rs_tl_get_load(lq_data, tid); /* * Don't create TX aggregation sessions when in high @@ -563,7 +437,7 @@ static u32 rate_n_flags_from_tbl(struct iwl_mvm *mvm, else if (is_mimo2(tbl->lq_type)) rate_n_flags |= iwl_rates[index].plcp_mimo2; else - rate_n_flags |= iwl_rates[index].plcp_mimo3; + WARN_ON_ONCE(1); } else { IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type); } @@ -601,7 +475,7 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); u8 mcs; - memset(tbl, 0, sizeof(struct iwl_scale_tbl_info)); + memset(tbl, 0, offsetof(struct iwl_scale_tbl_info, win)); *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); if (*rate_idx == IWL_RATE_INVALID) { @@ -640,12 +514,8 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { if (num_of_ant == 2) tbl->lq_type = LQ_MIMO2; - /* MIMO3 */ } else { - if (num_of_ant == 3) { - tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; - tbl->lq_type = LQ_MIMO3; - } + WARN_ON_ONCE(num_of_ant == 3); } } return 0; @@ -711,10 +581,10 @@ static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, } else { if (is_siso(rate_type)) return lq_sta->active_siso_rate; - else if (is_mimo2(rate_type)) + else { + WARN_ON_ONCE(!is_mimo2(rate_type)); return lq_sta->active_mimo2_rate; - else - return lq_sta->active_mimo3_rate; + } } } @@ -1089,7 +959,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, } /* Choose among many HT tables depending on number of streams - * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation + * (SISO/MIMO2), channel width (20/40), SGI, and aggregation * status */ if (is_siso(tbl->lq_type) && !tbl->is_ht40) ht_tbl_pointer = expected_tpt_siso20MHz; @@ -1097,12 +967,10 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, ht_tbl_pointer = expected_tpt_siso40MHz; else if (is_mimo2(tbl->lq_type) && !tbl->is_ht40) ht_tbl_pointer = expected_tpt_mimo2_20MHz; - else if (is_mimo2(tbl->lq_type)) + else { + WARN_ON_ONCE(!is_mimo2(tbl->lq_type)); ht_tbl_pointer = expected_tpt_mimo2_40MHz; - else if (is_mimo3(tbl->lq_type) && !tbl->is_ht40) - ht_tbl_pointer = expected_tpt_mimo3_20MHz; - else /* if (is_mimo3(tbl->lq_type)) <-- must be true */ - ht_tbl_pointer = expected_tpt_mimo3_40MHz; + } if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ tbl->expected_tpt = ht_tbl_pointer[0]; @@ -1274,58 +1142,6 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm, } /* - * Set up search table for MIMO3 - */ -static int rs_switch_to_mimo3(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - s32 rate; - s8 is_green = lq_sta->is_green; - - if (!sta->ht_cap.ht_supported) - return -1; - - if (sta->smps_mode == IEEE80211_SMPS_STATIC) - return -1; - - /* Need both Tx chains/antennas to support MIMO */ - if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) < 3) - return -1; - - IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO3\n"); - - tbl->lq_type = LQ_MIMO3; - tbl->action = 0; - tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; - rate_mask = lq_sta->active_mimo3_rate; - - if (iwl_is_ht40_tx_allowed(sta)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - rs_set_expected_tpt_table(lq_sta, tbl); - - rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(mvm, "LQ: MIMO3 best rate %d mask %X\n", - rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(mvm, "Can't switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green); - - IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* * Set up search table for SISO */ static int rs_switch_to_siso(struct iwl_mvm *mvm, @@ -1434,21 +1250,14 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm, } break; - case IWL_LEGACY_SWITCH_MIMO2_AB: - case IWL_LEGACY_SWITCH_MIMO2_AC: - case IWL_LEGACY_SWITCH_MIMO2_BC: + case IWL_LEGACY_SWITCH_MIMO2: IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO2\n"); /* Set up search table to try MIMO */ memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; - if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; + search_tbl->ant_type = ANT_AB; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) @@ -1461,30 +1270,11 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm, goto out; } break; - - case IWL_LEGACY_SWITCH_MIMO3_ABC: - IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO3\n"); - - /* Set up search table to try MIMO3 */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - search_tbl->ant_type = ANT_ABC; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo3(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - break; + default: + WARN_ON_ONCE(1); } tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) + if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; if (tbl->action == start_action) @@ -1496,7 +1286,7 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm, out: lq_sta->search_better_tbl = 1; tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) + if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; @@ -1531,7 +1321,7 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, case IWL_BT_COEX_TRAFFIC_LOAD_LOW: /* avoid antenna B unless MIMO */ if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) - tbl->action = IWL_SISO_SWITCH_MIMO2_AB; + tbl->action = IWL_SISO_SWITCH_MIMO2; break; case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: @@ -1573,19 +1363,12 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, goto out; } break; - case IWL_SISO_SWITCH_MIMO2_AB: - case IWL_SISO_SWITCH_MIMO2_AC: - case IWL_SISO_SWITCH_MIMO2_BC: + case IWL_SISO_SWITCH_MIMO2: IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO2\n"); memcpy(search_tbl, tbl, sz); search_tbl->is_SGI = 0; - if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; + search_tbl->ant_type = ANT_AB; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) @@ -1626,24 +1409,11 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, index, is_green); update_search_tbl_counter = 1; goto out; - case IWL_SISO_SWITCH_MIMO3_ABC: - IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO3\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - search_tbl->ant_type = ANT_ABC; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo3(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) - goto out; - break; + default: + WARN_ON_ONCE(1); } tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) + if (tbl->action > IWL_SISO_SWITCH_GI) tbl->action = IWL_SISO_SWITCH_ANTENNA1; if (tbl->action == start_action) @@ -1655,7 +1425,7 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, out: lq_sta->search_better_tbl = 1; tbl->action++; - if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC) + if (tbl->action > IWL_SISO_SWITCH_GI) tbl->action = IWL_SISO_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; @@ -1696,8 +1466,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, break; case IWL_BT_COEX_TRAFFIC_LOAD_LOW: /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_MIMO2_SWITCH_SISO_B || - tbl->action == IWL_MIMO2_SWITCH_SISO_C) + if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) tbl->action = IWL_MIMO2_SWITCH_SISO_A; break; default: @@ -1730,7 +1499,6 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, break; case IWL_MIMO2_SWITCH_SISO_A: case IWL_MIMO2_SWITCH_SISO_B: - case IWL_MIMO2_SWITCH_SISO_C: IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n"); /* Set up new search table for SISO */ @@ -1738,10 +1506,8 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) search_tbl->ant_type = ANT_A; - else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) + else /* tbl->action == IWL_MIMO2_SWITCH_SISO_B */ search_tbl->ant_type = ANT_B; - else - search_tbl->ant_type = ANT_C; if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) @@ -1784,26 +1550,11 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, index, is_green); update_search_tbl_counter = 1; goto out; - - case IWL_MIMO2_SWITCH_MIMO3_ABC: - IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to MIMO3\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - search_tbl->ant_type = ANT_ABC; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo3(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) - goto out; - - break; + default: + WARN_ON_ONCE(1); } tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) + if (tbl->action > IWL_MIMO2_SWITCH_GI) tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; if (tbl->action == start_action) @@ -1814,7 +1565,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, out: lq_sta->search_better_tbl = 1; tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) + if (tbl->action > IWL_MIMO2_SWITCH_GI) tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; if (update_search_tbl_counter) search_tbl->action = tbl->action; @@ -1823,171 +1574,6 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, } /* - * Try to switch to new modulation mode from MIMO3 - */ -static int rs_move_mimo3_to_other(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, int index) -{ - s8 is_green = lq_sta->is_green; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - u8 tx_chains_num = num_of_ant(valid_tx_ant); - int ret; - u8 update_search_tbl_counter = 0; - - switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - if (tbl->action != IWL_MIMO3_SWITCH_SISO_A) - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_MIMO3_SWITCH_SISO_B || - tbl->action == IWL_MIMO3_SWITCH_SISO_C) - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - break; - default: - IWL_ERR(mvm, "Invalid BT load %d", - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)); - break; - } - - start_action = tbl->action; - while (1) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_MIMO3_SWITCH_ANTENNA1: - case IWL_MIMO3_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(mvm, "LQ: MIMO3 toggle Antennas\n"); - - if (tx_chains_num <= 3) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, - search_tbl)) - goto out; - break; - case IWL_MIMO3_SWITCH_SISO_A: - case IWL_MIMO3_SWITCH_SISO_B: - case IWL_MIMO3_SWITCH_SISO_C: - IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to SISO\n"); - - /* Set up new search table for SISO */ - memcpy(search_tbl, tbl, sz); - - if (tbl->action == IWL_MIMO3_SWITCH_SISO_A) - search_tbl->ant_type = ANT_A; - else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B) - search_tbl->ant_type = ANT_B; - else - search_tbl->ant_type = ANT_C; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_siso(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - - case IWL_MIMO3_SWITCH_MIMO2_AB: - case IWL_MIMO3_SWITCH_MIMO2_AC: - case IWL_MIMO3_SWITCH_MIMO2_BC: - IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to MIMO2\n"); - - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - - case IWL_MIMO3_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - break; - - IWL_DEBUG_RATE(mvm, "LQ: MIMO3 toggle SGI/NGI\n"); - - /* Set up new search table for MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - /* - * If active table already uses the fastest possible - * modulation (dual stream with short guard interval), - * and it's working well, there's no need to look - * for a better type of modulation! - */ - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(mvm, search_tbl, - index, is_green); - update_search_tbl_counter = 1; - goto out; - } - tbl->action++; - if (tbl->action > IWL_MIMO3_SWITCH_GI) - tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_MIMO3_SWITCH_GI) - tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - - return 0; -} - -/* * Check whether we should continue using same modulation mode, or * begin search for a new mode, based on: * 1) # tx successes or failures while using this mode @@ -2086,6 +1672,22 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm, iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false); } +static u8 rs_get_tid(struct iwl_lq_sta *lq_data, + struct ieee80211_hdr *hdr) +{ + u8 tid = IWL_MAX_TID_COUNT; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } + + if (unlikely(tid > IWL_MAX_TID_COUNT)) + tid = IWL_MAX_TID_COUNT; + + return tid; +} + /* * Do rate scaling and search for new modulation mode. */ @@ -2129,7 +1731,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; - tid = rs_tl_add_packet(lq_sta, hdr); + tid = rs_get_tid(lq_sta, hdr); if ((tid != IWL_MAX_TID_COUNT) && (lq_sta->tx_agg_tid_en & (1 << tid))) { tid_data = &sta_priv->tid_data[tid]; @@ -2377,8 +1979,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, scale_action = 0; if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= - IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && - (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { + IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && (is_mimo(tbl->lq_type))) { if (lq_sta->last_bt_traffic > BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { /* @@ -2395,8 +1996,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD); if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= - IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && - (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { + IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && is_mimo(tbl->lq_type)) { /* search for a new modulation */ rs_stay_in_table(lq_sta, true); goto lq_update; @@ -2456,7 +2056,7 @@ lq_update: else if (is_mimo2(tbl->lq_type)) rs_move_mimo2_to_other(mvm, lq_sta, sta, index); else - rs_move_mimo3_to_other(mvm, lq_sta, sta, index); + WARN_ON_ONCE(1); /* If new "search" mode was selected, set up in uCode table */ if (lq_sta->search_better_tbl) { @@ -2621,11 +2221,10 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, rate_idx -= IWL_FIRST_OFDM_RATE; /* 6M and 9M shared same MCS index */ rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; + WARN_ON_ONCE(rs_extract_rate(lq_sta->last_rate_n_flags) >= + IWL_RATE_MIMO3_6M_PLCP); if (rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO3_6M_PLCP) - rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM); - else if (rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO2_6M_PLCP) + IWL_RATE_MIMO2_6M_PLCP) rate_idx = rate_idx + MCS_INDEX_PER_STREAM; info->control.rates[0].flags = IEEE80211_TX_RC_MCS; if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) @@ -2688,9 +2287,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->flush_timer = 0; lq_sta->supp_rates = sta->supp_rates[sband->band]; - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); IWL_DEBUG_RATE(mvm, "LQ: *** rate scale station global init for station %d ***\n", @@ -2727,16 +2323,10 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->active_mimo2_rate &= ~((u16)0x2); lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1; - lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1; - lq_sta->active_mimo3_rate &= ~((u16)0x2); - lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; - IWL_DEBUG_RATE(mvm, - "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n", + "SISO-RATE=%X MIMO2-RATE=%X\n", lq_sta->active_siso_rate, - lq_sta->active_mimo2_rate, - lq_sta->active_mimo3_rate); + lq_sta->active_mimo2_rate); /* These values will be overridden later */ lq_sta->lq.single_stream_ant_msk = @@ -2780,7 +2370,7 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; /* Override starting rate (index 0) if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + rs_dbgfs_set_mcs(lq_sta, &new_rate); /* Interpret new_rate (rate_n_flags) */ rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, @@ -2827,7 +2417,7 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, } /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + rs_dbgfs_set_mcs(lq_sta, &new_rate); /* Fill next table entry */ lq_cmd->rs_table[index] = @@ -2869,7 +2459,7 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, use_ht_possible = 0; /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + rs_dbgfs_set_mcs(lq_sta, &new_rate); /* Fill next table entry */ lq_cmd->rs_table[index] = cpu_to_le32(new_rate); @@ -2914,7 +2504,7 @@ static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta, #ifdef CONFIG_MAC80211_DEBUGFS static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags, int index) + u32 *rate_n_flags) { struct iwl_mvm *mvm; u8 valid_tx_ant; @@ -2999,8 +2589,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); if (is_Ht(tbl->lq_type)) { desc += sprintf(buff+desc, " %s", - (is_siso(tbl->lq_type)) ? "SISO" : - ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); + (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2"); desc += sprintf(buff+desc, " %s", (tbl->is_ht40) ? "40MHz" : "20MHz"); desc += sprintf(buff+desc, " %s %s %s\n", @@ -3100,32 +2689,6 @@ static const struct file_operations rs_sta_dbgfs_stats_table_ops = { .llseek = default_llseek, }; -static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; - char buff[120]; - int desc = 0; - - if (is_Ht(tbl->lq_type)) - desc += sprintf(buff+desc, - "Bit Rate= %d Mb/s\n", - tbl->expected_tpt[lq_sta->last_txrate_idx]); - else - desc += sprintf(buff+desc, - "Bit Rate= %d Mb/s\n", - iwl_rates[lq_sta->last_txrate_idx].ieee >> 1); - - return simple_read_from_buffer(user_buf, count, ppos, buff, desc); -} - -static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { - .read = rs_sta_dbgfs_rate_scale_data_read, - .open = simple_open, - .llseek = default_llseek, -}; - static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) { struct iwl_lq_sta *lq_sta = mvm_sta; @@ -3135,9 +2698,6 @@ static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) lq_sta->rs_sta_dbgfs_stats_table_file = debugfs_create_file("rate_stats_table", S_IRUSR, dir, lq_sta, &rs_sta_dbgfs_stats_table_ops); - lq_sta->rs_sta_dbgfs_rate_scale_data_file = - debugfs_create_file("rate_scale_data", S_IRUSR, dir, - lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, &lq_sta->tx_agg_tid_en); @@ -3148,7 +2708,6 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta) struct iwl_lq_sta *lq_sta = mvm_sta; debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); } #endif @@ -3159,8 +2718,9 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta) * station is added we ignore it. */ static void rs_rate_init_stub(void *mvm_r, - struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *mvm_sta) + struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *mvm_sta) { } static struct rate_control_ops rs_mvm_ops = { @@ -3193,13 +2753,14 @@ void iwl_mvm_rate_control_unregister(void) * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable * Tx protection, according to this rquest and previous requests, * and send the LQ command. - * @lq: The LQ command * @mvmsta: The station * @enable: Enable Tx protection? */ -int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, - struct iwl_mvm_sta *mvmsta, bool enable) +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, + bool enable) { + struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq; + lockdep_assert_held(&mvm->mutex); if (enable) { diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index cff4f6d..335cf16 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -38,14 +38,8 @@ struct iwl_rs_rate_info { u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ - u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ - u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ - u8 prev_ieee; /* previous rate in IEEE speeds */ - u8 next_ieee; /* next rate in IEEE speeds */ u8 prev_rs; /* previous rate used in rs algo */ u8 next_rs; /* next rate used in rs algo */ - u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ - u8 next_rs_tgg; /* next rate used in TGG rs algo */ }; #define IWL_RATE_60M_PLCP 3 @@ -120,23 +114,6 @@ enum { IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, }; -/* MAC header values for bit rates */ -enum { - IWL_RATE_6M_IEEE = 12, - IWL_RATE_9M_IEEE = 18, - IWL_RATE_12M_IEEE = 24, - IWL_RATE_18M_IEEE = 36, - IWL_RATE_24M_IEEE = 48, - IWL_RATE_36M_IEEE = 72, - IWL_RATE_48M_IEEE = 96, - IWL_RATE_54M_IEEE = 108, - IWL_RATE_60M_IEEE = 120, - IWL_RATE_1M_IEEE = 2, - IWL_RATE_2M_IEEE = 4, - IWL_RATE_5M_IEEE = 11, - IWL_RATE_11M_IEEE = 22, -}; - #define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) #define IWL_INVALID_VALUE -1 @@ -165,47 +142,22 @@ enum { #define IWL_LEGACY_SWITCH_ANTENNA1 0 #define IWL_LEGACY_SWITCH_ANTENNA2 1 #define IWL_LEGACY_SWITCH_SISO 2 -#define IWL_LEGACY_SWITCH_MIMO2_AB 3 -#define IWL_LEGACY_SWITCH_MIMO2_AC 4 -#define IWL_LEGACY_SWITCH_MIMO2_BC 5 -#define IWL_LEGACY_SWITCH_MIMO3_ABC 6 +#define IWL_LEGACY_SWITCH_MIMO2 3 /* possible actions when in siso mode */ #define IWL_SISO_SWITCH_ANTENNA1 0 #define IWL_SISO_SWITCH_ANTENNA2 1 -#define IWL_SISO_SWITCH_MIMO2_AB 2 -#define IWL_SISO_SWITCH_MIMO2_AC 3 -#define IWL_SISO_SWITCH_MIMO2_BC 4 -#define IWL_SISO_SWITCH_GI 5 -#define IWL_SISO_SWITCH_MIMO3_ABC 6 - +#define IWL_SISO_SWITCH_MIMO2 2 +#define IWL_SISO_SWITCH_GI 3 /* possible actions when in mimo mode */ #define IWL_MIMO2_SWITCH_ANTENNA1 0 #define IWL_MIMO2_SWITCH_ANTENNA2 1 #define IWL_MIMO2_SWITCH_SISO_A 2 #define IWL_MIMO2_SWITCH_SISO_B 3 -#define IWL_MIMO2_SWITCH_SISO_C 4 -#define IWL_MIMO2_SWITCH_GI 5 -#define IWL_MIMO2_SWITCH_MIMO3_ABC 6 - +#define IWL_MIMO2_SWITCH_GI 4 -/* possible actions when in mimo3 mode */ -#define IWL_MIMO3_SWITCH_ANTENNA1 0 -#define IWL_MIMO3_SWITCH_ANTENNA2 1 -#define IWL_MIMO3_SWITCH_SISO_A 2 -#define IWL_MIMO3_SWITCH_SISO_B 3 -#define IWL_MIMO3_SWITCH_SISO_C 4 -#define IWL_MIMO3_SWITCH_MIMO2_AB 5 -#define IWL_MIMO3_SWITCH_MIMO2_AC 6 -#define IWL_MIMO3_SWITCH_MIMO2_BC 7 -#define IWL_MIMO3_SWITCH_GI 8 - - -#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI -#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC - -/*FIXME:RS:add possible actions for MIMO3*/ +#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_GI #define IWL_ACTION_LIMIT 3 /* # possible actions */ @@ -240,15 +192,13 @@ enum iwl_table_type { LQ_A, LQ_SISO, /* high-throughput types */ LQ_MIMO2, - LQ_MIMO3, LQ_MAX, }; #define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) #define is_siso(tbl) ((tbl) == LQ_SISO) #define is_mimo2(tbl) ((tbl) == LQ_MIMO2) -#define is_mimo3(tbl) ((tbl) == LQ_MIMO3) -#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl)) +#define is_mimo(tbl) is_mimo2(tbl) #define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) #define is_a_band(tbl) ((tbl) == LQ_A) #define is_g_and(tbl) ((tbl) == LQ_G) @@ -290,17 +240,6 @@ struct iwl_scale_tbl_info { struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ }; -struct iwl_traffic_load { - unsigned long time_stamp; /* age of the oldest statistics */ - u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time - * slice */ - u32 total; /* total num of packets during the - * last TID_MAX_TIME_DIFF */ - u8 queue_count; /* number of queues that has - * been used since the last cleanup */ - u8 head; /* start of the circular buffer */ -}; - /** * struct iwl_lq_sta -- driver's rate scaling private structure * @@ -331,18 +270,15 @@ struct iwl_lq_sta { u16 active_legacy_rate; u16 active_siso_rate; u16 active_mimo2_rate; - u16 active_mimo3_rate; s8 max_rate_idx; /* Max rate set by user */ u8 missed_rate_counter; struct iwl_lq_cmd lq; struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ - struct iwl_traffic_load load[IWL_MAX_TID_COUNT]; u8 tx_agg_tid_en; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *rs_sta_dbgfs_scale_table_file; struct dentry *rs_sta_dbgfs_stats_table_file; - struct dentry *rs_sta_dbgfs_rate_scale_data_file; struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; u32 dbg_fixed_rate; #endif @@ -404,7 +340,7 @@ extern void iwl_mvm_rate_control_unregister(void); struct iwl_mvm_sta; -int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, - struct iwl_mvm_sta *mvmsta, bool enable); +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, + bool enable); #endif /* __rs__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index e4930d5..2a8cb5a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -124,24 +124,15 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, ieee80211_rx_ni(mvm->hw, skb); } -/* - * iwl_mvm_calc_rssi - calculate the rssi in dBm - * @phy_info: the phy information for the coming packet - */ -static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm, - struct iwl_rx_phy_info *phy_info) +static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm, + struct iwl_rx_phy_info *phy_info, + struct ieee80211_rx_status *rx_status) { int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; int rssi_all_band_a, rssi_all_band_b; u32 agc_a, agc_b, max_agc; u32 val; - /* Find max rssi among 2 possible receivers. - * These values are measured by the Digital Signal Processor (DSP). - * They should stay fairly constant even as the signal strength varies, - * if the radio's Automatic Gain Control (AGC) is working right. - * AGC value (see below) will provide the "interesting" info. - */ val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]); agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS; agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS; @@ -166,7 +157,51 @@ static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm, IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b); - return max_rssi_dbm; + rx_status->signal = max_rssi_dbm; + rx_status->chains = (le16_to_cpu(phy_info->phy_flags) & + RX_RES_PHY_FLAGS_ANTENNA) + >> RX_RES_PHY_FLAGS_ANTENNA_POS; + rx_status->chain_signal[0] = rssi_a_dbm; + rx_status->chain_signal[1] = rssi_b_dbm; +} + +/* + * iwl_mvm_get_signal_strength - use new rx PHY INFO API + * values are reported by the fw as positive values - need to negate + * to obtain their dBM. Account for missing antennas by replacing 0 + * values by -256dBm: practically 0 power and a non-feasible 8 bit value. + */ +static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, + struct iwl_rx_phy_info *phy_info, + struct ieee80211_rx_status *rx_status) +{ + int energy_a, energy_b, energy_c, max_energy; + u32 val; + + val = + le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]); + energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >> + IWL_RX_INFO_ENERGY_ANT_A_POS; + energy_a = energy_a ? -energy_a : -256; + energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >> + IWL_RX_INFO_ENERGY_ANT_B_POS; + energy_b = energy_b ? -energy_b : -256; + energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >> + IWL_RX_INFO_ENERGY_ANT_C_POS; + energy_c = energy_c ? -energy_c : -256; + max_energy = max(energy_a, energy_b); + max_energy = max(max_energy, energy_c); + + IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n", + energy_a, energy_b, energy_c, max_energy); + + rx_status->signal = max_energy; + rx_status->chains = (le16_to_cpu(phy_info->phy_flags) & + RX_RES_PHY_FLAGS_ANTENNA) + >> RX_RES_PHY_FLAGS_ANTENNA_POS; + rx_status->chain_signal[0] = energy_a; + rx_status->chain_signal[1] = energy_b; + rx_status->chain_signal[2] = energy_c; } /* @@ -289,29 +324,14 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, */ /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ - /* Find max signal strength (dBm) among 3 antenna/receiver chains */ - rx_status.signal = iwl_mvm_calc_rssi(mvm, phy_info); + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_RX_ENERGY_API) + iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status); + else + iwl_mvm_calc_rssi(mvm, phy_info, &rx_status); IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal, (unsigned long long)rx_status.mactime); - /* - * "antenna number" - * - * It seems that the antenna field in the phy flags value - * is actually a bit field. This is undefined by radiotap, - * it wants an actual antenna number but I always get "7" - * for most legacy frames I receive indicating that the - * same frame was received on all three RX chains. - * - * I think this field should be removed in favor of a - * new 802.11n radiotap field "RX chains" that is defined - * as a bitmask. - */ - rx_status.antenna = (le16_to_cpu(phy_info->phy_flags) & - RX_RES_PHY_FLAGS_ANTENNA) - >> RX_RES_PHY_FLAGS_ANTENNA_POS; - /* set the preamble flag if appropriate */ if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE)) rx_status.flag |= RX_FLAG_SHORTPRE; @@ -364,11 +384,74 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, return 0; } +static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm, + struct iwl_notif_statistics *stats) +{ + /* + * NOTE FW aggregates the statistics - BUT the statistics are cleared + * when the driver issues REPLY_STATISTICS_CMD 0x9c with CLEAR_STATS + * bit set. + */ + lockdep_assert_held(&mvm->mutex); + memcpy(&mvm->rx_stats, &stats->rx, sizeof(struct mvm_statistics_rx)); +} + +struct iwl_mvm_stat_data { + struct iwl_notif_statistics *stats; + struct iwl_mvm *mvm; +}; + +static void iwl_mvm_stat_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_stat_data *data = _data; + struct iwl_notif_statistics *stats = data->stats; + struct iwl_mvm *mvm = data->mvm; + int sig = -stats->general.beacon_filter_average_energy; + int last_event; + int thold = vif->bss_conf.cqm_rssi_thold; + int hyst = vif->bss_conf.cqm_rssi_hyst; + u16 id = le32_to_cpu(stats->rx.general.mac_id); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (mvmvif->id != id) + return; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + mvmvif->bf_data.ave_beacon_signal = sig; + + if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) + return; + + /* CQM Notification */ + last_event = mvmvif->bf_data.last_cqm_event; + if (thold && sig < thold && (last_event == 0 || + sig < last_event - hyst)) { + mvmvif->bf_data.last_cqm_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n", + sig); + ieee80211_cqm_rssi_notify( + vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + GFP_KERNEL); + } else if (sig > thold && + (last_event == 0 || sig > last_event + hyst)) { + mvmvif->bf_data.last_cqm_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n", + sig); + ieee80211_cqm_rssi_notify( + vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + GFP_KERNEL); + } +} + /* * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler * * TODO: This handler is implemented partially. - * It only gets the NIC's temperature. */ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, @@ -377,11 +460,20 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_notif_statistics *stats = (void *)&pkt->data; struct mvm_statistics_general_common *common = &stats->general.common; + struct iwl_mvm_stat_data data = { + .stats = stats, + .mvm = mvm, + }; if (mvm->temperature != le32_to_cpu(common->temperature)) { mvm->temperature = le32_to_cpu(common->temperature); iwl_mvm_tt_handler(mvm); } + iwl_mvm_update_rx_statistics(mvm, stats); + ieee80211_iterate_active_interfaces(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_stat_iterator, + &data); return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index acdff6b..9a7ab84 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -301,10 +301,12 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, */ if (req->n_ssids > 0) { cmd->passive2active = cpu_to_le16(1); + cmd->scan_flags |= SCAN_FLAGS_PASSIVE2ACTIVE; ssid = req->ssids[0].ssid; ssid_len = req->ssids[0].ssid_len; } else { cmd->passive2active = 0; + cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE; } iwl_mvm_scan_fill_ssids(cmd, req); diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 563f559..44add29 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -826,8 +826,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * method for HT traffic * this function also sends the LQ command */ - return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq, - mvmsta, true); + return iwl_mvm_tx_protection(mvm, mvmsta, true); /* * TODO: remove the TLC_RTS flag when we tear down the last * AGG session (agg_tids_count in DVM) diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 7fd6fbf..c17b74c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -73,7 +73,6 @@ #include "iwl-prph.h" /* A TimeUnit is 1024 microsecond */ -#define TU_TO_JIFFIES(_tu) (usecs_to_jiffies((_tu) * 1024)) #define MSEC_TO_TU(_msec) (_msec*1000/1024) /* @@ -185,7 +184,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, } } - if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_END) { + if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) { IWL_DEBUG_TE(mvm, "TE ended - current time %lu, estimated end %lu\n", jiffies, te_data->end_jiffies); @@ -202,10 +201,9 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, iwl_mvm_te_check_disconnect(mvm, te_data->vif, "No assocation and the time event is over already..."); iwl_mvm_te_clear_data(mvm, te_data); - } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) { + } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) { te_data->running = true; - te_data->end_jiffies = jiffies + - TU_TO_JIFFIES(te_data->duration); + te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration); if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); @@ -270,10 +268,67 @@ static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, return true; } +/* used to convert from time event API v2 to v1 */ +#define TE_V2_DEP_POLICY_MSK (TE_V2_DEP_OTHER | TE_V2_DEP_TSF |\ + TE_V2_EVENT_SOCIOPATHIC) +static inline u16 te_v2_get_notify(__le16 policy) +{ + return le16_to_cpu(policy) & TE_V2_NOTIF_MSK; +} + +static inline u16 te_v2_get_dep_policy(__le16 policy) +{ + return (le16_to_cpu(policy) & TE_V2_DEP_POLICY_MSK) >> + TE_V2_PLACEMENT_POS; +} + +static inline u16 te_v2_get_absence(__le16 policy) +{ + return (le16_to_cpu(policy) & TE_V2_ABSENCE) >> TE_V2_ABSENCE_POS; +} + +static void iwl_mvm_te_v2_to_v1(const struct iwl_time_event_cmd_v2 *cmd_v2, + struct iwl_time_event_cmd_v1 *cmd_v1) +{ + cmd_v1->id_and_color = cmd_v2->id_and_color; + cmd_v1->action = cmd_v2->action; + cmd_v1->id = cmd_v2->id; + cmd_v1->apply_time = cmd_v2->apply_time; + cmd_v1->max_delay = cmd_v2->max_delay; + cmd_v1->depends_on = cmd_v2->depends_on; + cmd_v1->interval = cmd_v2->interval; + cmd_v1->duration = cmd_v2->duration; + if (cmd_v2->repeat == TE_V2_REPEAT_ENDLESS) + cmd_v1->repeat = cpu_to_le32(TE_V1_REPEAT_ENDLESS); + else + cmd_v1->repeat = cpu_to_le32(cmd_v2->repeat); + cmd_v1->max_frags = cpu_to_le32(cmd_v2->max_frags); + cmd_v1->interval_reciprocal = 0; /* unused */ + + cmd_v1->dep_policy = cpu_to_le32(te_v2_get_dep_policy(cmd_v2->policy)); + cmd_v1->is_present = cpu_to_le32(!te_v2_get_absence(cmd_v2->policy)); + cmd_v1->notify = cpu_to_le32(te_v2_get_notify(cmd_v2->policy)); +} + +static int iwl_mvm_send_time_event_cmd(struct iwl_mvm *mvm, + const struct iwl_time_event_cmd_v2 *cmd) +{ + struct iwl_time_event_cmd_v1 cmd_v1; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2) + return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, + sizeof(*cmd), cmd); + + iwl_mvm_te_v2_to_v1(cmd, &cmd_v1); + return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, + sizeof(cmd_v1), &cmd_v1); +} + + static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_time_event_data *te_data, - struct iwl_time_event_cmd *te_cmd) + struct iwl_time_event_cmd_v2 *te_cmd) { static const u8 time_event_response[] = { TIME_EVENT_CMD }; struct iwl_notification_wait wait_time_event; @@ -309,8 +364,7 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, ARRAY_SIZE(time_event_response), iwl_mvm_time_event_response, te_data); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, - sizeof(*te_cmd), te_cmd); + ret = iwl_mvm_send_time_event_cmd(mvm, te_cmd); if (ret) { IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); iwl_remove_notification(&mvm->notif_wait, &wait_time_event); @@ -337,13 +391,12 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - struct iwl_time_event_cmd time_cmd = {}; + struct iwl_time_event_cmd_v2 time_cmd = {}; lockdep_assert_held(&mvm->mutex); if (te_data->running && - time_after(te_data->end_jiffies, - jiffies + TU_TO_JIFFIES(min_duration))) { + time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", jiffies_to_msecs(te_data->end_jiffies - jiffies)); return; @@ -372,17 +425,14 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, time_cmd.apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG)); - time_cmd.dep_policy = TE_INDEPENDENT; - time_cmd.is_present = cpu_to_le32(1); - time_cmd.max_frags = cpu_to_le32(TE_FRAG_NONE); + time_cmd.max_frags = TE_V2_FRAG_NONE; time_cmd.max_delay = cpu_to_le32(500); /* TODO: why do we need to interval = bi if it is not periodic? */ time_cmd.interval = cpu_to_le32(1); - time_cmd.interval_reciprocal = cpu_to_le32(iwl_mvm_reciprocal(1)); time_cmd.duration = cpu_to_le32(duration); - time_cmd.repeat = cpu_to_le32(1); - time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START | - TE_NOTIF_HOST_EVENT_END); + time_cmd.repeat = 1; + time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | + TE_V2_NOTIF_HOST_EVENT_END); iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); } @@ -396,7 +446,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif, struct iwl_mvm_time_event_data *te_data) { - struct iwl_time_event_cmd time_cmd = {}; + struct iwl_time_event_cmd_v2 time_cmd = {}; u32 id, uid; int ret; @@ -433,8 +483,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, - sizeof(time_cmd), &time_cmd); + ret = iwl_mvm_send_time_event_cmd(mvm, &time_cmd); if (WARN_ON(ret)) return; } @@ -454,7 +503,7 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - struct iwl_time_event_cmd time_cmd = {}; + struct iwl_time_event_cmd_v2 time_cmd = {}; lockdep_assert_held(&mvm->mutex); if (te_data->running) { @@ -485,8 +534,6 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, } time_cmd.apply_time = cpu_to_le32(0); - time_cmd.dep_policy = cpu_to_le32(TE_INDEPENDENT); - time_cmd.is_present = cpu_to_le32(1); time_cmd.interval = cpu_to_le32(1); /* @@ -495,12 +542,12 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * scheduled. To improve the chances of it being scheduled, allow them * to be fragmented, and in addition allow them to be delayed. */ - time_cmd.max_frags = cpu_to_le32(MSEC_TO_TU(duration)/20); + time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS); time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2)); time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); - time_cmd.repeat = cpu_to_le32(1); - time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START | - TE_NOTIF_HOST_EVENT_END); + time_cmd.repeat = 1; + time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | + TE_V2_NOTIF_HOST_EVENT_END); return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); } diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c index d6ae7f1..1f3282d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -391,8 +391,7 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) mvmsta = (void *)sta->drv_priv; if (enable == mvmsta->tt_tx_protection) continue; - err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq, - mvmsta, enable); + err = iwl_mvm_tx_protection(mvm, mvmsta, enable); if (err) { IWL_ERR(mvm, "Failed to %s Tx protection\n", enable ? "enable" : "disable"); @@ -513,12 +512,39 @@ static const struct iwl_tt_params iwl7000_tt_params = { .support_tx_backoff = true, }; +static const struct iwl_tt_params iwl7000_high_temp_tt_params = { + .ct_kill_entry = 118, + .ct_kill_exit = 96, + .ct_kill_duration = 5, + .dynamic_smps_entry = 114, + .dynamic_smps_exit = 110, + .tx_protection_entry = 114, + .tx_protection_exit = 108, + .tx_backoff = { + {.temperature = 112, .backoff = 300}, + {.temperature = 113, .backoff = 800}, + {.temperature = 114, .backoff = 1500}, + {.temperature = 115, .backoff = 3000}, + {.temperature = 116, .backoff = 5000}, + {.temperature = 117, .backoff = 10000}, + }, + .support_ct_kill = true, + .support_dynamic_smps = true, + .support_tx_protection = true, + .support_tx_backoff = true, +}; + void iwl_mvm_tt_initialize(struct iwl_mvm *mvm) { struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n"); - tt->params = &iwl7000_tt_params; + + if (mvm->cfg->high_temp) + tt->params = &iwl7000_high_temp_tt_params; + else + tt->params = &iwl7000_tt_params; + tt->throttle = false; INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); } diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index f0e96a9..e05440d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -91,11 +91,10 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */ - if (info->band == IEEE80211_BAND_2GHZ && - (skb->protocol == cpu_to_be16(ETH_P_PAE) || - is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_back_req(fc) || - ieee80211_is_mgmt(fc))) + if (info->band == IEEE80211_BAND_2GHZ && + (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO || + is_multicast_ether_addr(hdr->addr1) || + ieee80211_is_back_req(fc) || ieee80211_is_mgmt(fc))) tx_flags |= TX_CMD_FLG_BT_DIS; if (ieee80211_has_morefrags(fc)) @@ -123,6 +122,8 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, * it */ WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU); + } else if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + tx_cmd->pm_frame_timeout = cpu_to_le16(2); } else { tx_cmd->pm_frame_timeout = 0; } @@ -171,7 +172,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, } /* - * for data packets, rate info comes from the table inside he fw. This + * for data packets, rate info comes from the table inside the fw. This * table is controlled by LINK_QUALITY commands */ diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 1e13328..a9c3574 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -453,6 +453,29 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler); } +void iwl_mvm_dump_sram(struct iwl_mvm *mvm) +{ + const struct fw_img *img; + int ofs, len = 0; + u8 *buf; + + if (!mvm->ucode_loaded) + return; + + img = &mvm->fw->img[mvm->cur_ucode]; + ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return; + + iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len); + iwl_print_hex_error(mvm->trans, buf, len); + + kfree(buf); +} + /** * iwl_mvm_send_lq_cmd() - Send link quality command * @init: This command is sent as part of station initialization right diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index ff13458..dc02cb9 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -273,9 +273,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)}, {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)}, @@ -325,15 +325,15 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) int ret; iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg); - if (iwl_trans == NULL) - return -ENOMEM; + if (IS_ERR(iwl_trans)) + return PTR_ERR(iwl_trans); pci_set_drvdata(pdev, iwl_trans); trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans); trans_pcie->drv = iwl_drv_start(iwl_trans, cfg); - if (IS_ERR_OR_NULL(trans_pcie->drv)) { + if (IS_ERR(trans_pcie->drv)) { ret = PTR_ERR(trans_pcie->drv); goto out_free_trans; } @@ -368,21 +368,19 @@ static void iwl_pci_remove(struct pci_dev *pdev) static int iwl_pci_suspend(struct device *device) { - struct pci_dev *pdev = to_pci_dev(device); - struct iwl_trans *iwl_trans = pci_get_drvdata(pdev); - /* Before you put code here, think about WoWLAN. You cannot check here * whether WoWLAN is enabled or not, and your code will run even if * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx. */ - return iwl_trans_suspend(iwl_trans); + return 0; } static int iwl_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); - struct iwl_trans *iwl_trans = pci_get_drvdata(pdev); + struct iwl_trans *trans = pci_get_drvdata(pdev); + bool hw_rfkill; /* Before you put code here, think about WoWLAN. You cannot check here * whether WoWLAN is enabled or not, and your code will run even if @@ -395,7 +393,15 @@ static int iwl_pci_resume(struct device *device) */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); - return iwl_trans_resume(iwl_trans); + if (!trans->op_mode) + return 0; + + iwl_enable_rfkill_int(trans); + + hw_rfkill = iwl_is_rfkill_set(trans); + iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + + return 0; } static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index b654dcd..fa22639 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -392,7 +392,6 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); /***************************************************** * Error handling ******************************************************/ -int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf); void iwl_pcie_dump_csr(struct iwl_trans *trans); /***************************************************** diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index fd848cd..3f237b4 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -112,15 +112,16 @@ */ static int iwl_rxq_space(const struct iwl_rxq *rxq) { - int s = rxq->read - rxq->write; - - if (s <= 0) - s += RX_QUEUE_SIZE; - /* keep some buffer to not confuse full and empty queue */ - s -= 2; - if (s < 0) - s = 0; - return s; + /* Make sure RX_QUEUE_SIZE is a power of 2 */ + BUILD_BUG_ON(RX_QUEUE_SIZE & (RX_QUEUE_SIZE - 1)); + + /* + * There can be up to (RX_QUEUE_SIZE - 1) free slots, to avoid ambiguity + * between empty and completely full queues. + * The following is equivalent to modulo by RX_QUEUE_SIZE and is well + * defined for negative dividends. + */ + return (rxq->read - rxq->write - 1) & (RX_QUEUE_SIZE - 1); } /* @@ -793,7 +794,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) } iwl_pcie_dump_csr(trans); - iwl_pcie_dump_fh(trans, NULL); + iwl_dump_fh(trans, NULL); set_bit(STATUS_FW_ERROR, &trans_pcie->status); clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); @@ -1120,6 +1121,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) struct iwl_trans *trans = data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 inta, inta_mask; + irqreturn_t ret = IRQ_NONE; lockdep_assert_held(&trans_pcie->irq_lock); @@ -1168,10 +1170,8 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) /* the thread will service interrupts and re-enable them */ if (likely(inta)) return IRQ_WAKE_THREAD; - else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) - iwl_enable_interrupts(trans); - return IRQ_HANDLED; + + ret = IRQ_HANDLED; none: /* re-enable interrupts here since we don't have anything to service. */ @@ -1180,7 +1180,7 @@ none: !trans_pcie->inta) iwl_enable_interrupts(trans); - return IRQ_NONE; + return ret; } /* interrupt handler using ict table, with this interrupt driver will @@ -1199,6 +1199,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) u32 val = 0; u32 read; unsigned long flags; + irqreturn_t ret = IRQ_NONE; if (!trans) return IRQ_NONE; @@ -1211,7 +1212,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) * use legacy interrupt. */ if (unlikely(!trans_pcie->use_ict)) { - irqreturn_t ret = iwl_pcie_isr(irq, data); + ret = iwl_pcie_isr(irq, data); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); return ret; } @@ -1280,17 +1281,9 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) if (likely(inta)) { spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); return IRQ_WAKE_THREAD; - } else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) { - /* Allow interrupt if was disabled by this handler and - * no tasklet was schedules, We should not enable interrupt, - * tasklet will enable it. - */ - iwl_enable_interrupts(trans); } - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return IRQ_HANDLED; + ret = IRQ_HANDLED; none: /* re-enable interrupts here since we don't have anything to service. @@ -1301,5 +1294,5 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) iwl_enable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return IRQ_NONE; + return ret; } diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 390e2f0..bad95d2 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -820,25 +820,6 @@ static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); } -#ifdef CONFIG_PM_SLEEP -static int iwl_trans_pcie_suspend(struct iwl_trans *trans) -{ - return 0; -} - -static int iwl_trans_pcie_resume(struct iwl_trans *trans) -{ - bool hw_rfkill; - - iwl_enable_rfkill_int(trans); - - hw_rfkill = iwl_is_rfkill_set(trans); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, unsigned long *flags) { @@ -1038,71 +1019,6 @@ static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); } -static const char *get_fh_string(int cmd) -{ -#define IWL_CMD(x) case x: return #x - switch (cmd) { - IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); - IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); - IWL_CMD(FH_RSCSR_CHNL0_WPTR); - IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); - IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); - IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); - IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); - IWL_CMD(FH_TSSR_TX_STATUS_REG); - IWL_CMD(FH_TSSR_TX_ERROR_REG); - default: - return "UNKNOWN"; - } -#undef IWL_CMD -} - -int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf) -{ - int i; - static const u32 fh_tbl[] = { - FH_RSCSR_CHNL0_STTS_WPTR_REG, - FH_RSCSR_CHNL0_RBDCB_BASE_REG, - FH_RSCSR_CHNL0_WPTR, - FH_MEM_RCSR_CHNL0_CONFIG_REG, - FH_MEM_RSSR_SHARED_CTRL_REG, - FH_MEM_RSSR_RX_STATUS_REG, - FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, - FH_TSSR_TX_STATUS_REG, - FH_TSSR_TX_ERROR_REG - }; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (buf) { - int pos = 0; - size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; - - *buf = kmalloc(bufsz, GFP_KERNEL); - if (!*buf) - return -ENOMEM; - - pos += scnprintf(*buf + pos, bufsz - pos, - "FH register values:\n"); - - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) - pos += scnprintf(*buf + pos, bufsz - pos, - " %34s: 0X%08x\n", - get_fh_string(fh_tbl[i]), - iwl_read_direct32(trans, fh_tbl[i])); - - return pos; - } -#endif - - IWL_ERR(trans, "FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) - IWL_ERR(trans, " %34s: 0X%08x\n", - get_fh_string(fh_tbl[i]), - iwl_read_direct32(trans, fh_tbl[i])); - - return 0; -} - static const char *get_csr_string(int cmd) { #define IWL_CMD(x) case x: return #x @@ -1183,18 +1099,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans) } while (0) /* file operation */ -#define DEBUGFS_READ_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ - char __user *user_buf, \ - size_t count, loff_t *ppos); - -#define DEBUGFS_WRITE_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ - const char __user *user_buf, \ - size_t count, loff_t *ppos); - #define DEBUGFS_READ_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ .open = simple_open, \ @@ -1202,7 +1107,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ }; #define DEBUGFS_WRITE_FILE_OPS(name) \ - DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .open = simple_open, \ @@ -1210,8 +1114,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ }; #define DEBUGFS_READ_WRITE_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ - DEBUGFS_WRITE_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .write = iwl_dbgfs_##name##_write, \ .read = iwl_dbgfs_##name##_read, \ @@ -1395,7 +1297,7 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, int pos = 0; ssize_t ret = -EFAULT; - ret = pos = iwl_pcie_dump_fh(trans, &buf); + ret = pos = iwl_dump_fh(trans, &buf); if (buf) { ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); @@ -1459,10 +1361,6 @@ static const struct iwl_trans_ops trans_ops_pcie = { .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, -#ifdef CONFIG_PM_SLEEP - .suspend = iwl_trans_pcie_suspend, - .resume = iwl_trans_pcie_resume, -#endif .write8 = iwl_trans_pcie_write8, .write32 = iwl_trans_pcie_write32, .read32 = iwl_trans_pcie_read32, @@ -1488,9 +1386,10 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, trans = kzalloc(sizeof(struct iwl_trans) + sizeof(struct iwl_trans_pcie), GFP_KERNEL); - - if (!trans) - return NULL; + if (!trans) { + err = -ENOMEM; + goto out; + } trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1502,15 +1401,20 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, spin_lock_init(&trans_pcie->reg_lock); init_waitqueue_head(&trans_pcie->ucode_write_waitq); - if (pci_enable_device(pdev)) { - err = -ENODEV; - goto out_no_pci; + if (!cfg->base_params->pcie_l1_allowed) { + /* + * W/A - seems to solve weird behavior. We need to remove this + * if we don't want to stay in L1 all the time. This wastes a + * lot of power. + */ + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | + PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); } - /* W/A - seems to solve weird behavior. We need to remove this if we - * don't want to stay in L1 all the time. This wastes a lot of power */ - pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM); + err = pci_enable_device(pdev); + if (err) + goto out_no_pci; pci_set_master(pdev); @@ -1579,17 +1483,20 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, SLAB_HWCACHE_ALIGN, NULL); - if (!trans->dev_cmd_pool) + if (!trans->dev_cmd_pool) { + err = -ENOMEM; goto out_pci_disable_msi; + } trans_pcie->inta_mask = CSR_INI_SET_MASK; if (iwl_pcie_alloc_ict(trans)) goto out_free_cmd_pool; - if (request_threaded_irq(pdev->irq, iwl_pcie_isr_ict, - iwl_pcie_irq_handler, - IRQF_SHARED, DRV_NAME, trans)) { + err = request_threaded_irq(pdev->irq, iwl_pcie_isr_ict, + iwl_pcie_irq_handler, + IRQF_SHARED, DRV_NAME, trans); + if (err) { IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq); goto out_free_ict; } @@ -1608,5 +1515,6 @@ out_pci_disable_device: pci_disable_device(pdev); out_no_pci: kfree(trans); - return NULL; +out: + return ERR_PTR(err); } diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index c47c921..f45eb29 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -65,18 +65,30 @@ ***************************************************/ static int iwl_queue_space(const struct iwl_queue *q) { - int s = q->read_ptr - q->write_ptr; - - if (q->read_ptr > q->write_ptr) - s -= q->n_bd; - - if (s <= 0) - s += q->n_window; - /* keep some reserve to not confuse empty and full situations */ - s -= 2; - if (s < 0) - s = 0; - return s; + unsigned int max; + unsigned int used; + + /* + * To avoid ambiguity between empty and completely full queues, there + * should always be less than q->n_bd elements in the queue. + * If q->n_window is smaller than q->n_bd, there is no need to reserve + * any queue entries for this purpose. + */ + if (q->n_window < q->n_bd) + max = q->n_window; + else + max = q->n_bd - 1; + + /* + * q->n_bd is a power of 2, so the following is equivalent to modulo by + * q->n_bd and is well defined for negative dividends. + */ + used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1); + + if (WARN_ON(used > max)) + return 0; + + return max - used; } /* @@ -451,13 +463,10 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, return -EINVAL; } - if (WARN_ON(addr & ~DMA_BIT_MASK(36))) + if (WARN(addr & ~IWL_TX_DMA_MASK, + "Unaligned address = %llx\n", (unsigned long long)addr)) return -EINVAL; - if (unlikely(addr & ~IWL_TX_DMA_MASK)) - IWL_ERR(trans, "Unaligned address = %llx\n", - (unsigned long long)addr); - iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len); return 0; @@ -829,7 +838,7 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) sizeof(struct iwl_txq), GFP_KERNEL); if (!trans_pcie->txq) { IWL_ERR(trans, "Not enough memory for txq\n"); - ret = ENOMEM; + ret = -ENOMEM; goto error; } @@ -1153,10 +1162,10 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) /* * iwl_pcie_enqueue_hcmd - enqueue a uCode command * @priv: device private data point - * @cmd: a point to the ucode command structure + * @cmd: a pointer to the ucode command structure * - * The function returns < 0 values to indicate the operation is - * failed. On success, it turns the index (> 0) of command in the + * The function returns < 0 values to indicate the operation + * failed. On success, it returns the index (>= 0) of command in the * command queue. */ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, @@ -1619,10 +1628,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, txq = &trans_pcie->txq[txq_id]; q = &txq->q; - if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) { - WARN_ON_ONCE(1); + if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used), + "TX on unused queue %d\n", txq_id)) return -EINVAL; - } spin_lock(&txq->lock); @@ -1632,7 +1640,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, * Check here that the packets are in the right place on the ring. */ wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); - WARN_ONCE(trans_pcie->txq[txq_id].ampdu && + WARN_ONCE(txq->ampdu && (wifi_seq & 0xff) != q->write_ptr, "Q: %d WiFi Seq %d tfdNum %d", txq_id, wifi_seq, q->write_ptr); @@ -1664,7 +1672,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, */ len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + hdr_len - IWL_HCMD_SCRATCHBUF_SIZE; - tb1_len = (len + 3) & ~3; + tb1_len = ALIGN(len, 4); /* Tell NIC about any 2-byte padding after MAC header */ if (tb1_len != len) diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index efae07e..6fef746 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -1017,7 +1017,7 @@ static int lbs_add_mesh(struct lbs_private *priv) mesh_dev->netdev_ops = &mesh_netdev_ops; mesh_dev->ethtool_ops = &lbs_ethtool_ops; - memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, ETH_ALEN); + eth_hw_addr_inherit(mesh_dev, priv->dev); SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index cb34c78..2cd3f54 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -867,7 +867,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, if (WARN_ON(skb->len < 10)) { /* Should not happen; just a sanity check for addr1 use */ - dev_kfree_skb(skb); + ieee80211_free_txskb(hw, skb); return; } @@ -884,13 +884,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, } if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) { - dev_kfree_skb(skb); + ieee80211_free_txskb(hw, skb); return; } if (data->idle && !data->tmp_chan) { wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n"); - dev_kfree_skb(skb); + ieee80211_free_txskb(hw, skb); return; } @@ -1364,6 +1364,7 @@ static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { static int hwsim_fops_ps_write(void *dat, u64 val); static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, void *data, int len) { struct mac80211_hwsim_data *hwsim = hw->priv; @@ -2309,7 +2310,9 @@ static int __init init_mac80211_hwsim(void) hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_AP_UAPSD; + hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; /* ask mac80211 to reserve space for magic */ hw->vif_data_size = sizeof(struct hwsim_vif_priv); @@ -2525,8 +2528,10 @@ static int __init init_mac80211_hwsim(void) } hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup); - if (hwsim_mon == NULL) + if (hwsim_mon == NULL) { + err = -ENOMEM; goto failed; + } rtnl_lock(); diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 41e9d25..0b803c0 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -292,6 +292,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, struct mwifiex_ie_types_extcap *ext_cap; int ret_len = 0; struct ieee80211_supported_band *sband; + struct ieee_types_header *hdr; u8 radio_type; if (!buffer || !*buffer) @@ -388,17 +389,24 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, } if (bss_desc->bcn_ext_cap) { + hdr = (void *)bss_desc->bcn_ext_cap; ext_cap = (struct mwifiex_ie_types_extcap *) *buffer; memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap)); ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); - ext_cap->header.len = cpu_to_le16(sizeof(ext_cap->ext_cap)); + ext_cap->header.len = cpu_to_le16(hdr->len); - memcpy((u8 *)ext_cap + sizeof(struct mwifiex_ie_types_header), + memcpy((u8 *)ext_cap->ext_capab, bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header), le16_to_cpu(ext_cap->header.len)); - *buffer += sizeof(struct mwifiex_ie_types_extcap); - ret_len += sizeof(struct mwifiex_ie_types_extcap); + if (hdr->len > 3 && + ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED) + priv->hs2_enabled = true; + else + priv->hs2_enabled = false; + + *buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; + ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; } return ret_len; diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index a78e065..21c6882 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -69,7 +69,8 @@ mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr, memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); /* Copy SNAP header */ - snap.snap_type = *(u16 *) ((u8 *)skb_src->data + dt_offset); + snap.snap_type = + le16_to_cpu(*(__le16 *) ((u8 *)skb_src->data + dt_offset)); dt_offset += sizeof(u16); memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); @@ -189,7 +190,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, skb_src = skb_dequeue(&pra_list->skb_head); - pra_list->total_pkts_size -= skb_src->len; + pra_list->total_pkt_count--; atomic_dec(&priv->wmm.tx_pkts_queued); @@ -268,7 +269,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, skb_queue_tail(&pra_list->skb_head, skb_aggr); - pra_list->total_pkts_size += skb_aggr->len; + pra_list->total_pkt_count++; atomic_inc(&priv->wmm.tx_pkts_queued); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 89459db..fbad00a 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -25,7 +25,9 @@ module_param(reg_alpha2, charp, 0); static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { { - .max = 2, .types = BIT(NL80211_IFTYPE_STATION), + .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT), }, { .max = 1, .types = BIT(NL80211_IFTYPE_AP), @@ -189,6 +191,7 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct sk_buff *skb; u16 pkt_len; const struct ieee80211_mgmt *mgmt; + struct mwifiex_txinfo *tx_info; struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); if (!buf || !len) { @@ -216,6 +219,10 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, return -ENOMEM; } + tx_info = MWIFIEX_SKB_TXCB(skb); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + mwifiex_form_mgmt_frame(skb, buf, len); mwifiex_queue_tx_pkt(priv, skb); @@ -235,16 +242,20 @@ mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy, u16 frame_type, bool reg) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + u32 mask; if (reg) - priv->mgmt_frame_mask |= BIT(frame_type >> 4); + mask = priv->mgmt_frame_mask | BIT(frame_type >> 4); else - priv->mgmt_frame_mask &= ~BIT(frame_type >> 4); - - mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG, - HostCmd_ACT_GEN_SET, 0, &priv->mgmt_frame_mask); + mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4); - wiphy_dbg(wiphy, "info: mgmt frame registered\n"); + if (mask != priv->mgmt_frame_mask) { + priv->mgmt_frame_mask = mask; + mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG, + HostCmd_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask); + wiphy_dbg(wiphy, "info: mgmt frame registered\n"); + } } /* @@ -1497,6 +1508,7 @@ mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, " reason code %d\n", priv->cfg_bssid, reason_code); memset(priv->cfg_bssid, 0, ETH_ALEN); + priv->hs2_enabled = false; return 0; } @@ -2296,10 +2308,9 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) } EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); -#ifdef CONFIG_PM static bool -mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat, - s8 *byte_seq) +mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, + u8 max_byte_seq) { int j, k, valid_byte_cnt = 0; bool dont_care_byte = false; @@ -2317,16 +2328,17 @@ mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat, dont_care_byte = true; } - if (valid_byte_cnt > MAX_BYTESEQ) + if (valid_byte_cnt > max_byte_seq) return false; } } - byte_seq[MAX_BYTESEQ] = valid_byte_cnt; + byte_seq[max_byte_seq] = valid_byte_cnt; return true; } +#ifdef CONFIG_PM static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wowlan) { @@ -2335,7 +2347,7 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, struct mwifiex_mef_entry *mef_entry; int i, filt_num = 0, ret; bool first_pat = true; - u8 byte_seq[MAX_BYTESEQ + 1]; + u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; const u8 ipv4_mc_mac[] = {0x33, 0x33}; const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; struct mwifiex_private *priv = @@ -2365,7 +2377,8 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, for (i = 0; i < wowlan->n_patterns; i++) { memset(byte_seq, 0, sizeof(byte_seq)); if (!mwifiex_is_pattern_supported(&wowlan->patterns[i], - byte_seq)) { + byte_seq, + MWIFIEX_MEF_MAX_BYTESEQ)) { wiphy_err(wiphy, "Pattern not supported\n"); kfree(mef_entry); return -EOPNOTSUPP; @@ -2373,16 +2386,16 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, if (!wowlan->patterns[i].pkt_offset) { if (!(byte_seq[0] & 0x01) && - (byte_seq[MAX_BYTESEQ] == 1)) { + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) { mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST; continue; } else if (is_broadcast_ether_addr(byte_seq)) { mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST; continue; } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && - (byte_seq[MAX_BYTESEQ] == 2)) || + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) || (!memcmp(byte_seq, ipv6_mc_mac, 3) && - (byte_seq[MAX_BYTESEQ] == 3))) { + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) { mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST; continue; } @@ -2408,7 +2421,8 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, mef_entry->filter[filt_num].repeat = 16; memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, ETH_ALEN); - mef_entry->filter[filt_num].byte_seq[MAX_BYTESEQ] = ETH_ALEN; + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + ETH_ALEN; mef_entry->filter[filt_num].offset = 14; mef_entry->filter[filt_num].filt_type = TYPE_EQ; if (filt_num) @@ -2442,6 +2456,119 @@ static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy, } #endif +static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq) +{ + const u8 ipv4_mc_mac[] = {0x33, 0x33}; + const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff}; + + if ((byte_seq[0] & 0x01) && + (byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1)) + return PACKET_TYPE_UNICAST; + else if (!memcmp(byte_seq, bc_mac, 4)) + return PACKET_TYPE_BROADCAST; + else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3)) + return PACKET_TYPE_MULTICAST; + + return 0; +} + +static int +mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv, + struct cfg80211_coalesce_rules *crule, + struct mwifiex_coalesce_rule *mrule) +{ + u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1]; + struct filt_field_param *param; + int i; + + mrule->max_coalescing_delay = crule->delay; + + param = mrule->params; + + for (i = 0; i < crule->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!mwifiex_is_pattern_supported(&crule->patterns[i], + byte_seq, + MWIFIEX_COALESCE_MAX_BYTESEQ)) { + dev_err(priv->adapter->dev, "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!crule->patterns[i].pkt_offset) { + u8 pkt_type; + + pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq); + if (pkt_type && mrule->pkt_type) { + dev_err(priv->adapter->dev, + "Multiple packet types not allowed\n"); + return -EOPNOTSUPP; + } else if (pkt_type) { + mrule->pkt_type = pkt_type; + continue; + } + } + + if (crule->condition == NL80211_COALESCE_CONDITION_MATCH) + param->operation = RECV_FILTER_MATCH_TYPE_EQ; + else + param->operation = RECV_FILTER_MATCH_TYPE_NE; + + param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ]; + memcpy(param->operand_byte_stream, byte_seq, + param->operand_len); + param->offset = crule->patterns[i].pkt_offset; + param++; + + mrule->num_of_fields++; + } + + if (!mrule->pkt_type) { + dev_err(priv->adapter->dev, + "Packet type can not be determined\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + int i, ret; + struct mwifiex_ds_coalesce_cfg coalesce_cfg; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); + if (!coalesce) { + dev_dbg(adapter->dev, + "Disable coalesce and reset all previous rules\n"); + return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, + &coalesce_cfg); + } + + coalesce_cfg.num_of_rules = coalesce->n_rules; + for (i = 0; i < coalesce->n_rules; i++) { + ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i], + &coalesce_cfg.rule[i]); + if (ret) { + dev_err(priv->adapter->dev, + "Recheck the patterns provided for rule %d\n", + i + 1); + return ret; + } + } + + return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, &coalesce_cfg); +} + /* station cfg80211 operations */ static struct cfg80211_ops mwifiex_cfg80211_ops = { .add_virtual_intf = mwifiex_add_virtual_intf, @@ -2476,12 +2603,13 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .resume = mwifiex_cfg80211_resume, .set_wakeup = mwifiex_cfg80211_set_wakeup, #endif + .set_coalesce = mwifiex_cfg80211_set_coalesce, }; #ifdef CONFIG_PM static const struct wiphy_wowlan_support mwifiex_wowlan_support = { .flags = WIPHY_WOWLAN_MAGIC_PKT, - .n_patterns = MWIFIEX_MAX_FILTERS, + .n_patterns = MWIFIEX_MEF_MAX_FILTERS, .pattern_min_len = 1, .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, @@ -2499,6 +2627,15 @@ static bool mwifiex_is_valid_alpha2(const char *alpha2) return false; } +static const struct wiphy_coalesce_support mwifiex_coalesce_support = { + .n_rules = MWIFIEX_COALESCE_MAX_RULES, + .max_delay = MWIFIEX_MAX_COALESCING_DELAY, + .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, + .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, +}; + /* * This function registers the device with CFG802.11 subsystem. * @@ -2560,6 +2697,8 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->wowlan = &mwifiex_wowlan_support; #endif + wiphy->coalesce = &mwifiex_coalesce_support; + wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c index 5178c46..9eefacb 100644 --- a/drivers/net/wireless/mwifiex/cfp.c +++ b/drivers/net/wireless/mwifiex/cfp.c @@ -404,11 +404,43 @@ mwifiex_is_rate_auto(struct mwifiex_private *priv) return false; } -/* - * This function gets the supported data rates. - * - * The function works in both Ad-Hoc and infra mode by printing the - * band and returning the data rates. +/* This function gets the supported data rates from bitmask inside + * cfg80211_scan_request. + */ +u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, + u8 *rates, u8 radio_type) +{ + struct wiphy *wiphy = priv->adapter->wiphy; + struct cfg80211_scan_request *request = priv->scan_request; + u32 num_rates, rate_mask; + struct ieee80211_supported_band *sband; + int i; + + if (radio_type) { + sband = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask = request->rates[IEEE80211_BAND_5GHZ]; + } else { + sband = wiphy->bands[IEEE80211_BAND_2GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask = request->rates[IEEE80211_BAND_2GHZ]; + } + + num_rates = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if ((BIT(i) & rate_mask) == 0) + continue; /* skip rate */ + rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5); + } + + return num_rates; +} + +/* This function gets the supported data rates. The function works in + * both Ad-Hoc and infra mode by printing the band and returning the + * data rates. */ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) { diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 94cc09d4..5c85d78 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -26,6 +26,7 @@ #include <linux/wait.h> #include <linux/timer.h> #include <linux/ieee80211.h> +#include <uapi/linux/if_arp.h> #include <net/mac80211.h> @@ -75,7 +76,8 @@ #define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) #define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1) -#define MWIFIEX_BRIDGED_PKTS_THRESHOLD 1024 +#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 +#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 enum mwifiex_bss_type { MWIFIEX_BSS_TYPE_STA = 0, @@ -151,4 +153,12 @@ struct mwifiex_types_wmm_info { u8 reserved; struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; } __packed; + +struct mwifiex_arp_eth_header { + struct arphdr hdr; + u8 ar_sha[ETH_ALEN]; + u8 ar_sip[4]; + u8 ar_tha[ETH_ALEN]; + u8 ar_tip[4]; +} __packed; #endif /* !_MWIFIEX_DECL_H_ */ diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 1b45aa5..f80f30b 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -85,9 +85,6 @@ enum KEY_TYPE_ID { #define WAPI_KEY_LEN 50 #define MAX_POLL_TRIES 100 - -#define MAX_MULTI_INTERFACE_POLL_TRIES 1000 - #define MAX_FIRMWARE_POLL_TRIES 100 #define FIRMWARE_READY_SDIO 0xfedc @@ -156,6 +153,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123) #define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) #define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) +#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 @@ -297,6 +295,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_CAU_REG_ACCESS 0x00ed #define HostCmd_CMD_SET_BSS_MODE 0x00f7 #define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa +#define HostCmd_CMD_COALESCE_CFG 0x010a #define HostCmd_CMD_MGMT_FRAME_REG 0x010c #define HostCmd_CMD_REMAIN_ON_CHAN 0x010d #define HostCmd_CMD_11AC_CFG 0x0112 @@ -453,7 +452,7 @@ enum P2P_MODES { (((event_cause) >> 24) & 0x00ff) #define MWIFIEX_MAX_PATTERN_LEN 20 -#define MWIFIEX_MAX_OFFSET_LEN 50 +#define MWIFIEX_MAX_OFFSET_LEN 100 #define STACK_NBYTES 100 #define TYPE_DNUM 1 #define TYPE_BYTESEQ 2 @@ -1331,7 +1330,7 @@ struct mwifiex_ie_types_2040bssco { struct mwifiex_ie_types_extcap { struct mwifiex_ie_types_header header; - u8 ext_cap; + u8 ext_capab[0]; } __packed; struct host_cmd_ds_mac_reg_access { @@ -1369,11 +1368,6 @@ struct host_cmd_ds_802_11_eeprom_access { u8 value; } __packed; -struct host_cmd_tlv { - __le16 type; - __le16 len; -} __packed; - struct mwifiex_assoc_event { u8 sta_addr[ETH_ALEN]; __le16 type; @@ -1399,99 +1393,99 @@ struct host_cmd_11ac_vht_cfg { } __packed; struct host_cmd_tlv_akmp { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; __le16 key_mgmt; __le16 key_mgmt_operation; } __packed; struct host_cmd_tlv_pwk_cipher { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; __le16 proto; u8 cipher; u8 reserved; } __packed; struct host_cmd_tlv_gwk_cipher { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 cipher; u8 reserved; } __packed; struct host_cmd_tlv_passphrase { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 passphrase[0]; } __packed; struct host_cmd_tlv_wep_key { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 key_index; u8 is_default; u8 key[1]; }; struct host_cmd_tlv_auth_type { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 auth_type; } __packed; struct host_cmd_tlv_encrypt_protocol { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; __le16 proto; } __packed; struct host_cmd_tlv_ssid { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 ssid[0]; } __packed; struct host_cmd_tlv_rates { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 rates[0]; } __packed; struct host_cmd_tlv_bcast_ssid { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 bcast_ctl; } __packed; struct host_cmd_tlv_beacon_period { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; __le16 period; } __packed; struct host_cmd_tlv_dtim_period { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 period; } __packed; struct host_cmd_tlv_frag_threshold { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; __le16 frag_thr; } __packed; struct host_cmd_tlv_rts_threshold { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; __le16 rts_thr; } __packed; struct host_cmd_tlv_retry_limit { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 limit; } __packed; struct host_cmd_tlv_mac_addr { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 mac_addr[ETH_ALEN]; } __packed; struct host_cmd_tlv_channel_band { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; u8 band_config; u8 channel; } __packed; struct host_cmd_tlv_ageout_timer { - struct host_cmd_tlv tlv; + struct mwifiex_ie_types_header header; __le32 sta_ao_timer; } __packed; @@ -1604,6 +1598,27 @@ struct host_cmd_ds_802_11_cfg_data { __le16 data_len; } __packed; +struct coalesce_filt_field_param { + u8 operation; + u8 operand_len; + __le16 offset; + u8 operand_byte_stream[4]; +}; + +struct coalesce_receive_filt_rule { + struct mwifiex_ie_types_header header; + u8 num_of_fields; + u8 pkt_type; + __le16 max_coalescing_delay; + struct coalesce_filt_field_param params[0]; +} __packed; + +struct host_cmd_ds_coalesce_cfg { + __le16 action; + __le16 num_of_rules; + struct coalesce_receive_filt_rule rule[0]; +} __packed; + struct host_cmd_ds_command { __le16 command; __le16 size; @@ -1664,6 +1679,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_sta_deauth sta_deauth; struct host_cmd_11ac_vht_cfg vht_cfg; struct host_cmd_ds_802_11_cfg_data cfg_data; + struct host_cmd_ds_coalesce_cfg coalesce_cfg; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c index e38342f..220af4f 100644 --- a/drivers/net/wireless/mwifiex/ie.c +++ b/drivers/net/wireless/mwifiex/ie.c @@ -87,7 +87,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv, u8 *tmp; input_len = le16_to_cpu(ie_list->len); - travel_len = sizeof(struct host_cmd_tlv); + travel_len = sizeof(struct mwifiex_ie_types_header); ie_list->len = 0; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 2cf8b96..6499117 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -135,6 +135,8 @@ int mwifiex_init_priv(struct mwifiex_private *priv) priv->csa_chan = 0; priv->csa_expire_time = 0; + priv->del_list_idx = 0; + priv->hs2_enabled = false; return mwifiex_add_bss_prio_tbl(priv); } @@ -377,18 +379,11 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) static void mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) { - int i; - if (!adapter) { pr_err("%s: adapter is NULL\n", __func__); return; } - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) - del_timer_sync(&adapter->priv[i]->scan_delay_timer); - } - mwifiex_cancel_all_pending_cmd(adapter); /* Free lock variables */ @@ -398,13 +393,8 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "info: free cmd buffer\n"); mwifiex_free_cmd_buffer(adapter); - del_timer(&adapter->cmd_timer); - dev_dbg(adapter->dev, "info: free scan table\n"); - if (adapter->if_ops.cleanup_if) - adapter->if_ops.cleanup_if(adapter); - if (adapter->sleep_cfm) dev_kfree_skb_any(adapter->sleep_cfm); } @@ -702,7 +692,6 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, if (!adapter->winner) { dev_notice(adapter->dev, "FW already running! Skip FW dnld\n"); - poll_num = MAX_MULTI_INTERFACE_POLL_TRIES; goto poll_fw; } } diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 7f27e45..00a95f4 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -362,13 +362,13 @@ struct mwifiex_ds_misc_subsc_evt { struct subsc_evt_cfg bcn_h_rssi_cfg; }; -#define MAX_BYTESEQ 6 /* non-adjustable */ -#define MWIFIEX_MAX_FILTERS 10 +#define MWIFIEX_MEF_MAX_BYTESEQ 6 /* non-adjustable */ +#define MWIFIEX_MEF_MAX_FILTERS 10 struct mwifiex_mef_filter { u16 repeat; u16 offset; - s8 byte_seq[MAX_BYTESEQ + 1]; + s8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; u8 filt_type; u8 filt_action; }; @@ -376,7 +376,7 @@ struct mwifiex_mef_filter { struct mwifiex_mef_entry { u8 mode; u8 action; - struct mwifiex_mef_filter filter[MWIFIEX_MAX_FILTERS]; + struct mwifiex_mef_filter filter[MWIFIEX_MEF_MAX_FILTERS]; }; struct mwifiex_ds_mef_cfg { @@ -397,4 +397,39 @@ enum { MWIFIEX_FUNC_SHUTDOWN, }; +enum COALESCE_OPERATION { + RECV_FILTER_MATCH_TYPE_EQ = 0x80, + RECV_FILTER_MATCH_TYPE_NE, +}; + +enum COALESCE_PACKET_TYPE { + PACKET_TYPE_UNICAST = 1, + PACKET_TYPE_MULTICAST = 2, + PACKET_TYPE_BROADCAST = 3 +}; + +#define MWIFIEX_COALESCE_MAX_RULES 8 +#define MWIFIEX_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ +#define MWIFIEX_COALESCE_MAX_FILTERS 4 +#define MWIFIEX_MAX_COALESCING_DELAY 100 /* in msecs */ + +struct filt_field_param { + u8 operation; + u8 operand_len; + u16 offset; + u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ]; +}; + +struct mwifiex_coalesce_rule { + u16 max_coalescing_delay; + u8 num_of_fields; + u8 pkt_type; + struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS]; +}; + +struct mwifiex_ds_coalesce_cfg { + u16 num_of_rules; + struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES]; +}; + #endif /* !_MWIFIEX_IOCTL_H_ */ diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 12e7781..9d7c0e6 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -1427,6 +1427,7 @@ int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac) switch (priv->bss_mode) { case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: return mwifiex_deauthenticate_infra(priv, mac); case NL80211_IFTYPE_ADHOC: return mwifiex_send_cmd_sync(priv, diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 1753431..fd77833 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -191,12 +191,16 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter) { s32 i; + if (adapter->if_ops.cleanup_if) + adapter->if_ops.cleanup_if(adapter); + del_timer(&adapter->cmd_timer); /* Free private structures */ for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { mwifiex_free_curr_bcn(adapter->priv[i]); + del_timer_sync(&adapter->priv[i]->scan_delay_timer); kfree(adapter->priv[i]); } } @@ -386,6 +390,17 @@ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) } /* + * This function cancels all works in the queue and destroys + * the main workqueue. + */ +static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) +{ + flush_workqueue(adapter->workqueue); + destroy_workqueue(adapter->workqueue); + adapter->workqueue = NULL; +} + +/* * This function gets firmware and initializes it. * * The main initialization steps followed are - @@ -394,16 +409,18 @@ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) */ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) { - int ret; + int ret, i; char fmt[64]; struct mwifiex_private *priv; struct mwifiex_adapter *adapter = context; struct mwifiex_fw_image fw; + struct semaphore *sem = adapter->card_sem; + bool init_failed = false; if (!firmware) { dev_err(adapter->dev, "Failed to get firmware %s\n", adapter->fw_name); - goto done; + goto err_dnld_fw; } memset(&fw, 0, sizeof(struct mwifiex_fw_image)); @@ -416,7 +433,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) else ret = mwifiex_dnld_fw(adapter, &fw); if (ret == -1) - goto done; + goto err_dnld_fw; dev_notice(adapter->dev, "WLAN FW is active\n"); @@ -428,13 +445,15 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) } /* enable host interrupt after fw dnld is successful */ - if (adapter->if_ops.enable_int) - adapter->if_ops.enable_int(adapter); + if (adapter->if_ops.enable_int) { + if (adapter->if_ops.enable_int(adapter)) + goto err_dnld_fw; + } adapter->init_wait_q_woken = false; ret = mwifiex_init_fw(adapter); if (ret == -1) { - goto done; + goto err_init_fw; } else if (!ret) { adapter->hw_status = MWIFIEX_HW_STATUS_READY; goto done; @@ -443,12 +462,12 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) wait_event_interruptible(adapter->init_wait_q, adapter->init_wait_q_woken); if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) - goto done; + goto err_init_fw; priv = adapter->priv[MWIFIEX_BSS_ROLE_STA]; if (mwifiex_register_cfg80211(adapter)) { dev_err(adapter->dev, "cannot register with cfg80211\n"); - goto err_init_fw; + goto err_register_cfg80211; } rtnl_lock(); @@ -458,20 +477,6 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) dev_err(adapter->dev, "cannot create default STA interface\n"); goto err_add_intf; } - - /* Create AP interface by default */ - if (!mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", - NL80211_IFTYPE_AP, NULL, NULL)) { - dev_err(adapter->dev, "cannot create default AP interface\n"); - goto err_add_intf; - } - - /* Create P2P interface by default */ - if (!mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", - NL80211_IFTYPE_P2P_CLIENT, NULL, NULL)) { - dev_err(adapter->dev, "cannot create default P2P interface\n"); - goto err_add_intf; - } rtnl_unlock(); mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); @@ -479,20 +484,52 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) goto done; err_add_intf: - mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + + if (!priv) + continue; + + if (priv->wdev && priv->netdev) + mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev); + } rtnl_unlock(); +err_register_cfg80211: + wiphy_unregister(adapter->wiphy); + wiphy_free(adapter->wiphy); err_init_fw: if (adapter->if_ops.disable_int) adapter->if_ops.disable_int(adapter); +err_dnld_fw: pr_debug("info: %s: unregister device\n", __func__); - adapter->if_ops.unregister_dev(adapter); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + + if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) || + (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) { + pr_debug("info: %s: shutdown mwifiex\n", __func__); + adapter->init_wait_q_woken = false; + + if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + } + adapter->surprise_removed = true; + mwifiex_terminate_workqueue(adapter); + init_failed = true; done: if (adapter->cal_data) { release_firmware(adapter->cal_data); adapter->cal_data = NULL; } - release_firmware(adapter->firmware); + if (adapter->firmware) { + release_firmware(adapter->firmware); + adapter->firmware = NULL; + } complete(&adapter->fw_load); + if (init_failed) + mwifiex_free_adapter(adapter); + up(sem); return; } @@ -803,18 +840,6 @@ static void mwifiex_main_work_queue(struct work_struct *work) } /* - * This function cancels all works in the queue and destroys - * the main workqueue. - */ -static void -mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) -{ - flush_workqueue(adapter->workqueue); - destroy_workqueue(adapter->workqueue); - adapter->workqueue = NULL; -} - -/* * This function adds the card. * * This function follows the following major steps to set up the device - @@ -842,6 +867,7 @@ mwifiex_add_card(void *card, struct semaphore *sem, } adapter->iface_type = iface_type; + adapter->card_sem = sem; adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; adapter->surprise_removed = false; @@ -872,17 +898,12 @@ mwifiex_add_card(void *card, struct semaphore *sem, goto err_init_fw; } - up(sem); return 0; err_init_fw: pr_debug("info: %s: unregister device\n", __func__); if (adapter->if_ops.unregister_dev) adapter->if_ops.unregister_dev(adapter); -err_registerdev: - adapter->surprise_removed = true; - mwifiex_terminate_workqueue(adapter); -err_kmalloc: if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) || (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) { pr_debug("info: %s: shutdown mwifiex\n", __func__); @@ -892,7 +913,10 @@ err_kmalloc: wait_event_interruptible(adapter->init_wait_q, adapter->init_wait_q_woken); } - +err_registerdev: + adapter->surprise_removed = true; + mwifiex_terminate_workqueue(adapter); +err_kmalloc: mwifiex_free_adapter(adapter); err_init_sw: diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 253e0bd..1d72f13 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -204,11 +204,11 @@ struct mwifiex_ra_list_tbl { struct list_head list; struct sk_buff_head skb_head; u8 ra[ETH_ALEN]; - u32 total_pkts_size; u32 is_11n_enabled; u16 max_amsdu; - u16 pkt_count; + u16 ba_pkt_count; u8 ba_packet_thr; + u16 total_pkt_count; }; struct mwifiex_tid_tbl { @@ -515,6 +515,8 @@ struct mwifiex_private { bool scan_aborting; u8 csa_chan; unsigned long csa_expire_time; + u8 del_list_idx; + bool hs2_enabled; }; enum mwifiex_ba_status { @@ -748,6 +750,7 @@ struct mwifiex_adapter { atomic_t is_tx_received; atomic_t pending_bridged_pkts; + struct semaphore *card_sem; }; int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); @@ -900,6 +903,8 @@ int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask, u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates); u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates); +u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, + u8 *rates, u8 radio_type); u8 mwifiex_is_rate_auto(struct mwifiex_private *priv); extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE]; void mwifiex_save_curr_bcn(struct mwifiex_private *priv); @@ -1021,7 +1026,7 @@ mwifiex_netdev_get_priv(struct net_device *dev) */ static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb) { - return (*(u32 *)skb->data == PKT_TYPE_MGMT); + return (le32_to_cpu(*(__le32 *)skb->data) == PKT_TYPE_MGMT); } /* This function retrieves channel closed for operation by Channel diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 20c9c4c..52da8ee 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -76,7 +76,7 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) return false; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /* * Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume @@ -85,11 +85,12 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) * If already not suspended, this function allocates and sends a host * sleep activate request to the firmware and turns off the traffic. */ -static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state) +static int mwifiex_pcie_suspend(struct device *dev) { struct mwifiex_adapter *adapter; struct pcie_service_card *card; int hs_actived; + struct pci_dev *pdev = to_pci_dev(dev); if (pdev) { card = (struct pcie_service_card *) pci_get_drvdata(pdev); @@ -120,10 +121,11 @@ static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state) * If already not resumed, this function turns on the traffic and * sends a host sleep cancel request to the firmware. */ -static int mwifiex_pcie_resume(struct pci_dev *pdev) +static int mwifiex_pcie_resume(struct device *dev) { struct mwifiex_adapter *adapter; struct pcie_service_card *card; + struct pci_dev *pdev = to_pci_dev(dev); if (pdev) { card = (struct pcie_service_card *) pci_get_drvdata(pdev); @@ -211,9 +213,9 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) wait_for_completion(&adapter->fw_load); if (user_rmmod) { -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP if (adapter->is_suspended) - mwifiex_pcie_resume(pdev); + mwifiex_pcie_resume(&pdev->dev); #endif for (i = 0; i < adapter->priv_num; i++) @@ -233,6 +235,14 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) kfree(card); } +static void mwifiex_pcie_shutdown(struct pci_dev *pdev) +{ + user_rmmod = 1; + mwifiex_pcie_remove(pdev); + + return; +} + static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = { { PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P, @@ -249,17 +259,24 @@ static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = { MODULE_DEVICE_TABLE(pci, mwifiex_ids); +#ifdef CONFIG_PM_SLEEP +/* Power Management Hooks */ +static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend, + mwifiex_pcie_resume); +#endif + /* PCI Device Driver */ static struct pci_driver __refdata mwifiex_pcie = { .name = "mwifiex_pcie", .id_table = mwifiex_ids, .probe = mwifiex_pcie_probe, .remove = mwifiex_pcie_remove, -#ifdef CONFIG_PM - /* Power Management Hooks */ - .suspend = mwifiex_pcie_suspend, - .resume = mwifiex_pcie_resume, +#ifdef CONFIG_PM_SLEEP + .driver = { + .pm = &mwifiex_pcie_pm_ops, + }, #endif + .shutdown = mwifiex_pcie_shutdown, }; /* @@ -1925,7 +1942,7 @@ mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) ret = 0; break; } else { - mdelay(100); + msleep(100); ret = -1; } } @@ -1937,12 +1954,10 @@ mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) else if (!winner_status) { dev_err(adapter->dev, "PCI-E is the winner\n"); adapter->winner = 1; - ret = -1; } else { dev_err(adapter->dev, "PCI-E is not the winner <%#x,%d>, exit dnld\n", ret, adapter->winner); - ret = 0; } } diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index c447d9b..8cf7d50 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -543,6 +543,37 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv, return chan_idx; } +/* This function appends rate TLV to scan config command. */ +static int +mwifiex_append_rate_tlv(struct mwifiex_private *priv, + struct mwifiex_scan_cmd_config *scan_cfg_out, + u8 radio) +{ + struct mwifiex_ie_types_rates_param_set *rates_tlv; + u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos; + u32 rates_size; + + memset(rates, 0, sizeof(rates)); + + tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len; + + if (priv->scan_request) + rates_size = mwifiex_get_rates_from_cfg80211(priv, rates, + radio); + else + rates_size = mwifiex_get_supported_rates(priv, rates); + + dev_dbg(priv->adapter->dev, "info: SCAN_CMD: Rates size = %d\n", + rates_size); + rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos; + rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); + rates_tlv->header.len = cpu_to_le16((u16) rates_size); + memcpy(rates_tlv->rates, rates, rates_size); + scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size; + + return rates_size; +} + /* * This function constructs and sends multiple scan config commands to * the firmware. @@ -564,9 +595,10 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, struct mwifiex_chan_scan_param_set *tmp_chan_list; struct mwifiex_chan_scan_param_set *start_chan; - u32 tlv_idx; + u32 tlv_idx, rates_size; u32 total_scan_time; u32 done_early; + u8 radio_type; if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) { dev_dbg(priv->adapter->dev, @@ -591,6 +623,7 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, tlv_idx = 0; total_scan_time = 0; + radio_type = 0; chan_tlv_out->header.len = 0; start_chan = tmp_chan_list; done_early = false; @@ -612,6 +645,7 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, continue; } + radio_type = tmp_chan_list->radio_type; dev_dbg(priv->adapter->dev, "info: Scan: Chan(%3d), Radio(%d)," " Mode(%d, %d), Dur(%d)\n", @@ -692,6 +726,9 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, break; } + rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out, + radio_type); + priv->adapter->scan_channels = start_chan; /* Send the scan command to the firmware with the specified @@ -699,6 +736,14 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN, HostCmd_ACT_GEN_SET, 0, scan_cfg_out); + + /* rate IE is updated per scan command but same starting + * pointer is used each time so that rate IE from earlier + * scan_cfg_out->buf is overwritten with new one. + */ + scan_cfg_out->tlv_buf_len -= + sizeof(struct mwifiex_ie_types_header) + rates_size; + if (ret) break; } @@ -741,7 +786,6 @@ mwifiex_config_scan(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_ie_types_num_probes *num_probes_tlv; struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; - struct mwifiex_ie_types_rates_param_set *rates_tlv; u8 *tlv_pos; u32 num_probes; u32 ssid_len; @@ -753,8 +797,6 @@ mwifiex_config_scan(struct mwifiex_private *priv, u8 radio_type; int i; u8 ssid_filter; - u8 rates[MWIFIEX_SUPPORTED_RATES]; - u32 rates_size; struct mwifiex_ie_types_htcap *ht_cap; /* The tlv_buf_len is calculated for each scan command. The TLVs added @@ -889,19 +931,6 @@ mwifiex_config_scan(struct mwifiex_private *priv, } - /* Append rates tlv */ - memset(rates, 0, sizeof(rates)); - - rates_size = mwifiex_get_supported_rates(priv, rates); - - rates_tlv = (struct mwifiex_ie_types_rates_param_set *) tlv_pos; - rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); - rates_tlv->header.len = cpu_to_le16((u16) rates_size); - memcpy(rates_tlv->rates, rates, rates_size); - tlv_pos += sizeof(rates_tlv->header) + rates_size; - - dev_dbg(adapter->dev, "info: SCAN_CMD: Rates size = %d\n", rates_size); - if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && (priv->adapter->config_bands & BAND_GN || priv->adapter->config_bands & BAND_AN)) { diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 09185c9..1576104 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -50,9 +50,6 @@ static struct mwifiex_if_ops sdio_ops; static struct semaphore add_remove_card_sem; -static int mwifiex_sdio_resume(struct device *dev); -static void mwifiex_sdio_interrupt(struct sdio_func *func); - /* * SDIO probe. * @@ -113,6 +110,51 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) } /* + * SDIO resume. + * + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not resumed, this function turns on the traffic and + * sends a host sleep cancel request to the firmware. + */ +static int mwifiex_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + struct mwifiex_adapter *adapter; + mmc_pm_flag_t pm_flag = 0; + + if (func) { + pm_flag = sdio_get_host_pm_caps(func); + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + pr_err("resume: invalid card or adapter\n"); + return 0; + } + } else { + pr_err("resume: sdio_func is not specified\n"); + return 0; + } + + adapter = card->adapter; + + if (!adapter->is_suspended) { + dev_warn(adapter->dev, "device already resumed\n"); + return 0; + } + + adapter->is_suspended = false; + + /* Disable Host Sleep */ + mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), + MWIFIEX_ASYNC_CMD); + + return 0; +} + +/* * SDIO remove. * * This function removes the interface and frees up the card structure. @@ -212,51 +254,6 @@ static int mwifiex_sdio_suspend(struct device *dev) return ret; } -/* - * SDIO resume. - * - * Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not resumed, this function turns on the traffic and - * sends a host sleep cancel request to the firmware. - */ -static int mwifiex_sdio_resume(struct device *dev) -{ - struct sdio_func *func = dev_to_sdio_func(dev); - struct sdio_mmc_card *card; - struct mwifiex_adapter *adapter; - mmc_pm_flag_t pm_flag = 0; - - if (func) { - pm_flag = sdio_get_host_pm_caps(func); - card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_err("resume: invalid card or adapter\n"); - return 0; - } - } else { - pr_err("resume: sdio_func is not specified\n"); - return 0; - } - - adapter = card->adapter; - - if (!adapter->is_suspended) { - dev_warn(adapter->dev, "device already resumed\n"); - return 0; - } - - adapter->is_suspended = false; - - /* Disable Host Sleep */ - mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), - MWIFIEX_ASYNC_CMD); - - return 0; -} - /* Device ID for SD8786 */ #define SDIO_DEVICE_ID_MARVELL_8786 (0x9116) /* Device ID for SD8787 */ @@ -707,6 +704,65 @@ static void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) } /* + * This function reads the interrupt status from card. + */ +static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + u8 sdio_ireg; + unsigned long flags; + + if (mwifiex_read_data_sync(adapter, card->mp_regs, + card->reg->max_mp_regs, + REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { + dev_err(adapter->dev, "read mp_regs failed\n"); + return; + } + + sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG]; + if (sdio_ireg) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * For SDIO new mode CMD port interrupts + * DN_LD_CMD_PORT_HOST_INT_STATUS and/or + * UP_LD_CMD_PORT_HOST_INT_STATUS + * Clear the interrupt status register + */ + dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg); + spin_lock_irqsave(&adapter->int_lock, flags); + adapter->int_status |= sdio_ireg; + spin_unlock_irqrestore(&adapter->int_lock, flags); + } +} + +/* + * SDIO interrupt handler. + * + * This function reads the interrupt status from firmware and handles + * the interrupt in current thread (ksdioirqd) right away. + */ +static void +mwifiex_sdio_interrupt(struct sdio_func *func) +{ + struct mwifiex_adapter *adapter; + struct sdio_mmc_card *card; + + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + pr_debug("int: func=%p card=%p adapter=%p\n", + func, card, card ? card->adapter : NULL); + return; + } + adapter = card->adapter; + + if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) + adapter->ps_state = PS_STATE_AWAKE; + + mwifiex_interrupt_status(adapter); + mwifiex_main_process(adapter); +} + +/* * This function enables the host interrupt. * * The host interrupt enable mask is written to the card @@ -944,7 +1000,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, ret = 0; break; } else { - mdelay(100); + msleep(100); ret = -1; } } @@ -963,65 +1019,6 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, } /* - * This function reads the interrupt status from card. - */ -static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - u8 sdio_ireg; - unsigned long flags; - - if (mwifiex_read_data_sync(adapter, card->mp_regs, - card->reg->max_mp_regs, - REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { - dev_err(adapter->dev, "read mp_regs failed\n"); - return; - } - - sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG]; - if (sdio_ireg) { - /* - * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS - * For SDIO new mode CMD port interrupts - * DN_LD_CMD_PORT_HOST_INT_STATUS and/or - * UP_LD_CMD_PORT_HOST_INT_STATUS - * Clear the interrupt status register - */ - dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg); - spin_lock_irqsave(&adapter->int_lock, flags); - adapter->int_status |= sdio_ireg; - spin_unlock_irqrestore(&adapter->int_lock, flags); - } -} - -/* - * SDIO interrupt handler. - * - * This function reads the interrupt status from firmware and handles - * the interrupt in current thread (ksdioirqd) right away. - */ -static void -mwifiex_sdio_interrupt(struct sdio_func *func) -{ - struct mwifiex_adapter *adapter; - struct sdio_mmc_card *card; - - card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_debug("int: func=%p card=%p adapter=%p\n", - func, card, card ? card->adapter : NULL); - return; - } - adapter = card->adapter; - - if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) - adapter->ps_state = PS_STATE_AWAKE; - - mwifiex_interrupt_status(adapter); - mwifiex_main_process(adapter); -} - -/* * This function decodes a received packet. * * Based on the type, the packet is treated as either a data, or @@ -1065,7 +1062,7 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, case MWIFIEX_TYPE_EVENT: dev_dbg(adapter->dev, "info: --- Rx: Event ---\n"); - adapter->event_cause = *(u32 *) skb->data; + adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data); if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) memcpy(adapter->event_body, @@ -1210,8 +1207,8 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { /* get curr PKT len & type */ - pkt_len = *(u16 *) &curr_ptr[0]; - pkt_type = *(u16 *) &curr_ptr[2]; + pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]); + pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]); /* copy pkt to deaggr buf */ skb_deaggr = card->mpa_rx.skb_arr[pind]; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 8ece485..c0268b5 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -707,8 +707,9 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) { tlv_mac = (void *)((u8 *)&key_material->key_param_set + key_param_len); - tlv_mac->tlv.type = cpu_to_le16(TLV_TYPE_STA_MAC_ADDR); - tlv_mac->tlv.len = cpu_to_le16(ETH_ALEN); + tlv_mac->header.type = + cpu_to_le16(TLV_TYPE_STA_MAC_ADDR); + tlv_mac->header.len = cpu_to_le16(ETH_ALEN); memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN); cmd_size = key_param_len + S_DS_GEN + sizeof(key_material->action) + @@ -1069,7 +1070,7 @@ mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv, int i, byte_len; u8 *stack_ptr = *buffer; - for (i = 0; i < MWIFIEX_MAX_FILTERS; i++) { + for (i = 0; i < MWIFIEX_MEF_MAX_FILTERS; i++) { filter = &mef_entry->filter[i]; if (!filter->filt_type) break; @@ -1078,7 +1079,7 @@ mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv, *stack_ptr = TYPE_DNUM; stack_ptr += 1; - byte_len = filter->byte_seq[MAX_BYTESEQ]; + byte_len = filter->byte_seq[MWIFIEX_MEF_MAX_BYTESEQ]; memcpy(stack_ptr, filter->byte_seq, byte_len); stack_ptr += byte_len; *stack_ptr = byte_len; @@ -1183,6 +1184,70 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, return 0; } +static int +mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_coalesce_cfg *coalesce_cfg = + &cmd->params.coalesce_cfg; + struct mwifiex_ds_coalesce_cfg *cfg = data_buf; + struct coalesce_filt_field_param *param; + u16 cnt, idx, length; + struct coalesce_receive_filt_rule *rule; + + cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG); + cmd->size = cpu_to_le16(S_DS_GEN); + + coalesce_cfg->action = cpu_to_le16(cmd_action); + coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules); + rule = coalesce_cfg->rule; + + for (cnt = 0; cnt < cfg->num_of_rules; cnt++) { + rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE); + rule->max_coalescing_delay = + cpu_to_le16(cfg->rule[cnt].max_coalescing_delay); + rule->pkt_type = cfg->rule[cnt].pkt_type; + rule->num_of_fields = cfg->rule[cnt].num_of_fields; + + length = 0; + + param = rule->params; + for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) { + param->operation = cfg->rule[cnt].params[idx].operation; + param->operand_len = + cfg->rule[cnt].params[idx].operand_len; + param->offset = + cpu_to_le16(cfg->rule[cnt].params[idx].offset); + memcpy(param->operand_byte_stream, + cfg->rule[cnt].params[idx].operand_byte_stream, + param->operand_len); + + length += sizeof(struct coalesce_filt_field_param); + + param++; + } + + /* Total rule length is sizeof max_coalescing_delay(u16), + * num_of_fields(u8), pkt_type(u8) and total length of the all + * params + */ + rule->header.len = cpu_to_le16(length + sizeof(u16) + + sizeof(u8) + sizeof(u8)); + + /* Add the rule length to the command size*/ + le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) + + sizeof(struct mwifiex_ie_types_header)); + + rule = (void *)((u8 *)rule->params + length); + } + + /* Add sizeof action, num_of_rules to total command length */ + le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16)); + + return 0; +} + /* * This function prepares the commands before sending them to the firmware. * @@ -1406,6 +1471,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_MEF_CFG: ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf); break; + case HostCmd_CMD_COALESCE_CFG: + ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action, + data_buf); + break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd- %#x\n", cmd_no); diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index d85df15..58a6013 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -280,7 +280,7 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); - tlv_buf_len = *(u16 *) (tlv_buf + sizeof(u16)); + tlv_buf_len = le16_to_cpu(*(__le16 *) (tlv_buf + sizeof(u16))); while (tlv_buf && tlv_buf_len > 0) { tlv = (*tlv_buf); @@ -997,6 +997,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, break; case HostCmd_CMD_MEF_CFG: break; + case HostCmd_CMD_COALESCE_CFG: + break; default: dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", resp->command); diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index ea265ec..8b05752 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -201,6 +201,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) case EVENT_DEAUTHENTICATED: dev_dbg(adapter->dev, "event: Deauthenticated\n"); + if (priv->wps.session_enable) { + dev_dbg(adapter->dev, + "info: receive deauth event in wps session\n"); + break; + } adapter->dbg.num_event_deauth++; if (priv->media_connected) { reason_code = @@ -211,6 +216,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) case EVENT_DISASSOCIATED: dev_dbg(adapter->dev, "event: Disassociated\n"); + if (priv->wps.session_enable) { + dev_dbg(adapter->dev, + "info: receive disassoc event in wps session\n"); + break; + } adapter->dbg.num_event_disassoc++; if (priv->media_connected) { reason_code = diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 8af97ab..f084412 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -797,15 +797,16 @@ static int mwifiex_set_wps_ie(struct mwifiex_private *priv, u8 *ie_data_ptr, u16 ie_len) { if (ie_len) { - priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL); - if (!priv->wps_ie) - return -ENOMEM; - if (ie_len > sizeof(priv->wps_ie)) { + if (ie_len > MWIFIEX_MAX_VSIE_LEN) { dev_dbg(priv->adapter->dev, "info: failed to copy WPS IE, too big\n"); - kfree(priv->wps_ie); return -1; } + + priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL); + if (!priv->wps_ie) + return -ENOMEM; + memcpy(priv->wps_ie, ie_data_ptr, ie_len); priv->wps_ie_len = ie_len; dev_dbg(priv->adapter->dev, "cmd: Set wps_ie_len=%d IE=%#x\n", diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index b5c1095..bb22664 100644 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ b/drivers/net/wireless/mwifiex/sta_rx.c @@ -17,6 +17,8 @@ * this warranty disclaimer. */ +#include <uapi/linux/ipv6.h> +#include <net/ndisc.h> #include "decl.h" #include "ioctl.h" #include "util.h" @@ -25,6 +27,46 @@ #include "11n_aggr.h" #include "11n_rxreorder.h" +/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement + * frame. If frame has both source and destination mac address as same, this + * function drops such gratuitous frames. + */ +static bool +mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + const struct mwifiex_arp_eth_header *arp; + struct ethhdr *eth_hdr; + struct ipv6hdr *ipv6; + struct icmp6hdr *icmpv6; + + eth_hdr = (struct ethhdr *)skb->data; + switch (ntohs(eth_hdr->h_proto)) { + case ETH_P_ARP: + arp = (void *)(skb->data + sizeof(struct ethhdr)); + if (arp->hdr.ar_op == htons(ARPOP_REPLY) || + arp->hdr.ar_op == htons(ARPOP_REQUEST)) { + if (!memcmp(arp->ar_sip, arp->ar_tip, 4)) + return true; + } + break; + case ETH_P_IPV6: + ipv6 = (void *)(skb->data + sizeof(struct ethhdr)); + icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) + + sizeof(struct ipv6hdr)); + if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) { + if (!memcmp(&ipv6->saddr, &ipv6->daddr, + sizeof(struct in6_addr))) + return true; + } + break; + default: + break; + } + + return false; +} + /* * This function processes the received packet and forwards it * to kernel/upper layer. @@ -90,6 +132,13 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, either the reconstructed EthII frame or the 802.2/llc/snap frame */ skb_pull(skb, hdr_chop); + if (priv->hs2_enabled && + mwifiex_discard_gratuitous_arp(priv, skb)) { + dev_dbg(priv->adapter->dev, "Bypassed Gratuitous ARP\n"); + dev_kfree_skb_any(skb); + return 0; + } + priv->rxpd_rate = local_rx_pd->rx_rate; priv->rxpd_htinfo = local_rx_pd->ht_info; diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index 2de882d..64424c8 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -293,9 +293,9 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) u8 *tlv = *tlv_buf; tlv_akmp = (struct host_cmd_tlv_akmp *)tlv; - tlv_akmp->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); - tlv_akmp->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - - sizeof(struct host_cmd_tlv)); + tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); + tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - + sizeof(struct mwifiex_ie_types_header)); tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation); tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt); cmd_size += sizeof(struct host_cmd_tlv_akmp); @@ -303,10 +303,10 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; - pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); - pwk_cipher->tlv.len = + pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA); pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa; cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); @@ -315,10 +315,10 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; - pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); - pwk_cipher->tlv.len = + pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2); pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2; cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); @@ -327,10 +327,10 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv; - gwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); - gwk_cipher->tlv.len = + gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); + gwk_cipher->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher; cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher); tlv += sizeof(struct host_cmd_tlv_gwk_cipher); @@ -338,13 +338,15 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) if (bss_cfg->wpa_cfg.length) { passphrase = (struct host_cmd_tlv_passphrase *)tlv; - passphrase->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); - passphrase->tlv.len = cpu_to_le16(bss_cfg->wpa_cfg.length); + passphrase->header.type = + cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); + passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length); memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase, bss_cfg->wpa_cfg.length); - cmd_size += sizeof(struct host_cmd_tlv) + + cmd_size += sizeof(struct mwifiex_ie_types_header) + bss_cfg->wpa_cfg.length; - tlv += sizeof(struct host_cmd_tlv) + bss_cfg->wpa_cfg.length; + tlv += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->wpa_cfg.length; } *param_size = cmd_size; @@ -403,16 +405,17 @@ mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size) (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 || bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) { wep_key = (struct host_cmd_tlv_wep_key *)tlv; - wep_key->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); - wep_key->tlv.len = + wep_key->header.type = + cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + wep_key->header.len = cpu_to_le16(bss_cfg->wep_cfg[i].length + 2); wep_key->key_index = bss_cfg->wep_cfg[i].key_index; wep_key->is_default = bss_cfg->wep_cfg[i].is_default; memcpy(wep_key->key, bss_cfg->wep_cfg[i].key, bss_cfg->wep_cfg[i].length); - cmd_size += sizeof(struct host_cmd_tlv) + 2 + + cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 + bss_cfg->wep_cfg[i].length; - tlv += sizeof(struct host_cmd_tlv) + 2 + + tlv += sizeof(struct mwifiex_ie_types_header) + 2 + bss_cfg->wep_cfg[i].length; } } @@ -449,16 +452,17 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) if (bss_cfg->ssid.ssid_len) { ssid = (struct host_cmd_tlv_ssid *)tlv; - ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_SSID); - ssid->tlv.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len); + ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID); + ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len); memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len); - cmd_size += sizeof(struct host_cmd_tlv) + + cmd_size += sizeof(struct mwifiex_ie_types_header) + bss_cfg->ssid.ssid_len; - tlv += sizeof(struct host_cmd_tlv) + bss_cfg->ssid.ssid_len; + tlv += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->ssid.ssid_len; bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv; - bcast_ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); - bcast_ssid->tlv.len = + bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); + bcast_ssid->header.len = cpu_to_le16(sizeof(bcast_ssid->bcast_ctl)); bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl; cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid); @@ -466,13 +470,13 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) } if (bss_cfg->rates[0]) { tlv_rates = (struct host_cmd_tlv_rates *)tlv; - tlv_rates->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RATES); + tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES); for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i]; i++) tlv_rates->rates[i] = bss_cfg->rates[i]; - tlv_rates->tlv.len = cpu_to_le16(i); + tlv_rates->header.len = cpu_to_le16(i); cmd_size += sizeof(struct host_cmd_tlv_rates) + i; tlv += sizeof(struct host_cmd_tlv_rates) + i; } @@ -482,10 +486,10 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) (bss_cfg->band_cfg == BAND_CONFIG_A && bss_cfg->channel <= MAX_CHANNEL_BAND_A))) { chan_band = (struct host_cmd_tlv_channel_band *)tlv; - chan_band->tlv.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); - chan_band->tlv.len = + chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + chan_band->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); chan_band->band_config = bss_cfg->band_cfg; chan_band->channel = bss_cfg->channel; cmd_size += sizeof(struct host_cmd_tlv_channel_band); @@ -494,11 +498,11 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD && bss_cfg->beacon_period <= MAX_BEACON_PERIOD) { beacon_period = (struct host_cmd_tlv_beacon_period *)tlv; - beacon_period->tlv.type = + beacon_period->header.type = cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); - beacon_period->tlv.len = + beacon_period->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); beacon_period->period = cpu_to_le16(bss_cfg->beacon_period); cmd_size += sizeof(struct host_cmd_tlv_beacon_period); tlv += sizeof(struct host_cmd_tlv_beacon_period); @@ -506,21 +510,22 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD && bss_cfg->dtim_period <= MAX_DTIM_PERIOD) { dtim_period = (struct host_cmd_tlv_dtim_period *)tlv; - dtim_period->tlv.type = cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); - dtim_period->tlv.len = + dtim_period->header.type = + cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); + dtim_period->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); dtim_period->period = bss_cfg->dtim_period; cmd_size += sizeof(struct host_cmd_tlv_dtim_period); tlv += sizeof(struct host_cmd_tlv_dtim_period); } if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) { rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv; - rts_threshold->tlv.type = + rts_threshold->header.type = cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); - rts_threshold->tlv.len = + rts_threshold->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold); cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); tlv += sizeof(struct host_cmd_tlv_frag_threshold); @@ -528,21 +533,22 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) && (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) { frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv; - frag_threshold->tlv.type = + frag_threshold->header.type = cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); - frag_threshold->tlv.len = + frag_threshold->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold); cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); tlv += sizeof(struct host_cmd_tlv_frag_threshold); } if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) { retry_limit = (struct host_cmd_tlv_retry_limit *)tlv; - retry_limit->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); - retry_limit->tlv.len = + retry_limit->header.type = + cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); + retry_limit->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); retry_limit->limit = (u8)bss_cfg->retry_limit; cmd_size += sizeof(struct host_cmd_tlv_retry_limit); tlv += sizeof(struct host_cmd_tlv_retry_limit); @@ -557,21 +563,21 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) || (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) { auth_type = (struct host_cmd_tlv_auth_type *)tlv; - auth_type->tlv.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); - auth_type->tlv.len = + auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth_type->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) - - sizeof(struct host_cmd_tlv)); + sizeof(struct mwifiex_ie_types_header)); auth_type->auth_type = (u8)bss_cfg->auth_mode; cmd_size += sizeof(struct host_cmd_tlv_auth_type); tlv += sizeof(struct host_cmd_tlv_auth_type); } if (bss_cfg->protocol) { encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv; - encrypt_protocol->tlv.type = + encrypt_protocol->header.type = cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL); - encrypt_protocol->tlv.len = + encrypt_protocol->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol) - - sizeof(struct host_cmd_tlv)); + - sizeof(struct mwifiex_ie_types_header)); encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol); cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol); tlv += sizeof(struct host_cmd_tlv_encrypt_protocol); @@ -608,9 +614,9 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) if (bss_cfg->sta_ao_timer) { ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; - ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); - ao_timer->tlv.len = cpu_to_le16(sizeof(*ao_timer) - - sizeof(struct host_cmd_tlv)); + ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); + ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) - + sizeof(struct mwifiex_ie_types_header)); ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer); cmd_size += sizeof(*ao_timer); tlv += sizeof(*ao_timer); @@ -618,9 +624,10 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) if (bss_cfg->ps_sta_ao_timer) { ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; - ps_ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER); - ps_ao_timer->tlv.len = cpu_to_le16(sizeof(*ps_ao_timer) - - sizeof(struct host_cmd_tlv)); + ps_ao_timer->header.type = + cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER); + ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) - + sizeof(struct mwifiex_ie_types_header)); ps_ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->ps_sta_ao_timer); cmd_size += sizeof(*ps_ao_timer); @@ -636,16 +643,17 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size) { struct mwifiex_ie_list *ap_ie = cmd_buf; - struct host_cmd_tlv *tlv_ie = (struct host_cmd_tlv *)tlv; + struct mwifiex_ie_types_header *tlv_ie = (void *)tlv; if (!ap_ie || !ap_ie->len || !ap_ie->ie_list) return -1; - *ie_size += le16_to_cpu(ap_ie->len) + sizeof(struct host_cmd_tlv); + *ie_size += le16_to_cpu(ap_ie->len) + + sizeof(struct mwifiex_ie_types_header); tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); tlv_ie->len = ap_ie->len; - tlv += sizeof(struct host_cmd_tlv); + tlv += sizeof(struct mwifiex_ie_types_header); memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len)); diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index a018e42..1cfe5a7 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -24,6 +24,69 @@ #include "11n_aggr.h" #include "11n_rxreorder.h" +/* This function checks if particular RA list has packets more than low bridge + * packet threshold and then deletes packet from this RA list. + * Function deletes packets from such RA list and returns true. If no such list + * is found, false is returned. + */ +static bool +mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, + struct list_head *ra_list_head) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct sk_buff *skb, *tmp; + bool pkt_deleted = false; + struct mwifiex_txinfo *tx_info; + struct mwifiex_adapter *adapter = priv->adapter; + + list_for_each_entry(ra_list, ra_list_head, list) { + if (skb_queue_empty(&ra_list->skb_head)) + continue; + + skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) { + tx_info = MWIFIEX_SKB_TXCB(skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) { + __skb_unlink(skb, &ra_list->skb_head); + mwifiex_write_data_complete(adapter, skb, 0, + -1); + atomic_dec(&priv->wmm.tx_pkts_queued); + pkt_deleted = true; + } + if ((atomic_read(&adapter->pending_bridged_pkts) <= + MWIFIEX_BRIDGED_PKTS_THR_LOW)) + break; + } + } + + return pkt_deleted; +} + +/* This function deletes packets from particular RA List. RA list index + * from which packets are deleted is preserved so that packets from next RA + * list are deleted upon subsequent call thus maintaining fairness. + */ +static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv) +{ + unsigned long flags; + struct list_head *ra_list; + int i; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) { + if (priv->del_list_idx == MAX_NUM_TID) + priv->del_list_idx = 0; + ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list; + if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list)) { + priv->del_list_idx++; + break; + } + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + + static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, struct sk_buff *skb) { @@ -40,10 +103,11 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); if ((atomic_read(&adapter->pending_bridged_pkts) >= - MWIFIEX_BRIDGED_PKTS_THRESHOLD)) { + MWIFIEX_BRIDGED_PKTS_THR_HIGH)) { dev_err(priv->adapter->dev, "Tx: Bridge packet limit reached. Drop packet!\n"); kfree_skb(skb); + mwifiex_uap_cleanup_tx_queues(priv); return; } @@ -95,10 +159,6 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, atomic_inc(&adapter->tx_pending); atomic_inc(&adapter->pending_bridged_pkts); - if ((atomic_read(&adapter->tx_pending) >= MAX_TX_PENDING)) { - mwifiex_set_trans_start(priv->netdev); - mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); - } return; } diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index f90fe21..2472d4b 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -24,9 +24,9 @@ static const char usbdriver_name[] = "usb8797"; -static u8 user_rmmod; static struct mwifiex_if_ops usb_ops; static struct semaphore add_remove_card_sem; +static struct usb_card_rec *usb_card; static struct usb_device_id mwifiex_usb_table[] = { {USB_DEVICE(USB8797_VID, USB8797_PID_1)}, @@ -350,6 +350,7 @@ static int mwifiex_usb_probe(struct usb_interface *intf, card->udev = udev; card->intf = intf; + usb_card = card; pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocol=%#x\n", udev->descriptor.bcdUSB, udev->descriptor.bDeviceClass, @@ -532,7 +533,6 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) { struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; - int i; if (!card || !card->adapter) { pr_err("%s: card or card->adapter is NULL\n", __func__); @@ -543,27 +543,6 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) if (!adapter->priv_num) return; - /* In case driver is removed when asynchronous FW downloading is - * in progress - */ - wait_for_completion(&adapter->fw_load); - - if (user_rmmod) { -#ifdef CONFIG_PM - if (adapter->is_suspended) - mwifiex_usb_resume(intf); -#endif - for (i = 0; i < adapter->priv_num; i++) - if ((GET_BSS_ROLE(adapter->priv[i]) == - MWIFIEX_BSS_ROLE_STA) && - adapter->priv[i]->media_connected) - mwifiex_deauthenticate(adapter->priv[i], NULL); - - mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY), - MWIFIEX_FUNC_SHUTDOWN); - } - mwifiex_usb_free(card); dev_dbg(adapter->dev, "%s: removing card\n", __func__); @@ -786,6 +765,13 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) return 0; } +static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + usb_set_intfdata(card->intf, NULL); +} + static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *fw) { @@ -978,6 +964,7 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) static struct mwifiex_if_ops usb_ops = { .register_dev = mwifiex_register_dev, + .unregister_dev = mwifiex_unregister_dev, .wakeup = mwifiex_pm_wakeup_card, .wakeup_complete = mwifiex_pm_wakeup_card_complete, @@ -1024,8 +1011,29 @@ static void mwifiex_usb_cleanup_module(void) if (!down_interruptible(&add_remove_card_sem)) up(&add_remove_card_sem); - /* set the flag as user is removing this module */ - user_rmmod = 1; + if (usb_card) { + struct mwifiex_adapter *adapter = usb_card->adapter; + int i; + + /* In case driver is removed when asynchronous FW downloading is + * in progress + */ + wait_for_completion(&adapter->fw_load); + +#ifdef CONFIG_PM + if (adapter->is_suspended) + mwifiex_usb_resume(usb_card->intf); +#endif + for (i = 0; i < adapter->priv_num; i++) + if ((GET_BSS_ROLE(adapter->priv[i]) == + MWIFIEX_BSS_ROLE_STA) && + adapter->priv[i]->media_connected) + mwifiex_deauthenticate(adapter->priv[i], NULL); + + mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), + MWIFIEX_FUNC_SHUTDOWN); + } usb_deregister(&mwifiex_usb_driver); } diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index e57ac0d..5d9e150 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -171,8 +171,8 @@ mwifiex_process_mgmt_packet(struct mwifiex_private *priv, rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); cfg80211_rx_mgmt(priv->wdev, priv->roc_cfg.chan.center_freq, - CAL_RSSI(rx_pd->snr, rx_pd->nf), - skb->data, pkt_len, GFP_ATOMIC); + CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, + 0, GFP_ATOMIC); return 0; } diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 944e884..2e8f9cd 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -120,7 +120,7 @@ mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra) memcpy(ra_list->ra, ra, ETH_ALEN); - ra_list->total_pkts_size = 0; + ra_list->total_pkt_count = 0; dev_dbg(adapter->dev, "info: allocated ra_list %p\n", ra_list); @@ -188,7 +188,7 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra) ra_list, ra_list->is_11n_enabled); if (ra_list->is_11n_enabled) { - ra_list->pkt_count = 0; + ra_list->ba_pkt_count = 0; ra_list->ba_packet_thr = mwifiex_get_random_ba_threshold(); } @@ -679,8 +679,8 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, skb_queue_tail(&ra_list->skb_head, skb); - ra_list->total_pkts_size += skb->len; - ra_list->pkt_count++; + ra_list->ba_pkt_count++; + ra_list->total_pkt_count++; if (atomic_read(&priv->wmm.highest_queued_prio) < tos_to_tid_inv[tid_down]) @@ -1037,7 +1037,7 @@ mwifiex_send_single_packet(struct mwifiex_private *priv, tx_info = MWIFIEX_SKB_TXCB(skb); dev_dbg(adapter->dev, "data: dequeuing the packet %p %p\n", ptr, skb); - ptr->total_pkts_size -= skb->len; + ptr->total_pkt_count--; if (!skb_queue_empty(&ptr->skb_head)) skb_next = skb_peek(&ptr->skb_head); @@ -1062,8 +1062,8 @@ mwifiex_send_single_packet(struct mwifiex_private *priv, skb_queue_tail(&ptr->skb_head, skb); - ptr->total_pkts_size += skb->len; - ptr->pkt_count++; + ptr->total_pkt_count++; + ptr->ba_pkt_count++; tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); @@ -1224,7 +1224,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) mwifiex_send_single_packet() */ } else { if (mwifiex_is_ampdu_allowed(priv, tid) && - ptr->pkt_count > ptr->ba_packet_thr) { + ptr->ba_pkt_count > ptr->ba_packet_thr) { if (mwifiex_space_avail_for_new_ba_stream(adapter)) { mwifiex_create_ba_tbl(priv, ptr->ra, tid, BA_SETUP_INPROGRESS); diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index 3e60a31..68dbbb9 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -166,6 +166,12 @@ config RT2800USB_RT35XX rt2800usb driver. Supported chips: RT3572 +config RT2800USB_RT3573 + bool "rt2800usb - Include support for rt3573 devices (EXPERIMENTAL)" + ---help--- + This enables support for RT3573 chipset based wireless USB devices + in the rt2800usb driver. + config RT2800USB_RT53XX bool "rt2800usb - Include support for rt53xx devices (EXPERIMENTAL)" ---help--- diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index d78c495..fa33b5e 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -88,6 +88,7 @@ #define REV_RT3071E 0x0211 #define REV_RT3090E 0x0211 #define REV_RT3390E 0x0211 +#define REV_RT3593E 0x0211 #define REV_RT5390F 0x0502 #define REV_RT5390R 0x1502 #define REV_RT5592C 0x0221 @@ -1082,6 +1083,15 @@ #define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000) #define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000) #define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_0_CCK1_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_0_CCK1_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_0_CCK5_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_0_CCK5_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_0_OFDM6_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_0_OFDM6_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_0_OFDM12_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_0_OFDM12_CH1 FIELD32(0xf0000000) /* * TX_PWR_CFG_1: @@ -1095,6 +1105,15 @@ #define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000) #define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000) #define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_1_OFDM24_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_1_OFDM24_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_1_OFDM48_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_1_OFDM48_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_1_MCS0_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_1_MCS0_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_1_MCS2_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_1_MCS2_CH1 FIELD32(0xf0000000) /* * TX_PWR_CFG_2: @@ -1108,6 +1127,15 @@ #define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000) #define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000) #define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_2_MCS4_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_2_MCS4_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_2_MCS6_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_2_MCS6_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_2_MCS8_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_2_MCS8_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_2_MCS10_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_2_MCS10_CH1 FIELD32(0xf0000000) /* * TX_PWR_CFG_3: @@ -1121,6 +1149,15 @@ #define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000) #define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000) #define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_3_MCS12_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_MCS12_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_3_MCS14_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_MCS14_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_3_STBC0_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_3_STBC0_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_3_STBC2_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_3_STBC2_CH1 FIELD32(0xf0000000) /* * TX_PWR_CFG_4: @@ -1130,6 +1167,11 @@ #define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0) #define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00) #define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000) +/* bits for 3T devices */ +#define TX_PWR_CFG_3_STBC4_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_STBC4_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_3_STBC6_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_STBC6_CH1 FIELD32(0x0000f000) /* * TX_PIN_CFG: @@ -1451,6 +1493,81 @@ */ #define EXP_ACK_TIME 0x1380 +/* TX_PWR_CFG_5 */ +#define TX_PWR_CFG_5 0x1384 +#define TX_PWR_CFG_5_MCS16_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_5_MCS16_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_5_MCS16_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_5_MCS18_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_5_MCS18_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_5_MCS18_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_6 */ +#define TX_PWR_CFG_6 0x1388 +#define TX_PWR_CFG_6_MCS20_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_6_MCS20_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_6_MCS20_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_6_MCS22_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_6_MCS22_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_6_MCS22_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_0_EXT */ +#define TX_PWR_CFG_0_EXT 0x1390 +#define TX_PWR_CFG_0_EXT_CCK1_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_0_EXT_CCK5_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_0_EXT_OFDM6_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_0_EXT_OFDM12_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_1_EXT */ +#define TX_PWR_CFG_1_EXT 0x1394 +#define TX_PWR_CFG_1_EXT_OFDM24_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_1_EXT_OFDM48_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_1_EXT_MCS0_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_1_EXT_MCS2_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_2_EXT */ +#define TX_PWR_CFG_2_EXT 0x1398 +#define TX_PWR_CFG_2_EXT_MCS4_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_2_EXT_MCS6_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_2_EXT_MCS8_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_2_EXT_MCS10_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_3_EXT */ +#define TX_PWR_CFG_3_EXT 0x139c +#define TX_PWR_CFG_3_EXT_MCS12_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_EXT_MCS14_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_EXT_STBC0_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_3_EXT_STBC2_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_4_EXT */ +#define TX_PWR_CFG_4_EXT 0x13a0 +#define TX_PWR_CFG_4_EXT_STBC4_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_4_EXT_STBC6_CH2 FIELD32(0x00000f00) + +/* TX_PWR_CFG_7 */ +#define TX_PWR_CFG_7 0x13d4 +#define TX_PWR_CFG_7_OFDM54_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_7_OFDM54_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_7_OFDM54_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_7_MCS7_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_7_MCS7_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_7_MCS7_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_8 */ +#define TX_PWR_CFG_8 0x13d8 +#define TX_PWR_CFG_8_MCS15_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_8_MCS15_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_8_MCS15_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_8_MCS23_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_8_MCS23_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_8_MCS23_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_9 */ +#define TX_PWR_CFG_9 0x13dc +#define TX_PWR_CFG_9_STBC7_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_9_STBC7_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_9_STBC7_CH2 FIELD32(0x00000f00) + /* * RX_FILTER_CFG: RX configuration register. */ @@ -1902,11 +2019,13 @@ struct mac_iveiv_entry { #define HW_BEACON_BASE6 0x5dc0 #define HW_BEACON_BASE7 0x5bc0 -#define HW_BEACON_OFFSET(__index) \ +#define HW_BEACON_BASE(__index) \ (((__index) < 4) ? (HW_BEACON_BASE0 + (__index * 0x0200)) : \ (((__index) < 6) ? (HW_BEACON_BASE4 + ((__index - 4) * 0x0200)) : \ (HW_BEACON_BASE6 - ((__index - 6) * 0x0200)))) +#define BEACON_BASE_TO_OFFSET(_base) (((_base) - 0x4000) / 64) + /* * BBP registers. * The wordsize of the BBP is 8 bits. @@ -1975,6 +2094,10 @@ struct mac_iveiv_entry { #define BBP109_TX0_POWER FIELD8(0x0f) #define BBP109_TX1_POWER FIELD8(0xf0) +/* BBP 110 */ +#define BBP110_TX2_POWER FIELD8(0x0f) + + /* * BBP 138: Unknown */ @@ -2024,6 +2147,12 @@ struct mac_iveiv_entry { #define RFCSR3_PA2_CASCODE_BIAS_CCKK FIELD8(0x80) /* Bits for RF3290/RF5360/RF5370/RF5372/RF5390/RF5392 */ #define RFCSR3_VCOCAL_EN FIELD8(0x80) +/* Bits for RF3050 */ +#define RFCSR3_BIT1 FIELD8(0x02) +#define RFCSR3_BIT2 FIELD8(0x04) +#define RFCSR3_BIT3 FIELD8(0x08) +#define RFCSR3_BIT4 FIELD8(0x10) +#define RFCSR3_BIT5 FIELD8(0x20) /* * FRCSR 5: @@ -2036,6 +2165,8 @@ struct mac_iveiv_entry { #define RFCSR6_R1 FIELD8(0x03) #define RFCSR6_R2 FIELD8(0x40) #define RFCSR6_TXDIV FIELD8(0x0c) +/* bits for RF3053 */ +#define RFCSR6_VCO_IC FIELD8(0xc0) /* * RFCSR 7: @@ -2060,7 +2191,12 @@ struct mac_iveiv_entry { * RFCSR 11: */ #define RFCSR11_R FIELD8(0x03) +#define RFCSR11_PLL_MOD FIELD8(0x0c) #define RFCSR11_MOD FIELD8(0xc0) +/* bits for RF3053 */ +/* TODO: verify RFCSR11_MOD usage on other chips */ +#define RFCSR11_PLL_IDOH FIELD8(0x40) + /* * RFCSR 12: @@ -2092,6 +2228,10 @@ struct mac_iveiv_entry { #define RFCSR17_R FIELD8(0x20) #define RFCSR17_CODE FIELD8(0x7f) +/* RFCSR 18 */ +#define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40) + + /* * RFCSR 20: */ @@ -2152,6 +2292,12 @@ struct mac_iveiv_entry { #define RFCSR31_RX_H20M FIELD8(0x20) #define RFCSR31_RX_CALIB FIELD8(0x7f) +/* RFCSR 32 bits for RF3053 */ +#define RFCSR32_TX_AGC_FC FIELD8(0xf8) + +/* RFCSR 36 bits for RF3053 */ +#define RFCSR36_RF_BS FIELD8(0x80) + /* * RFCSR 38: */ @@ -2160,6 +2306,7 @@ struct mac_iveiv_entry { /* * RFCSR 39: */ +#define RFCSR39_RX_DIV FIELD8(0x40) #define RFCSR39_RX_LO2_EN FIELD8(0x80) /* @@ -2167,12 +2314,36 @@ struct mac_iveiv_entry { */ #define RFCSR49_TX FIELD8(0x3f) #define RFCSR49_EP FIELD8(0xc0) +/* bits for RT3593 */ +#define RFCSR49_TX_LO1_IC FIELD8(0x1c) +#define RFCSR49_TX_DIV FIELD8(0x20) /* * RFCSR 50: */ #define RFCSR50_TX FIELD8(0x3f) #define RFCSR50_EP FIELD8(0xc0) +/* bits for RT3593 */ +#define RFCSR50_TX_LO1_EN FIELD8(0x20) +#define RFCSR50_TX_LO2_EN FIELD8(0x10) + +/* RFCSR 51 */ +/* bits for RT3593 */ +#define RFCSR51_BITS01 FIELD8(0x03) +#define RFCSR51_BITS24 FIELD8(0x1c) +#define RFCSR51_BITS57 FIELD8(0xe0) + +#define RFCSR53_TX_POWER FIELD8(0x3f) +#define RFCSR53_UNKNOWN FIELD8(0xc0) + +#define RFCSR54_TX_POWER FIELD8(0x3f) +#define RFCSR54_UNKNOWN FIELD8(0xc0) + +#define RFCSR55_TX_POWER FIELD8(0x3f) +#define RFCSR55_UNKNOWN FIELD8(0xc0) + +#define RFCSR57_DRV_CC FIELD8(0xfc) + /* * RF registers @@ -2206,28 +2377,67 @@ struct mac_iveiv_entry { * The wordsize of the EEPROM is 16 bits. */ -/* - * Chip ID - */ -#define EEPROM_CHIP_ID 0x0000 +enum rt2800_eeprom_word { + EEPROM_CHIP_ID = 0, + EEPROM_VERSION, + EEPROM_MAC_ADDR_0, + EEPROM_MAC_ADDR_1, + EEPROM_MAC_ADDR_2, + EEPROM_NIC_CONF0, + EEPROM_NIC_CONF1, + EEPROM_FREQ, + EEPROM_LED_AG_CONF, + EEPROM_LED_ACT_CONF, + EEPROM_LED_POLARITY, + EEPROM_NIC_CONF2, + EEPROM_LNA, + EEPROM_RSSI_BG, + EEPROM_RSSI_BG2, + EEPROM_TXMIXER_GAIN_BG, + EEPROM_RSSI_A, + EEPROM_RSSI_A2, + EEPROM_TXMIXER_GAIN_A, + EEPROM_EIRP_MAX_TX_POWER, + EEPROM_TXPOWER_DELTA, + EEPROM_TXPOWER_BG1, + EEPROM_TXPOWER_BG2, + EEPROM_TSSI_BOUND_BG1, + EEPROM_TSSI_BOUND_BG2, + EEPROM_TSSI_BOUND_BG3, + EEPROM_TSSI_BOUND_BG4, + EEPROM_TSSI_BOUND_BG5, + EEPROM_TXPOWER_A1, + EEPROM_TXPOWER_A2, + EEPROM_TSSI_BOUND_A1, + EEPROM_TSSI_BOUND_A2, + EEPROM_TSSI_BOUND_A3, + EEPROM_TSSI_BOUND_A4, + EEPROM_TSSI_BOUND_A5, + EEPROM_TXPOWER_BYRATE, + EEPROM_BBP_START, + + /* IDs for extended EEPROM format used by three-chain devices */ + EEPROM_EXT_LNA2, + EEPROM_EXT_TXPOWER_BG3, + EEPROM_EXT_TXPOWER_A3, + + /* New values must be added before this */ + EEPROM_WORD_COUNT +}; /* * EEPROM Version */ -#define EEPROM_VERSION 0x0001 #define EEPROM_VERSION_FAE FIELD16(0x00ff) #define EEPROM_VERSION_VERSION FIELD16(0xff00) /* * HW MAC address. */ -#define EEPROM_MAC_ADDR_0 0x0002 #define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_1 0x0003 #define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_2 0x0004 #define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) @@ -2237,7 +2447,6 @@ struct mac_iveiv_entry { * TXPATH: 1: 1T, 2: 2T, 3: 3T * RF_TYPE: RFIC type */ -#define EEPROM_NIC_CONF0 0x001a #define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f) #define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0) #define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00) @@ -2261,7 +2470,6 @@ struct mac_iveiv_entry { * BT_COEXIST: 0: disable, 1: enable * DAC_TEST: 0: disable, 1: enable */ -#define EEPROM_NIC_CONF1 0x001b #define EEPROM_NIC_CONF1_HW_RADIO FIELD16(0x0001) #define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002) #define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004) @@ -2281,7 +2489,6 @@ struct mac_iveiv_entry { /* * EEPROM frequency */ -#define EEPROM_FREQ 0x001d #define EEPROM_FREQ_OFFSET FIELD16(0x00ff) #define EEPROM_FREQ_LED_MODE FIELD16(0x7f00) #define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000) @@ -2298,9 +2505,6 @@ struct mac_iveiv_entry { * POLARITY_GPIO_4: Polarity GPIO4 setting. * LED_MODE: Led mode. */ -#define EEPROM_LED_AG_CONF 0x001e -#define EEPROM_LED_ACT_CONF 0x001f -#define EEPROM_LED_POLARITY 0x0020 #define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001) #define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) #define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) @@ -2317,7 +2521,6 @@ struct mac_iveiv_entry { * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved */ -#define EEPROM_NIC_CONF2 0x0021 #define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) #define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) #define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) @@ -2325,54 +2528,46 @@ struct mac_iveiv_entry { /* * EEPROM LNA */ -#define EEPROM_LNA 0x0022 #define EEPROM_LNA_BG FIELD16(0x00ff) #define EEPROM_LNA_A0 FIELD16(0xff00) /* * EEPROM RSSI BG offset */ -#define EEPROM_RSSI_BG 0x0023 #define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff) #define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00) /* * EEPROM RSSI BG2 offset */ -#define EEPROM_RSSI_BG2 0x0024 #define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff) #define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00) /* * EEPROM TXMIXER GAIN BG offset (note overlaps with EEPROM RSSI BG2). */ -#define EEPROM_TXMIXER_GAIN_BG 0x0024 #define EEPROM_TXMIXER_GAIN_BG_VAL FIELD16(0x0007) /* * EEPROM RSSI A offset */ -#define EEPROM_RSSI_A 0x0025 #define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff) #define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00) /* * EEPROM RSSI A2 offset */ -#define EEPROM_RSSI_A2 0x0026 #define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff) #define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00) /* * EEPROM TXMIXER GAIN A offset (note overlaps with EEPROM RSSI A2). */ -#define EEPROM_TXMIXER_GAIN_A 0x0026 #define EEPROM_TXMIXER_GAIN_A_VAL FIELD16(0x0007) /* * EEPROM EIRP Maximum TX power values(unit: dbm) */ -#define EEPROM_EIRP_MAX_TX_POWER 0x0027 #define EEPROM_EIRP_MAX_TX_POWER_2GHZ FIELD16(0x00ff) #define EEPROM_EIRP_MAX_TX_POWER_5GHZ FIELD16(0xff00) @@ -2383,7 +2578,6 @@ struct mac_iveiv_entry { * TYPE: 1: Plus the delta value, 0: minus the delta value * ENABLE: enable tx power compensation for 40BW */ -#define EEPROM_TXPOWER_DELTA 0x0028 #define EEPROM_TXPOWER_DELTA_VALUE_2G FIELD16(0x003f) #define EEPROM_TXPOWER_DELTA_TYPE_2G FIELD16(0x0040) #define EEPROM_TXPOWER_DELTA_ENABLE_2G FIELD16(0x0080) @@ -2394,8 +2588,6 @@ struct mac_iveiv_entry { /* * EEPROM TXPOWER 802.11BG */ -#define EEPROM_TXPOWER_BG1 0x0029 -#define EEPROM_TXPOWER_BG2 0x0030 #define EEPROM_TXPOWER_BG_SIZE 7 #define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_BG_2 FIELD16(0xff00) @@ -2407,7 +2599,6 @@ struct mac_iveiv_entry { * MINUS3: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -3) */ -#define EEPROM_TSSI_BOUND_BG1 0x0037 #define EEPROM_TSSI_BOUND_BG1_MINUS4 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG1_MINUS3 FIELD16(0xff00) @@ -2418,7 +2609,6 @@ struct mac_iveiv_entry { * MINUS1: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -1) */ -#define EEPROM_TSSI_BOUND_BG2 0x0038 #define EEPROM_TSSI_BOUND_BG2_MINUS2 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG2_MINUS1 FIELD16(0xff00) @@ -2428,7 +2618,6 @@ struct mac_iveiv_entry { * PLUS1: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 1) */ -#define EEPROM_TSSI_BOUND_BG3 0x0039 #define EEPROM_TSSI_BOUND_BG3_REF FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG3_PLUS1 FIELD16(0xff00) @@ -2439,7 +2628,6 @@ struct mac_iveiv_entry { * PLUS3: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 3) */ -#define EEPROM_TSSI_BOUND_BG4 0x003a #define EEPROM_TSSI_BOUND_BG4_PLUS2 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG4_PLUS3 FIELD16(0xff00) @@ -2449,19 +2637,20 @@ struct mac_iveiv_entry { * increased by (agc_step * 4) * AGC_STEP: Temperature compensation step. */ -#define EEPROM_TSSI_BOUND_BG5 0x003b #define EEPROM_TSSI_BOUND_BG5_PLUS4 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_BG5_AGC_STEP FIELD16(0xff00) /* * EEPROM TXPOWER 802.11A */ -#define EEPROM_TXPOWER_A1 0x003c -#define EEPROM_TXPOWER_A2 0x0053 #define EEPROM_TXPOWER_A_SIZE 6 #define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) #define EEPROM_TXPOWER_A_2 FIELD16(0xff00) +/* EEPROM_TXPOWER_{A,G} fields for RT3593 */ +#define EEPROM_TXPOWER_ALC FIELD8(0x1f) +#define EEPROM_TXPOWER_FINE_CTRL FIELD8(0xe0) + /* * EEPROM temperature compensation boundaries 802.11A * MINUS4: If the actual TSSI is below this boundary, tx power needs to be @@ -2469,7 +2658,6 @@ struct mac_iveiv_entry { * MINUS3: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -3) */ -#define EEPROM_TSSI_BOUND_A1 0x006a #define EEPROM_TSSI_BOUND_A1_MINUS4 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A1_MINUS3 FIELD16(0xff00) @@ -2480,7 +2668,6 @@ struct mac_iveiv_entry { * MINUS1: If the actual TSSI is below this boundary, tx power needs to be * reduced by (agc_step * -1) */ -#define EEPROM_TSSI_BOUND_A2 0x006b #define EEPROM_TSSI_BOUND_A2_MINUS2 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A2_MINUS1 FIELD16(0xff00) @@ -2490,7 +2677,6 @@ struct mac_iveiv_entry { * PLUS1: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 1) */ -#define EEPROM_TSSI_BOUND_A3 0x006c #define EEPROM_TSSI_BOUND_A3_REF FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A3_PLUS1 FIELD16(0xff00) @@ -2501,7 +2687,6 @@ struct mac_iveiv_entry { * PLUS3: If the actual TSSI is above this boundary, tx power needs to be * increased by (agc_step * 3) */ -#define EEPROM_TSSI_BOUND_A4 0x006d #define EEPROM_TSSI_BOUND_A4_PLUS2 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A4_PLUS3 FIELD16(0xff00) @@ -2511,14 +2696,12 @@ struct mac_iveiv_entry { * increased by (agc_step * 4) * AGC_STEP: Temperature compensation step. */ -#define EEPROM_TSSI_BOUND_A5 0x006e #define EEPROM_TSSI_BOUND_A5_PLUS4 FIELD16(0x00ff) #define EEPROM_TSSI_BOUND_A5_AGC_STEP FIELD16(0xff00) /* * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode */ -#define EEPROM_TXPOWER_BYRATE 0x006f #define EEPROM_TXPOWER_BYRATE_SIZE 9 #define EEPROM_TXPOWER_BYRATE_RATE0 FIELD16(0x000f) @@ -2529,11 +2712,14 @@ struct mac_iveiv_entry { /* * EEPROM BBP. */ -#define EEPROM_BBP_START 0x0078 #define EEPROM_BBP_SIZE 16 #define EEPROM_BBP_VALUE FIELD16(0x00ff) #define EEPROM_BBP_REG_ID FIELD16(0xff00) +/* EEPROM_EXT_LNA2 */ +#define EEPROM_EXT_LNA2_A1 FIELD16(0x00ff) +#define EEPROM_EXT_LNA2_A2 FIELD16(0xff00) + /* * EEPROM IQ Calibration, unlike other entries those are byte addresses. */ @@ -2610,6 +2796,7 @@ struct mac_iveiv_entry { #define MCU_RADAR 0x60 #define MCU_BOOT_SIGNAL 0x72 #define MCU_ANT_SELECT 0X73 +#define MCU_FREQ_OFFSET 0x74 #define MCU_BBP_SIGNAL 0x80 #define MCU_POWER_SAVE 0x83 #define MCU_BAND_SELECT 0x91 @@ -2630,6 +2817,7 @@ struct mac_iveiv_entry { #define TXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32)) #define RXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32)) +#define RXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32)) #define RXWI_DESC_SIZE_6WORDS (6 * sizeof(__le32)) /* @@ -2750,18 +2938,15 @@ struct mac_iveiv_entry { #define MAX_A_TXPOWER 15 #define DEFAULT_TXPOWER 5 +#define MIN_A_TXPOWER_3593 0 +#define MAX_A_TXPOWER_3593 31 + #define TXPOWER_G_FROM_DEV(__txpower) \ ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) -#define TXPOWER_G_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_G_TXPOWER, MAX_G_TXPOWER) - #define TXPOWER_A_FROM_DEV(__txpower) \ ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) -#define TXPOWER_A_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_A_TXPOWER, MAX_A_TXPOWER) - /* * Board's maximun TX power limitation */ diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 1b41c8e..95e6e61 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -221,6 +221,157 @@ static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev, mutex_unlock(&rt2x00dev->csr_mutex); } +static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = { + [EEPROM_CHIP_ID] = 0x0000, + [EEPROM_VERSION] = 0x0001, + [EEPROM_MAC_ADDR_0] = 0x0002, + [EEPROM_MAC_ADDR_1] = 0x0003, + [EEPROM_MAC_ADDR_2] = 0x0004, + [EEPROM_NIC_CONF0] = 0x001a, + [EEPROM_NIC_CONF1] = 0x001b, + [EEPROM_FREQ] = 0x001d, + [EEPROM_LED_AG_CONF] = 0x001e, + [EEPROM_LED_ACT_CONF] = 0x001f, + [EEPROM_LED_POLARITY] = 0x0020, + [EEPROM_NIC_CONF2] = 0x0021, + [EEPROM_LNA] = 0x0022, + [EEPROM_RSSI_BG] = 0x0023, + [EEPROM_RSSI_BG2] = 0x0024, + [EEPROM_TXMIXER_GAIN_BG] = 0x0024, /* overlaps with RSSI_BG2 */ + [EEPROM_RSSI_A] = 0x0025, + [EEPROM_RSSI_A2] = 0x0026, + [EEPROM_TXMIXER_GAIN_A] = 0x0026, /* overlaps with RSSI_A2 */ + [EEPROM_EIRP_MAX_TX_POWER] = 0x0027, + [EEPROM_TXPOWER_DELTA] = 0x0028, + [EEPROM_TXPOWER_BG1] = 0x0029, + [EEPROM_TXPOWER_BG2] = 0x0030, + [EEPROM_TSSI_BOUND_BG1] = 0x0037, + [EEPROM_TSSI_BOUND_BG2] = 0x0038, + [EEPROM_TSSI_BOUND_BG3] = 0x0039, + [EEPROM_TSSI_BOUND_BG4] = 0x003a, + [EEPROM_TSSI_BOUND_BG5] = 0x003b, + [EEPROM_TXPOWER_A1] = 0x003c, + [EEPROM_TXPOWER_A2] = 0x0053, + [EEPROM_TSSI_BOUND_A1] = 0x006a, + [EEPROM_TSSI_BOUND_A2] = 0x006b, + [EEPROM_TSSI_BOUND_A3] = 0x006c, + [EEPROM_TSSI_BOUND_A4] = 0x006d, + [EEPROM_TSSI_BOUND_A5] = 0x006e, + [EEPROM_TXPOWER_BYRATE] = 0x006f, + [EEPROM_BBP_START] = 0x0078, +}; + +static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = { + [EEPROM_CHIP_ID] = 0x0000, + [EEPROM_VERSION] = 0x0001, + [EEPROM_MAC_ADDR_0] = 0x0002, + [EEPROM_MAC_ADDR_1] = 0x0003, + [EEPROM_MAC_ADDR_2] = 0x0004, + [EEPROM_NIC_CONF0] = 0x001a, + [EEPROM_NIC_CONF1] = 0x001b, + [EEPROM_NIC_CONF2] = 0x001c, + [EEPROM_EIRP_MAX_TX_POWER] = 0x0020, + [EEPROM_FREQ] = 0x0022, + [EEPROM_LED_AG_CONF] = 0x0023, + [EEPROM_LED_ACT_CONF] = 0x0024, + [EEPROM_LED_POLARITY] = 0x0025, + [EEPROM_LNA] = 0x0026, + [EEPROM_EXT_LNA2] = 0x0027, + [EEPROM_RSSI_BG] = 0x0028, + [EEPROM_TXPOWER_DELTA] = 0x0028, /* Overlaps with RSSI_BG */ + [EEPROM_RSSI_BG2] = 0x0029, + [EEPROM_TXMIXER_GAIN_BG] = 0x0029, /* Overlaps with RSSI_BG2 */ + [EEPROM_RSSI_A] = 0x002a, + [EEPROM_RSSI_A2] = 0x002b, + [EEPROM_TXMIXER_GAIN_A] = 0x002b, /* Overlaps with RSSI_A2 */ + [EEPROM_TXPOWER_BG1] = 0x0030, + [EEPROM_TXPOWER_BG2] = 0x0037, + [EEPROM_EXT_TXPOWER_BG3] = 0x003e, + [EEPROM_TSSI_BOUND_BG1] = 0x0045, + [EEPROM_TSSI_BOUND_BG2] = 0x0046, + [EEPROM_TSSI_BOUND_BG3] = 0x0047, + [EEPROM_TSSI_BOUND_BG4] = 0x0048, + [EEPROM_TSSI_BOUND_BG5] = 0x0049, + [EEPROM_TXPOWER_A1] = 0x004b, + [EEPROM_TXPOWER_A2] = 0x0065, + [EEPROM_EXT_TXPOWER_A3] = 0x007f, + [EEPROM_TSSI_BOUND_A1] = 0x009a, + [EEPROM_TSSI_BOUND_A2] = 0x009b, + [EEPROM_TSSI_BOUND_A3] = 0x009c, + [EEPROM_TSSI_BOUND_A4] = 0x009d, + [EEPROM_TSSI_BOUND_A5] = 0x009e, + [EEPROM_TXPOWER_BYRATE] = 0x00a0, +}; + +static unsigned int rt2800_eeprom_word_index(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word) +{ + const unsigned int *map; + unsigned int index; + + if (WARN_ONCE(word >= EEPROM_WORD_COUNT, + "%s: invalid EEPROM word %d\n", + wiphy_name(rt2x00dev->hw->wiphy), word)) + return 0; + + if (rt2x00_rt(rt2x00dev, RT3593)) + map = rt2800_eeprom_map_ext; + else + map = rt2800_eeprom_map; + + index = map[word]; + + /* Index 0 is valid only for EEPROM_CHIP_ID. + * Otherwise it means that the offset of the + * given word is not initialized in the map, + * or that the field is not usable on the + * actual chipset. + */ + WARN_ONCE(word != EEPROM_CHIP_ID && index == 0, + "%s: invalid access of EEPROM word %d\n", + wiphy_name(rt2x00dev->hw->wiphy), word); + + return index; +} + +static void *rt2800_eeprom_addr(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, word); + return rt2x00_eeprom_addr(rt2x00dev, index); +} + +static void rt2800_eeprom_read(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word, u16 *data) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, word); + rt2x00_eeprom_read(rt2x00dev, index, data); +} + +static void rt2800_eeprom_write(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word, u16 data) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, word); + rt2x00_eeprom_write(rt2x00dev, index, data); +} + +static void rt2800_eeprom_read_from_array(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word array, + unsigned int offset, + u16 *data) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, array); + rt2x00_eeprom_read(rt2x00dev, index + offset, data); +} + static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -370,6 +521,29 @@ void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev) } EXPORT_SYMBOL_GPL(rt2800_disable_wpdma); +void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev, + unsigned short *txwi_size, + unsigned short *rxwi_size) +{ + switch (rt2x00dev->chip.rt) { + case RT3593: + *txwi_size = TXWI_DESC_SIZE_4WORDS; + *rxwi_size = RXWI_DESC_SIZE_5WORDS; + break; + + case RT5592: + *txwi_size = TXWI_DESC_SIZE_5WORDS; + *rxwi_size = RXWI_DESC_SIZE_6WORDS; + break; + + default: + *txwi_size = TXWI_DESC_SIZE_4WORDS; + *rxwi_size = RXWI_DESC_SIZE_4WORDS; + break; + } +} +EXPORT_SYMBOL_GPL(rt2800_get_txwi_rxwi_size); + static bool rt2800_check_firmware_crc(const u8 *data, const size_t len) { u16 fw_crc; @@ -609,16 +783,16 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) u8 offset2; if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom); offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0); offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1); - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_OFFSET2); } else { - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom); offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET0); offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET1); - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_OFFSET2); } @@ -766,6 +940,18 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) } EXPORT_SYMBOL_GPL(rt2800_txdone_entry); +static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev, + unsigned int index) +{ + return HW_BEACON_BASE(index); +} + +static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev, + unsigned int index) +{ + return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index)); +} + void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; @@ -818,7 +1004,8 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) return; } - beacon_base = HW_BEACON_OFFSET(entry->entry_idx); + beacon_base = rt2800_hw_beacon_base(rt2x00dev, entry->entry_idx); + rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, entry->skb->len + padding_len); @@ -837,10 +1024,13 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) EXPORT_SYMBOL_GPL(rt2800_write_beacon); static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev, - unsigned int beacon_base) + unsigned int index) { int i; const int txwi_desc_size = rt2x00dev->bcn->winfo_size; + unsigned int beacon_base; + + beacon_base = rt2800_hw_beacon_base(rt2x00dev, index); /* * For the Beacon base registers we only need to clear @@ -867,8 +1057,7 @@ void rt2800_clear_beacon(struct queue_entry *entry) /* * Clear beacon. */ - rt2800_clear_beacon_register(rt2x00dev, - HW_BEACON_OFFSET(entry->entry_idx)); + rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx); /* * Enabled beaconing again. @@ -890,6 +1079,9 @@ const struct rt2x00debug rt2800_rt2x00debug = { .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { + /* NOTE: The local EEPROM access functions can't + * be used here, use the generic versions instead. + */ .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, .word_base = EEPROM_BASE, @@ -1547,7 +1739,7 @@ static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev) led_r_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 0 : 3; if (led_g_mode != rt2x00_get_field32(reg, LED_CFG_G_LED_MODE) || led_r_mode != rt2x00_get_field32(reg, LED_CFG_R_LED_MODE)) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); led_ctrl = rt2x00_get_field16(eeprom, EEPROM_FREQ_LED_MODE); if (led_ctrl == 0 || led_ctrl > 0x40) { rt2x00_set_field32(®, LED_CFG_G_LED_MODE, led_g_mode); @@ -1609,7 +1801,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); break; case 3: - rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0); + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); break; } @@ -1622,7 +1814,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT3390)) { - rt2x00_eeprom_read(rt2x00dev, + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY)) @@ -1649,6 +1841,13 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) rt2800_bbp_write(rt2x00dev, 3, r3); rt2800_bbp_write(rt2x00dev, 1, r1); + + if (rt2x00_rt(rt2x00dev, RT3593)) { + if (ant->rx_chain_num == 1) + rt2800_bbp_write(rt2x00dev, 86, 0x00); + else + rt2800_bbp_write(rt2x00dev, 86, 0x46); + } } EXPORT_SYMBOL_GPL(rt2800_config_ant); @@ -1659,22 +1858,73 @@ static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev, short lna_gain; if (libconf->rf.channel <= 14) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG); } else if (libconf->rf.channel <= 64) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0); } else if (libconf->rf.channel <= 128) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_LNA_A1); + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_EXT_LNA2_A1); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_RSSI_BG2_LNA_A1); + } } else { - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_LNA_A2); + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_EXT_LNA2_A2); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_RSSI_A2_LNA_A2); + } } rt2x00dev->lna_gain = lna_gain; } +#define FREQ_OFFSET_BOUND 0x5f + +static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev) +{ + u8 freq_offset, prev_freq_offset; + u8 rfcsr, prev_rfcsr; + + freq_offset = rt2x00_get_field8(rt2x00dev->freq_offset, RFCSR17_CODE); + freq_offset = min_t(u8, freq_offset, FREQ_OFFSET_BOUND); + + rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); + prev_rfcsr = rfcsr; + + rt2x00_set_field8(&rfcsr, RFCSR17_CODE, freq_offset); + if (rfcsr == prev_rfcsr) + return; + + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_mcu_request(rt2x00dev, MCU_FREQ_OFFSET, 0xff, + freq_offset, prev_rfcsr); + return; + } + + prev_freq_offset = rt2x00_get_field8(prev_rfcsr, RFCSR17_CODE); + while (prev_freq_offset != freq_offset) { + if (prev_freq_offset < freq_offset) + prev_freq_offset++; + else + prev_freq_offset--; + + rt2x00_set_field8(&rfcsr, RFCSR17_CODE, prev_freq_offset); + rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); + + usleep_range(1000, 1500); + } +} + static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, @@ -1993,22 +2243,306 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev, rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); } -#define POWER_BOUND 0x27 -#define POWER_BOUND_5G 0x2b -#define FREQ_OFFSET_BOUND 0x5f - -static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev) +static void rt2800_config_channel_rf3053(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) { + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 txrx_agc_fc; + u8 txrx_h20m; u8 rfcsr; + u8 bbp; + const bool txbf_enabled = false; /* TODO */ - rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); - if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND) - rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND); + /* TODO: use TX{0,1,2}FinePowerControl values from EEPROM */ + rt2800_bbp_read(rt2x00dev, 109, &bbp); + rt2x00_set_field8(&bbp, BBP109_TX0_POWER, 0); + rt2x00_set_field8(&bbp, BBP109_TX1_POWER, 0); + rt2800_bbp_write(rt2x00dev, 109, bbp); + + rt2800_bbp_read(rt2x00dev, 110, &bbp); + rt2x00_set_field8(&bbp, BBP110_TX2_POWER, 0); + rt2800_bbp_write(rt2x00dev, 110, bbp); + + if (rf->channel <= 14) { + /* Restore BBP 25 & 26 for 2.4 GHz */ + rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25); + rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26); + } else { + /* Hard code BBP 25 & 26 for 5GHz */ + + /* Enable IQ Phase correction */ + rt2800_bbp_write(rt2x00dev, 25, 0x09); + /* Setup IQ Phase correction value */ + rt2800_bbp_write(rt2x00dev, 26, 0xff); + } + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3 & 0xf); + + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, (rf->rf2 & 0x3)); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_PLL_IDOH, 1); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 1); else - rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset); - rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 2); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 53, &rfcsr); + if (rf->channel <= 14) { + rfcsr = 0; + rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER, + info->default_power1 & 0x1f); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rfcsr = 0x40; + + rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER, + ((info->default_power1 & 0x18) << 1) | + (info->default_power1 & 7)); + } + rt2800_rfcsr_write(rt2x00dev, 53, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 55, &rfcsr); + if (rf->channel <= 14) { + rfcsr = 0; + rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER, + info->default_power2 & 0x1f); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rfcsr = 0x40; + + rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER, + ((info->default_power2 & 0x18) << 1) | + (info->default_power2 & 7)); + } + rt2800_rfcsr_write(rt2x00dev, 55, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 54, &rfcsr); + if (rf->channel <= 14) { + rfcsr = 0; + rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER, + info->default_power3 & 0x1f); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rfcsr = 0x40; + + rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER, + ((info->default_power3 & 0x18) << 1) | + (info->default_power3 & 7)); + } + rt2800_rfcsr_write(rt2x00dev, 54, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); + /* fallthrough */ + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + /* fallthrough */ + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); + break; + } + + switch (rt2x00dev->default_ant.rx_chain_num) { + case 3: + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); + /* fallthrough */ + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + /* fallthrough */ + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); + break; + } + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + if (conf_is_ht40(conf)) { + txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw40, + RFCSR24_TX_AGC_FC); + txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw40, + RFCSR24_TX_H20M); + } else { + txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw20, + RFCSR24_TX_AGC_FC); + txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw20, + RFCSR24_TX_H20M); + } + + /* NOTE: the reference driver does not writes the new value + * back to RFCSR 32 + */ + rt2800_rfcsr_read(rt2x00dev, 32, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR32_TX_AGC_FC, txrx_agc_fc); + + if (rf->channel <= 14) + rfcsr = 0xa0; + else + rfcsr = 0x80; + rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, txrx_h20m); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, txrx_h20m); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + /* Band selection */ + rt2800_rfcsr_read(rt2x00dev, 36, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 0); + rt2800_rfcsr_write(rt2x00dev, 36, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 34, &rfcsr); + if (rf->channel <= 14) + rfcsr = 0x3c; + else + rfcsr = 0x20; + rt2800_rfcsr_write(rt2x00dev, 34, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); + if (rf->channel <= 14) + rfcsr = 0x1a; + else + rfcsr = 0x12; + rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + if (rf->channel >= 1 && rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1); + else if (rf->channel >= 36 && rf->channel <= 64) + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2); + else if (rf->channel >= 100 && rf->channel <= 128) + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2); + else + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 46, 0x60); + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 10, 0xd3); + rt2800_rfcsr_write(rt2x00dev, 13, 0x12); + } else { + rt2800_rfcsr_write(rt2x00dev, 10, 0xd8); + rt2800_rfcsr_write(rt2x00dev, 13, 0x23); + } + + rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS01, 1); + rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 5); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 3); + } else { + rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 4); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 2); + } + rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 3); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 2); + + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR49_TX_DIV, 1); + + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 57, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x1b); + else + rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 57, rfcsr); + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 44, 0x93); + rt2800_rfcsr_write(rt2x00dev, 52, 0x45); + } else { + rt2800_rfcsr_write(rt2x00dev, 44, 0x9b); + rt2800_rfcsr_write(rt2x00dev, 52, 0x05); + } + + /* Initiate VCO calibration */ + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + } else { + rt2x00_set_field8(&rfcsr, RFCSR3_BIT1, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT2, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT3, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT4, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT5, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + } + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + + if (rf->channel >= 1 && rf->channel <= 14) { + rfcsr = 0x23; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 45, 0xbb); + } else if (rf->channel >= 36 && rf->channel <= 64) { + rfcsr = 0x36; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, 0x36); + + rt2800_rfcsr_write(rt2x00dev, 45, 0xeb); + } else if (rf->channel >= 100 && rf->channel <= 128) { + rfcsr = 0x32; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 45, 0xb3); + } else { + rfcsr = 0x30; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 45, 0x9b); + } } +#define POWER_BOUND 0x27 +#define POWER_BOUND_5G 0x2b + static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, @@ -2563,6 +3097,23 @@ static void rt2800_iq_calibrate(struct rt2x00_dev *rt2x00dev, int channel) rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0); } +static char rt2800_txpower_to_dev(struct rt2x00_dev *rt2x00dev, + unsigned int channel, + char txpower) +{ + if (rt2x00_rt(rt2x00dev, RT3593)) + txpower = rt2x00_get_field8(txpower, EEPROM_TXPOWER_ALC); + + if (channel <= 14) + return clamp_t(char, txpower, MIN_G_TXPOWER, MAX_G_TXPOWER); + + if (rt2x00_rt(rt2x00dev, RT3593)) + return clamp_t(char, txpower, MIN_A_TXPOWER_3593, + MAX_A_TXPOWER_3593); + else + return clamp_t(char, txpower, MIN_A_TXPOWER, MAX_A_TXPOWER); +} + static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, @@ -2572,13 +3123,14 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, unsigned int tx_pin; u8 bbp, rfcsr; - if (rf->channel <= 14) { - info->default_power1 = TXPOWER_G_TO_DEV(info->default_power1); - info->default_power2 = TXPOWER_G_TO_DEV(info->default_power2); - } else { - info->default_power1 = TXPOWER_A_TO_DEV(info->default_power1); - info->default_power2 = TXPOWER_A_TO_DEV(info->default_power2); - } + info->default_power1 = rt2800_txpower_to_dev(rt2x00dev, rf->channel, + info->default_power1); + info->default_power2 = rt2800_txpower_to_dev(rt2x00dev, rf->channel, + info->default_power2); + if (rt2x00dev->default_ant.tx_chain_num > 2) + info->default_power3 = + rt2800_txpower_to_dev(rt2x00dev, rf->channel, + info->default_power3); switch (rt2x00dev->chip.rf) { case RF2020: @@ -2591,6 +3143,9 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, case RF3052: rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info); break; + case RF3053: + rt2800_config_channel_rf3053(rt2x00dev, conf, rf, info); + break; case RF3290: rt2800_config_channel_rf3290(rt2x00dev, conf, rf, info); break; @@ -2636,6 +3191,23 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); rt2800_bbp_write(rt2x00dev, 27, 0x20); rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); + } else if (rt2x00_rt(rt2x00dev, RT3593)) { + if (rf->channel > 14) { + /* Disable CCK Packet detection on 5GHz */ + rt2800_bbp_write(rt2x00dev, 70, 0x00); + } else { + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + } + + if (conf_is_ht40(conf)) + rt2800_bbp_write(rt2x00dev, 105, 0x04); + else + rt2800_bbp_write(rt2x00dev, 105, 0x34); + + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 77, 0x98); } else { rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); @@ -2651,16 +3223,27 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 75, 0x46); } else { - rt2800_bbp_write(rt2x00dev, 82, 0x84); + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 82, 0x62); + else + rt2800_bbp_write(rt2x00dev, 82, 0x84); rt2800_bbp_write(rt2x00dev, 75, 0x50); } + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 83, 0x8a); } + } else { if (rt2x00_rt(rt2x00dev, RT3572)) rt2800_bbp_write(rt2x00dev, 82, 0x94); + else if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 82, 0x82); else rt2800_bbp_write(rt2x00dev, 82, 0xf2); + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 83, 0x9a); + if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) rt2800_bbp_write(rt2x00dev, 75, 0x46); else @@ -2731,6 +3314,41 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, if (rt2x00_rt(rt2x00dev, RT3572)) rt2800_rfcsr_write(rt2x00dev, 8, 0x80); + if (rt2x00_rt(rt2x00dev, RT3593)) { + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + + /* Band selection. GPIO #8 controls all paths */ + rt2x00_set_field32(®, GPIO_CTRL_DIR8, 0); + if (rf->channel <= 14) + rt2x00_set_field32(®, GPIO_CTRL_VAL8, 1); + else + rt2x00_set_field32(®, GPIO_CTRL_VAL8, 0); + + rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); + rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); + + /* LNA PE control. + * GPIO #4 controls PE0 and PE1, + * GPIO #7 controls PE2 + */ + rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); + rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); + + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + } + + /* AGC init */ + if (rf->channel <= 14) + reg = 0x1c + 2 * rt2x00dev->lna_gain; + else + reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3); + + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + + usleep_range(1000, 1500); + } + if (rt2x00_rt(rt2x00dev, RT5592)) { rt2800_bbp_write(rt2x00dev, 195, 141); rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a); @@ -2790,6 +3408,13 @@ static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev) int i; /* + * First check if temperature compensation is supported. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + if (!rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC)) + return 0; + + /* * Read TSSI boundaries for temperature compensation from * the EEPROM. * @@ -2798,62 +3423,62 @@ static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev) * Example TSSI bounds 0xF0 0xD0 0xB5 0xA0 0x88 0x45 0x25 0x15 0x00 */ if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom); tssi_bounds[0] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG1_MINUS4); tssi_bounds[1] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG1_MINUS3); - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom); tssi_bounds[2] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG2_MINUS2); tssi_bounds[3] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG2_MINUS1); - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom); tssi_bounds[4] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG3_REF); tssi_bounds[5] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG3_PLUS1); - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom); tssi_bounds[6] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG4_PLUS2); tssi_bounds[7] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG4_PLUS3); - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom); tssi_bounds[8] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG5_PLUS4); step = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_BG5_AGC_STEP); } else { - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom); tssi_bounds[0] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A1_MINUS4); tssi_bounds[1] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A1_MINUS3); - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom); tssi_bounds[2] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A2_MINUS2); tssi_bounds[3] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A2_MINUS1); - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom); tssi_bounds[4] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A3_REF); tssi_bounds[5] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A3_PLUS1); - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom); tssi_bounds[6] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A4_PLUS2); tssi_bounds[7] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A4_PLUS3); - rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom); tssi_bounds[8] = rt2x00_get_field16(eeprom, EEPROM_TSSI_BOUND_A5_PLUS4); @@ -2899,7 +3524,7 @@ static int rt2800_get_txpower_bw_comp(struct rt2x00_dev *rt2x00dev, u8 comp_type; int comp_value = 0; - rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom); /* * HT40 compensation not required. @@ -2966,6 +3591,9 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, u8 eirp_txpower_criterion; u8 reg_limit; + if (rt2x00_rt(rt2x00dev, RT3593)) + return min_t(u8, txpower, 0xc); + if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) { /* * Check if eirp txpower exceed txpower_limit. @@ -2974,12 +3602,12 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, * .11b data rate need add additional 4dbm * when calculating eirp txpower. */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + 1, - &eeprom); + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + 1, &eeprom); criterion = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); - rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, + rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom); if (band == IEEE80211_BAND_2GHZ) @@ -3001,6 +3629,412 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, return min_t(u8, txpower, 0xc); } + +enum { + TX_PWR_CFG_0_IDX, + TX_PWR_CFG_1_IDX, + TX_PWR_CFG_2_IDX, + TX_PWR_CFG_3_IDX, + TX_PWR_CFG_4_IDX, + TX_PWR_CFG_5_IDX, + TX_PWR_CFG_6_IDX, + TX_PWR_CFG_7_IDX, + TX_PWR_CFG_8_IDX, + TX_PWR_CFG_9_IDX, + TX_PWR_CFG_0_EXT_IDX, + TX_PWR_CFG_1_EXT_IDX, + TX_PWR_CFG_2_EXT_IDX, + TX_PWR_CFG_3_EXT_IDX, + TX_PWR_CFG_4_EXT_IDX, + TX_PWR_CFG_IDX_COUNT, +}; + +static void rt2800_config_txpower_rt3593(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) +{ + u8 txpower; + u16 eeprom; + u32 regs[TX_PWR_CFG_IDX_COUNT]; + unsigned int offset; + enum ieee80211_band band = chan->band; + int delta; + int i; + + memset(regs, '\0', sizeof(regs)); + + /* TODO: adapt TX power reduction from the rt28xx code */ + + /* calculate temperature compensation delta */ + delta = rt2800_get_gain_calibration_delta(rt2x00dev); + + if (band == IEEE80211_BAND_5GHZ) + offset = 16; + else + offset = 0; + + if (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) + offset += 8; + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset, &eeprom); + + /* CCK 1MBS,2MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK1_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK1_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_CCK1_CH2, txpower); + + /* CCK 5.5MBS,11MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK5_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK5_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_CCK5_CH2, txpower); + + /* OFDM 6MBS,9MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM6_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM6_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_OFDM6_CH2, txpower); + + /* OFDM 12MBS,18MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM12_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM12_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_OFDM12_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 1, &eeprom); + + /* OFDM 24MBS,36MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM24_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM24_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_OFDM24_CH2, txpower); + + /* OFDM 48MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM48_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM48_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_OFDM48_CH2, txpower); + + /* OFDM 54MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_OFDM54_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_OFDM54_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_OFDM54_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 2, &eeprom); + + /* MCS 0,1 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS0_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS0_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_MCS0_CH2, txpower); + + /* MCS 2,3 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS2_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS2_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_MCS2_CH2, txpower); + + /* MCS 4,5 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS4_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS4_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS4_CH2, txpower); + + /* MCS 6 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS6_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS6_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS6_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 3, &eeprom); + + /* MCS 7 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_MCS7_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_MCS7_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_MCS7_CH2, txpower); + + /* MCS 8,9 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS8_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS8_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS8_CH2, txpower); + + /* MCS 10,11 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS10_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS10_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS10_CH2, txpower); + + /* MCS 12,13 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS12_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS12_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_MCS12_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 4, &eeprom); + + /* MCS 14 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS14_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS14_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_MCS14_CH2, txpower); + + /* MCS 15 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS15_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS15_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS15_CH2, txpower); + + /* MCS 16,17 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS16_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS16_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS16_CH2, txpower); + + /* MCS 18,19 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS18_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS18_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS18_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 5, &eeprom); + + /* MCS 20,21 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS20_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS20_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS20_CH2, txpower); + + /* MCS 22 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS22_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS22_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS22_CH2, txpower); + + /* MCS 23 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS23_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS23_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS23_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 6, &eeprom); + + /* STBC, MCS 0,1 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC0_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC0_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_STBC0_CH2, txpower); + + /* STBC, MCS 2,3 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC2_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC2_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_STBC2_CH2, txpower); + + /* STBC, MCS 4,5 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE0, + txpower); + + /* STBC, MCS 6 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE2, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE3, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE2, + txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 7, &eeprom); + + /* STBC, MCS 7 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], + TX_PWR_CFG_9_STBC7_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], + TX_PWR_CFG_9_STBC7_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], + TX_PWR_CFG_9_STBC7_CH2, txpower); + + rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, regs[TX_PWR_CFG_0_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, regs[TX_PWR_CFG_1_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, regs[TX_PWR_CFG_2_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, regs[TX_PWR_CFG_3_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, regs[TX_PWR_CFG_4_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_5, regs[TX_PWR_CFG_5_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_6, regs[TX_PWR_CFG_6_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_7, regs[TX_PWR_CFG_7_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_8, regs[TX_PWR_CFG_8_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_9, regs[TX_PWR_CFG_9_IDX]); + + rt2800_register_write(rt2x00dev, TX_PWR_CFG_0_EXT, + regs[TX_PWR_CFG_0_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_1_EXT, + regs[TX_PWR_CFG_1_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_2_EXT, + regs[TX_PWR_CFG_2_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_3_EXT, + regs[TX_PWR_CFG_3_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_4_EXT, + regs[TX_PWR_CFG_4_EXT_IDX]); + + for (i = 0; i < TX_PWR_CFG_IDX_COUNT; i++) + rt2x00_dbg(rt2x00dev, + "band:%cGHz, BW:%c0MHz, TX_PWR_CFG_%d%s = %08lx\n", + (band == IEEE80211_BAND_5GHZ) ? '5' : '2', + (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) ? + '4' : '2', + (i > TX_PWR_CFG_9_IDX) ? + (i - TX_PWR_CFG_9_IDX - 1) : i, + (i > TX_PWR_CFG_9_IDX) ? "_EXT" : "", + (unsigned long) regs[i]); +} + /* * We configure transmit power using MAC TX_PWR_CFG_{0,...,N} registers and * BBP R1 register. TX_PWR_CFG_X allow to configure per rate TX power values, @@ -3010,9 +4044,9 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, * EEPROM_TXPOWER_BYRATE offset. We adjust them and BBP R1 settings according to * current conditions (i.e. band, bandwidth, temperature, user settings). */ -static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, - struct ieee80211_channel *chan, - int power_level) +static void rt2800_config_txpower_rt28xx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) { u8 txpower, r1; u16 eeprom; @@ -3080,8 +4114,8 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, rt2800_register_read(rt2x00dev, offset, ®); /* read the next four txpower values */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i, - &eeprom); + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + i, &eeprom); is_rate_b = i ? 0 : 1; /* @@ -3129,8 +4163,8 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(®, TX_PWR_CFG_RATE3, txpower); /* read the next four txpower values */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i + 1, - &eeprom); + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + i + 1, &eeprom); is_rate_b = 0; /* @@ -3184,6 +4218,16 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, } } +static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) +{ + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_config_txpower_rt3593(rt2x00dev, chan, power_level); + else + rt2800_config_txpower_rt28xx(rt2x00dev, chan, power_level); +} + void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev) { rt2800_config_txpower(rt2x00dev, rt2x00dev->hw->conf.chandef.chan, @@ -3219,6 +4263,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); break; + case RF3053: case RF3290: case RF5360: case RF5370: @@ -3442,17 +4487,25 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) return ret; rt2800_register_read(rt2x00dev, BCN_OFFSET0, ®); - rt2x00_set_field32(®, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */ - rt2x00_set_field32(®, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */ - rt2x00_set_field32(®, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */ - rt2x00_set_field32(®, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */ + rt2x00_set_field32(®, BCN_OFFSET0_BCN0, + rt2800_get_beacon_offset(rt2x00dev, 0)); + rt2x00_set_field32(®, BCN_OFFSET0_BCN1, + rt2800_get_beacon_offset(rt2x00dev, 1)); + rt2x00_set_field32(®, BCN_OFFSET0_BCN2, + rt2800_get_beacon_offset(rt2x00dev, 2)); + rt2x00_set_field32(®, BCN_OFFSET0_BCN3, + rt2800_get_beacon_offset(rt2x00dev, 3)); rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg); rt2800_register_read(rt2x00dev, BCN_OFFSET1, ®); - rt2x00_set_field32(®, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */ - rt2x00_set_field32(®, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */ - rt2x00_set_field32(®, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */ - rt2x00_set_field32(®, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */ + rt2x00_set_field32(®, BCN_OFFSET1_BCN4, + rt2800_get_beacon_offset(rt2x00dev, 4)); + rt2x00_set_field32(®, BCN_OFFSET1_BCN5, + rt2800_get_beacon_offset(rt2x00dev, 5)); + rt2x00_set_field32(®, BCN_OFFSET1_BCN6, + rt2800_get_beacon_offset(rt2x00dev, 6)); + rt2x00_set_field32(®, BCN_OFFSET1_BCN7, + rt2800_get_beacon_offset(rt2x00dev, 7)); rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg); rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f); @@ -3528,7 +4581,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, + &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x0000002c); @@ -3559,6 +4613,23 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) } else if (rt2x00_rt(rt2x00dev, RT3572)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + } else if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + if (rt2x00_rt_rev_lt(rt2x00dev, RT3593, REV_RT3593E)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, + &eeprom); + if (rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_DAC_TEST)) + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000001f); + else + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000000f); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x00000000); + } } else if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392) || rt2x00_rt(rt2x00dev, RT5592)) { @@ -3786,14 +4857,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) /* * Clear all beacons */ - rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE0); - rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE1); - rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE2); - rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE3); - rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE4); - rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE5); - rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE6); - rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE7); + for (i = 0; i < 8; i++) + rt2800_clear_beacon_register(rt2x00dev, i); if (rt2x00_is_usb(rt2x00dev)) { rt2800_register_read(rt2x00dev, US_CYC_CNT, ®); @@ -3989,7 +5054,7 @@ static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev) u8 value; rt2800_bbp_read(rt2x00dev, 138, &value); - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) value |= 0x20; if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) @@ -4332,6 +5397,22 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_disable_unused_dac_adc(rt2x00dev); } +static void rt2800_init_bbp_3593(struct rt2x00_dev *rt2x00dev) +{ + rt2800_init_bbp_early(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + rt2800_bbp_write(rt2x00dev, 137, 0x0f); + + rt2800_bbp_write(rt2x00dev, 84, 0x19); + + /* Enable DC filter */ + if (rt2x00_rt_rev_gte(rt2x00dev, RT3593, REV_RT3593E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); +} + static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) { int ant, div_mode; @@ -4402,7 +5483,7 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_disable_unused_dac_adc(rt2x00dev); - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY); ant = (div_mode == 3) ? 1 : 0; @@ -4488,7 +5569,7 @@ static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) rt2800_bbp4_mac_if_ctrl(rt2x00dev); - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY); ant = (div_mode == 3) ? 1 : 0; rt2800_bbp_read(rt2x00dev, 152, &value); @@ -4547,6 +5628,9 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) case RT3572: rt2800_init_bbp_3572(rt2x00dev); break; + case RT3593: + rt2800_init_bbp_3593(rt2x00dev); + return; case RT5390: case RT5392: rt2800_init_bbp_53xx(rt2x00dev); @@ -4557,7 +5641,8 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) } for (i = 0; i < EEPROM_BBP_SIZE; i++) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_BBP_START, i, + &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); @@ -4728,7 +5813,7 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev) if (rt2x00_rt(rt2x00dev, RT3090)) { /* Turn off unused DAC1 and ADC1 to reduce power consumption */ rt2800_bbp_read(rt2x00dev, 138, &bbp); - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) rt2x00_set_field8(&bbp, BBP138_RX_ADC1, 0); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) @@ -4771,6 +5856,42 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev) } } +static void rt2800_normal_mode_setup_3593(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 rfcsr; + u8 tx_gain; + + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); + tx_gain = rt2x00_get_field8(drv_data->txmixer_gain_24g, + RFCSR17_TXMIXER_GAIN); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, tx_gain); + rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR38_RX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 38, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 39, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR39_RX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + /* TODO: enable stream mode */ +} + static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev) { u8 reg; @@ -4778,7 +5899,7 @@ static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev) /* Turn off unused DAC1 and ADC1 to reduce power consumption */ rt2800_bbp_read(rt2x00dev, 138, ®); - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) rt2x00_set_field8(®, BBP138_RX_ADC1, 0); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) @@ -4884,7 +6005,8 @@ static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, + &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); else @@ -5152,6 +6274,136 @@ static void rt2800_init_rfcsr_3572(struct rt2x00_dev *rt2x00dev) rt2800_normal_mode_setup_3xxx(rt2x00dev); } +static void rt3593_post_bbp_init(struct rt2x00_dev *rt2x00dev) +{ + u8 bbp; + bool txbf_enabled = false; /* FIXME */ + + rt2800_bbp_read(rt2x00dev, 105, &bbp); + if (rt2x00dev->default_ant.rx_chain_num == 1) + rt2x00_set_field8(&bbp, BBP105_MLD, 0); + else + rt2x00_set_field8(&bbp, BBP105_MLD, 1); + rt2800_bbp_write(rt2x00dev, 105, bbp); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + rt2800_bbp_write(rt2x00dev, 82, 0x82); + rt2800_bbp_write(rt2x00dev, 106, 0x05); + rt2800_bbp_write(rt2x00dev, 104, 0x92); + rt2800_bbp_write(rt2x00dev, 88, 0x90); + rt2800_bbp_write(rt2x00dev, 148, 0xc8); + rt2800_bbp_write(rt2x00dev, 47, 0x48); + rt2800_bbp_write(rt2x00dev, 120, 0x50); + + if (txbf_enabled) + rt2800_bbp_write(rt2x00dev, 163, 0xbd); + else + rt2800_bbp_write(rt2x00dev, 163, 0x9d); + + /* SNR mapping */ + rt2800_bbp_write(rt2x00dev, 142, 6); + rt2800_bbp_write(rt2x00dev, 143, 160); + rt2800_bbp_write(rt2x00dev, 142, 7); + rt2800_bbp_write(rt2x00dev, 143, 161); + rt2800_bbp_write(rt2x00dev, 142, 8); + rt2800_bbp_write(rt2x00dev, 143, 162); + + /* ADC/DAC control */ + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + /* RX AGC energy lower bound in log2 */ + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + /* FIXME: BBP 105 owerwrite? */ + rt2800_bbp_write(rt2x00dev, 105, 0x04); + +} + +static void rt2800_init_rfcsr_3593(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u32 reg; + u8 rfcsr; + + /* Disable GPIO #4 and #7 function for LAN PE control */ + rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); + rt2x00_set_field32(®, GPIO_SWITCH_4, 0); + rt2x00_set_field32(®, GPIO_SWITCH_7, 0); + rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); + + /* Initialize default register values */ + rt2800_rfcsr_write(rt2x00dev, 1, 0x03); + rt2800_rfcsr_write(rt2x00dev, 3, 0x80); + rt2800_rfcsr_write(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0x40); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0xd3); + rt2800_rfcsr_write(rt2x00dev, 11, 0x40); + rt2800_rfcsr_write(rt2x00dev, 12, 0x4e); + rt2800_rfcsr_write(rt2x00dev, 13, 0x12); + rt2800_rfcsr_write(rt2x00dev, 18, 0x40); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x78); + rt2800_rfcsr_write(rt2x00dev, 33, 0x3b); + rt2800_rfcsr_write(rt2x00dev, 34, 0x3c); + rt2800_rfcsr_write(rt2x00dev, 35, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 38, 0x86); + rt2800_rfcsr_write(rt2x00dev, 39, 0x23); + rt2800_rfcsr_write(rt2x00dev, 44, 0xd3); + rt2800_rfcsr_write(rt2x00dev, 45, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 46, 0x60); + rt2800_rfcsr_write(rt2x00dev, 49, 0x8e); + rt2800_rfcsr_write(rt2x00dev, 50, 0x86); + rt2800_rfcsr_write(rt2x00dev, 51, 0x75); + rt2800_rfcsr_write(rt2x00dev, 52, 0x45); + rt2800_rfcsr_write(rt2x00dev, 53, 0x18); + rt2800_rfcsr_write(rt2x00dev, 54, 0x18); + rt2800_rfcsr_write(rt2x00dev, 55, 0x18); + rt2800_rfcsr_write(rt2x00dev, 56, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 57, 0x6e); + + /* Initiate calibration */ + /* TODO: use rt2800_rf_init_calibration ? */ + rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 2, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + rt2800_rfcsr_read(rt2x00dev, 18, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR18_XO_TUNE_BYPASS, 1); + rt2800_rfcsr_write(rt2x00dev, 18, rfcsr); + + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + usleep_range(1000, 1500); + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + + /* Set initial values for RX filter calibration */ + drv_data->calibration_bw20 = 0x1f; + drv_data->calibration_bw40 = 0x2f; + + /* Save BBP 25 & 26 values for later use in channel switching */ + rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25); + rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26); + + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3593(rt2x00dev); + + rt3593_post_bbp_init(rt2x00dev); + + /* TODO: enable stream mode support */ +} + static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) { rt2800_rf_init_calibration(rt2x00dev, 2); @@ -5380,6 +6632,9 @@ static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) case RT3572: rt2800_init_rfcsr_3572(rt2x00dev); break; + case RT3593: + rt2800_init_rfcsr_3593(rt2x00dev); + break; case RT5390: rt2800_init_rfcsr_5390(rt2x00dev); break; @@ -5456,15 +6711,15 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) /* * Initialize LED control */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word); rt2800_mcu_request(rt2x00dev, MCU_LED_AG_CONF, 0xff, word & 0xff, (word >> 8) & 0xff); - rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word); rt2800_mcu_request(rt2x00dev, MCU_LED_ACT_CONF, 0xff, word & 0xff, (word >> 8) & 0xff); - rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word); rt2800_mcu_request(rt2x00dev, MCU_LED_LED_POLARITY, 0xff, word & 0xff, (word >> 8) & 0xff); @@ -5560,6 +6815,34 @@ int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) } EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse); +static u8 rt2800_get_txmixer_gain_24g(struct rt2x00_dev *rt2x00dev) +{ + u16 word; + + if (rt2x00_rt(rt2x00dev, RT3593)) + return 0; + + rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word); + if ((word & 0x00ff) != 0x00ff) + return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL); + + return 0; +} + +static u8 rt2800_get_txmixer_gain_5g(struct rt2x00_dev *rt2x00dev) +{ + u16 word; + + if (rt2x00_rt(rt2x00dev, RT3593)) + return 0; + + rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word); + if ((word & 0x00ff) != 0x00ff) + return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL); + + return 0; +} + static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) { struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; @@ -5578,18 +6861,18 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) /* * Start validation of the data that has been read. */ - mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + mac = rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { eth_random_addr(mac); rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); } - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); rt2x00_set_field16(&word, EEPROM_NIC_CONF0_TXPATH, 1); rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RF_TYPE, RF2820); - rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); + rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); } else if (rt2x00_rt(rt2x00dev, RT2860) || rt2x00_rt(rt2x00dev, RT2872)) { @@ -5598,10 +6881,10 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) */ if (rt2x00_get_field16(word, EEPROM_NIC_CONF0_RXPATH) > 2) rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); - rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); + rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); } - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_CONF1_HW_RADIO, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC, 0); @@ -5618,24 +6901,24 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_NIC_CONF1_INTERNAL_TX_ALC, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BT_COEXIST, 0); rt2x00_set_field16(&word, EEPROM_NIC_CONF1_DAC_TEST, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word); + rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word); rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); } - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); if ((word & 0x00ff) == 0x00ff) { rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); + rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word); rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); } if ((word & 0xff00) == 0xff00) { rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE, LED_MODE_TXRX_ACTIVITY); rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); - rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555); - rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221); - rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8); + rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word); + rt2800_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555); + rt2800_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221); + rt2800_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8); rt2x00_eeprom_dbg(rt2x00dev, "Led Mode: 0x%04x\n", word); } @@ -5644,56 +6927,61 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) * lna0 as correct value. Note that EEPROM_LNA * is never validated. */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &word); default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0); - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word); + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word); - rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word); - if ((word & 0x00ff) != 0x00ff) { - drv_data->txmixer_gain_24g = - rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL); - } else { - drv_data->txmixer_gain_24g = 0; - } + drv_data->txmixer_gain_24g = rt2800_get_txmixer_gain_24g(rt2x00dev); - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0); - if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 || - rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff) - rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1, - default_lna_gain); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word); - if ((word & 0x00ff) != 0x00ff) { - drv_data->txmixer_gain_5g = - rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL); - } else { - drv_data->txmixer_gain_5g = 0; + if (!rt2x00_rt(rt2x00dev, RT3593)) { + if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 || + rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff) + rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1, + default_lna_gain); } + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word); - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word); + drv_data->txmixer_gain_5g = rt2800_get_txmixer_gain_5g(rt2x00dev); + + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word); + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word); - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word); if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10) rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0); - if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 || - rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff) - rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2, - default_lna_gain); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word); + if (!rt2x00_rt(rt2x00dev, RT3593)) { + if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 || + rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff) + rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2, + default_lna_gain); + } + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word); + + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &word); + if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0x00 || + rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0xff) + rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1, + default_lna_gain); + if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0x00 || + rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0xff) + rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1, + default_lna_gain); + rt2800_eeprom_write(rt2x00dev, EEPROM_EXT_LNA2, word); + } return 0; } @@ -5707,7 +6995,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Read EEPROM word for configuration. */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); /* * Identify RF chipset by EEPROM value @@ -5717,7 +7005,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) - rt2x00_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf); + rt2800_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf); else rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE); @@ -5731,6 +7019,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) case RF3021: case RF3022: case RF3052: + case RF3053: case RF3290: case RF3320: case RF3322: @@ -5757,7 +7046,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00dev->default_ant.rx_chain_num = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH); - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3090) || @@ -5810,7 +7099,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Read frequency offset and RF programming sequence. */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); /* @@ -5827,7 +7116,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Check if support EIRP tx power limit feature. */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_2GHZ) < EIRP_MAX_TX_POWER_LIMIT) @@ -6109,12 +7398,79 @@ static const struct rf_channel rf_vals_5592_xtal40[] = { {196, 83, 0, 12, 1}, }; +static const struct rf_channel rf_vals_3053[] = { + /* Channel, N, R, K */ + {1, 241, 2, 2}, + {2, 241, 2, 7}, + {3, 242, 2, 2}, + {4, 242, 2, 7}, + {5, 243, 2, 2}, + {6, 243, 2, 7}, + {7, 244, 2, 2}, + {8, 244, 2, 7}, + {9, 245, 2, 2}, + {10, 245, 2, 7}, + {11, 246, 2, 2}, + {12, 246, 2, 7}, + {13, 247, 2, 2}, + {14, 248, 2, 4}, + + {36, 0x56, 0, 4}, + {38, 0x56, 0, 6}, + {40, 0x56, 0, 8}, + {44, 0x57, 0, 0}, + {46, 0x57, 0, 2}, + {48, 0x57, 0, 4}, + {52, 0x57, 0, 8}, + {54, 0x57, 0, 10}, + {56, 0x58, 0, 0}, + {60, 0x58, 0, 4}, + {62, 0x58, 0, 6}, + {64, 0x58, 0, 8}, + + {100, 0x5B, 0, 8}, + {102, 0x5B, 0, 10}, + {104, 0x5C, 0, 0}, + {108, 0x5C, 0, 4}, + {110, 0x5C, 0, 6}, + {112, 0x5C, 0, 8}, + + /* NOTE: Channel 114 has been removed intentionally. + * The EEPROM contains no TX power values for that, + * and it is disabled in the vendor driver as well. + */ + + {116, 0x5D, 0, 0}, + {118, 0x5D, 0, 2}, + {120, 0x5D, 0, 4}, + {124, 0x5D, 0, 8}, + {126, 0x5D, 0, 10}, + {128, 0x5E, 0, 0}, + {132, 0x5E, 0, 4}, + {134, 0x5E, 0, 6}, + {136, 0x5E, 0, 8}, + {140, 0x5F, 0, 0}, + + {149, 0x5F, 0, 9}, + {151, 0x5F, 0, 11}, + {153, 0x60, 0, 1}, + {157, 0x60, 0, 5}, + {159, 0x60, 0, 7}, + {161, 0x60, 0, 9}, + {165, 0x61, 0, 1}, + {167, 0x61, 0, 3}, + {169, 0x61, 0, 5}, + {171, 0x61, 0, 7}, + {173, 0x61, 0, 9}, +}; + static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; char *default_power1; char *default_power2; + char *default_power3; unsigned int i; u16 eeprom; u32 reg; @@ -6149,7 +7505,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, - rt2x00_eeprom_addr(rt2x00dev, + rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* @@ -6165,7 +7521,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->max_report_rates = 7; rt2x00dev->hw->max_rate_tries = 1; - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); /* * Initialize hw_mode information. @@ -6200,6 +7556,10 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_3x); spec->channels = rf_vals_3x; + } else if (rt2x00_rf(rt2x00dev, RF3053)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; + spec->num_channels = ARRAY_SIZE(rf_vals_3053); + spec->channels = rf_vals_3053; } else if (rt2x00_rf(rt2x00dev, RF5592)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; @@ -6265,21 +7625,40 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->channels_info = info; - default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1); - default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2); + default_power1 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1); + default_power2 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2); + + if (rt2x00dev->default_ant.tx_chain_num > 2) + default_power3 = rt2800_eeprom_addr(rt2x00dev, + EEPROM_EXT_TXPOWER_BG3); + else + default_power3 = NULL; for (i = 0; i < 14; i++) { info[i].default_power1 = default_power1[i]; info[i].default_power2 = default_power2[i]; + if (default_power3) + info[i].default_power3 = default_power3[i]; } if (spec->num_channels > 14) { - default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1); - default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2); + default_power1 = rt2800_eeprom_addr(rt2x00dev, + EEPROM_TXPOWER_A1); + default_power2 = rt2800_eeprom_addr(rt2x00dev, + EEPROM_TXPOWER_A2); + + if (rt2x00dev->default_ant.tx_chain_num > 2) + default_power3 = + rt2800_eeprom_addr(rt2x00dev, + EEPROM_EXT_TXPOWER_A3); + else + default_power3 = NULL; for (i = 14; i < spec->num_channels; i++) { info[i].default_power1 = default_power1[i - 14]; info[i].default_power2 = default_power2[i - 14]; + if (default_power3) + info[i].default_power3 = default_power3[i - 14]; } } @@ -6290,6 +7669,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) case RF3022: case RF3320: case RF3052: + case RF3053: case RF3290: case RF5360: case RF5370: @@ -6328,6 +7708,7 @@ static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev) case RT3352: case RT3390: case RT3572: + case RT3593: case RT5390: case RT5392: case RT5592: diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index 6ec7394..a94ba44 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -226,4 +226,8 @@ int rt2800_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev); +void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev, + unsigned short *txwi_size, + unsigned short *rxwi_size); + #endif /* RT2800LIB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 0005562..f8f2abb 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -507,9 +507,13 @@ static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); if (rt2x00_is_pcie(rt2x00dev) && - (rt2x00_rt(rt2x00dev, RT3572) || + (rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3390) || + rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392))) { + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT5592))) { rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, ®); rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); @@ -1189,12 +1193,17 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { static void rt2800pci_queue_init(struct data_queue *queue) { + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + unsigned short txwi_size, rxwi_size; + + rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); + switch (queue->qid) { case QID_RX: queue->limit = 128; queue->data_size = AGGREGATION_SIZE; queue->desc_size = RXD_DESC_SIZE; - queue->winfo_size = RXWI_DESC_SIZE_4WORDS; + queue->winfo_size = rxwi_size; queue->priv_size = sizeof(struct queue_entry_priv_mmio); break; @@ -1205,7 +1214,7 @@ static void rt2800pci_queue_init(struct data_queue *queue) queue->limit = 64; queue->data_size = AGGREGATION_SIZE; queue->desc_size = TXD_DESC_SIZE; - queue->winfo_size = TXWI_DESC_SIZE_4WORDS; + queue->winfo_size = txwi_size; queue->priv_size = sizeof(struct queue_entry_priv_mmio); break; @@ -1213,7 +1222,7 @@ static void rt2800pci_queue_init(struct data_queue *queue) queue->limit = 8; queue->data_size = 0; /* No DMA required for beacons */ queue->desc_size = TXD_DESC_SIZE; - queue->winfo_size = TXWI_DESC_SIZE_4WORDS; + queue->winfo_size = txwi_size; queue->priv_size = sizeof(struct queue_entry_priv_mmio); break; diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 840833b..96961b9 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -854,13 +854,7 @@ static void rt2800usb_queue_init(struct data_queue *queue) struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; unsigned short txwi_size, rxwi_size; - if (rt2x00_rt(rt2x00dev, RT5592)) { - txwi_size = TXWI_DESC_SIZE_5WORDS; - rxwi_size = RXWI_DESC_SIZE_6WORDS; - } else { - txwi_size = TXWI_DESC_SIZE_4WORDS; - rxwi_size = RXWI_DESC_SIZE_4WORDS; - } + rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); switch (queue->qid) { case QID_RX: @@ -977,6 +971,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0411, 0x016f) }, { USB_DEVICE(0x0411, 0x01a2) }, { USB_DEVICE(0x0411, 0x01ee) }, + { USB_DEVICE(0x0411, 0x01a8) }, /* Corega */ { USB_DEVICE(0x07aa, 0x002f) }, { USB_DEVICE(0x07aa, 0x003c) }, @@ -1194,6 +1189,40 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Zinwell */ { USB_DEVICE(0x5a57, 0x0284) }, #endif +#ifdef CONFIG_RT2800USB_RT3573 + /* AirLive */ + { USB_DEVICE(0x1b75, 0x7733) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x17bc) }, + { USB_DEVICE(0x0b05, 0x17ad) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x1103) }, + /* Cameo */ + { USB_DEVICE(0x148f, 0xf301) }, + /* Edimax */ + { USB_DEVICE(0x7392, 0x7733) }, + /* Hawking */ + { USB_DEVICE(0x0e66, 0x0020) }, + { USB_DEVICE(0x0e66, 0x0021) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x094e) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x003b) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x016b) }, + /* NETGEAR */ + { USB_DEVICE(0x0846, 0x9012) }, + { USB_DEVICE(0x0846, 0x9019) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xed19) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x3573) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0067) }, + { USB_DEVICE(0x0df6, 0x006a) }, + /* ZyXEL */ + { USB_DEVICE(0x0586, 0x3421) }, +#endif #ifdef CONFIG_RT2800USB_RT53XX /* Arcadyan */ { USB_DEVICE(0x043e, 0x7a12) }, diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index ee3fc57..fe4c572 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -211,6 +211,7 @@ struct channel_info { short max_power; short default_power1; short default_power2; + short default_power3; }; /* diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index b16521e..712eea9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -566,10 +566,10 @@ static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev, #undef TID_CHECK - if (compare_ether_addr(ba->ra, entry->ta)) + if (!ether_addr_equal(ba->ra, entry->ta)) continue; - if (compare_ether_addr(ba->ta, entry->ra)) + if (!ether_addr_equal(ba->ta, entry->ra)) continue; /* Mark BAR since we received the according BA */ diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index aa95c6c..6c8a33b 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -936,7 +936,7 @@ void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index) spin_unlock_irqrestore(&queue->index_lock, irqflags); } -void rt2x00queue_pause_queue_nocheck(struct data_queue *queue) +static void rt2x00queue_pause_queue_nocheck(struct data_queue *queue) { switch (queue->qid) { case QID_AC_VO: diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 91a04e2..fc207b2 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -3,10 +3,10 @@ * Linux device driver for RTL8180 / RTL8185 * * Copyright 2007 Michael Wu <flamingice@sourmilk.net> - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Based on the r8180 driver, which is: - * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al. + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. * * Thanks to Realtek for their support! * @@ -32,7 +32,7 @@ #include "grf5101.h" MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); -MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>"); +MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>"); MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rtl818x/rtl8180/grf5101.c b/drivers/net/wireless/rtl818x/rtl8180/grf5101.c index 077ff92..dc845693 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/grf5101.c +++ b/drivers/net/wireless/rtl818x/rtl8180/grf5101.c @@ -2,7 +2,7 @@ /* * Radio tuning for GCT GRF5101 on RTL8180 * - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things diff --git a/drivers/net/wireless/rtl818x/rtl8180/grf5101.h b/drivers/net/wireless/rtl818x/rtl8180/grf5101.h index 7664711..4d80a27 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/grf5101.h +++ b/drivers/net/wireless/rtl818x/rtl8180/grf5101.h @@ -4,7 +4,7 @@ /* * Radio tuning for GCT GRF5101 on RTL8180 * - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things diff --git a/drivers/net/wireless/rtl818x/rtl8180/max2820.c b/drivers/net/wireless/rtl818x/rtl8180/max2820.c index 4715000..a63c443c 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/max2820.c +++ b/drivers/net/wireless/rtl818x/rtl8180/max2820.c @@ -1,7 +1,7 @@ /* * Radio tuning for Maxim max2820 on RTL8180 * - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things diff --git a/drivers/net/wireless/rtl818x/rtl8180/max2820.h b/drivers/net/wireless/rtl818x/rtl8180/max2820.h index 61cf6d1..8e982b7 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/max2820.h +++ b/drivers/net/wireless/rtl818x/rtl8180/max2820.h @@ -4,7 +4,7 @@ /* * Radio tuning for Maxim max2820 on RTL8180 * - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c index cc2a541..ee638d0 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c @@ -3,10 +3,10 @@ * Radio tuning for RTL8225 on RTL8180 * * Copyright 2007 Michael Wu <flamingice@sourmilk.net> - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Based on the r8180 driver, which is: - * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al. + * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al. * * Thanks to Realtek for their support! * diff --git a/drivers/net/wireless/rtl818x/rtl8180/sa2400.c b/drivers/net/wireless/rtl818x/rtl8180/sa2400.c index b3ec40f..7614d9c 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/sa2400.c +++ b/drivers/net/wireless/rtl818x/rtl8180/sa2400.c @@ -2,7 +2,7 @@ /* * Radio tuning for Philips SA2400 on RTL8180 * - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things diff --git a/drivers/net/wireless/rtl818x/rtl8180/sa2400.h b/drivers/net/wireless/rtl818x/rtl8180/sa2400.h index a4aaa0d..fb0093f 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/sa2400.h +++ b/drivers/net/wireless/rtl818x/rtl8180/sa2400.h @@ -4,7 +4,7 @@ /* * Radio tuning for Philips SA2400 on RTL8180 * - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Code from the BSD driver and the rtl8181 project have been * very useful to understand certain things diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index f49220e..841fb9d 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -2,10 +2,10 @@ * Linux device driver for RTL8187 * * Copyright 2007 Michael Wu <flamingice@sourmilk.net> - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al. + * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al. * * The driver was extended to the RTL8187B in 2008 by: * Herton Ronaldo Krzesinski <herton@mandriva.com.br> @@ -37,7 +37,7 @@ #include "rfkill.h" MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); -MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>"); +MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>"); MODULE_AUTHOR("Herton Ronaldo Krzesinski <herton@mandriva.com.br>"); MODULE_AUTHOR("Hin-Tak Leung <htl10@users.sourceforge.net>"); MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>"); diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h index e19a20a..56aee06 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h @@ -2,10 +2,10 @@ * Definitions for RTL8187 hardware * * Copyright 2007 Michael Wu <flamingice@sourmilk.net> - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al. + * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c index f0bf35f..a26193a 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c @@ -2,10 +2,10 @@ * Radio tuning for RTL8225 on RTL8187 * * Copyright 2007 Michael Wu <flamingice@sourmilk.net> - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al. + * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al. * * Magic delays, register offsets, and phy value tables below are * taken from the original r8187 driver sources. Thanks to Realtek diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h index 20c5b6e..141afb0 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h @@ -2,10 +2,10 @@ * Radio tuning definitions for RTL8225 on RTL8187 * * Copyright 2007 Michael Wu <flamingice@sourmilk.net> - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al. + * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/net/wireless/rtl818x/rtl818x.h b/drivers/net/wireless/rtl818x/rtl818x.h index 1615f63..ce23dfd 100644 --- a/drivers/net/wireless/rtl818x/rtl818x.h +++ b/drivers/net/wireless/rtl818x/rtl818x.h @@ -2,10 +2,10 @@ * Definitions for RTL818x hardware * * Copyright 2007 Michael Wu <flamingice@sourmilk.net> - * Copyright 2007 Andrea Merello <andreamrl@tiscali.it> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> * * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al. + * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 7651f5a..8bb4a9a 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -1304,7 +1304,7 @@ void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb) return; /* and only beacons from the associated BSSID, please */ - if (compare_ether_addr(hdr->addr3, rtlpriv->mac80211.bssid)) + if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) return; rtlpriv->link_info.bcn_rx_inperiod++; diff --git a/drivers/net/wireless/rtlwifi/ps.c b/drivers/net/wireless/rtlwifi/ps.c index 298b615..0d81f76 100644 --- a/drivers/net/wireless/rtlwifi/ps.c +++ b/drivers/net/wireless/rtlwifi/ps.c @@ -688,7 +688,7 @@ static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data, find_p2p_ie = true; /*to find noa ie*/ while (ie + 1 < end) { - noa_len = READEF2BYTE(&ie[1]); + noa_len = READEF2BYTE((__le16 *)&ie[1]); if (ie + 3 + ie[1] > end) return; @@ -717,13 +717,13 @@ static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data, READEF1BYTE(ie+index); index += 1; p2pinfo->noa_duration[i] = - READEF4BYTE(ie+index); + READEF4BYTE((__le32 *)ie+index); index += 4; p2pinfo->noa_interval[i] = - READEF4BYTE(ie+index); + READEF4BYTE((__le32 *)ie+index); index += 4; p2pinfo->noa_start_time[i] = - READEF4BYTE(ie+index); + READEF4BYTE((__le32 *)ie+index); index += 4; } @@ -780,7 +780,7 @@ static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data, RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n"); /*to find noa ie*/ while (ie + 1 < end) { - noa_len = READEF2BYTE(&ie[1]); + noa_len = READEF2BYTE((__le16 *)&ie[1]); if (ie + 3 + ie[1] > end) return; @@ -809,13 +809,13 @@ static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data, READEF1BYTE(ie+index); index += 1; p2pinfo->noa_duration[i] = - READEF4BYTE(ie+index); + READEF4BYTE((__le32 *)ie+index); index += 4; p2pinfo->noa_interval[i] = - READEF4BYTE(ie+index); + READEF4BYTE((__le32 *)ie+index); index += 4; p2pinfo->noa_start_time[i] = - READEF4BYTE(ie+index); + READEF4BYTE((__le32 *)ie+index); index += 4; } @@ -923,7 +923,7 @@ void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len) return; /* and only beacons from the associated BSSID, please */ - if (compare_ether_addr(hdr->addr3, rtlpriv->mac80211.bssid)) + if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) return; /* check if this really is a beacon */ diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c index f9f059d..a98acef 100644 --- a/drivers/net/wireless/rtlwifi/rc.c +++ b/drivers/net/wireless/rtlwifi/rc.c @@ -218,6 +218,7 @@ static void rtl_tx_status(void *ppriv, static void rtl_rate_init(void *ppriv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta) { } diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c index a8871d6..68685a8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c @@ -305,13 +305,14 @@ static void _rtl88ee_translate_rx_signal_stuff(struct ieee80211_hw *hw, psaddr = ieee80211_get_SA(hdr); memcpy(pstatus->psaddr, psaddr, ETH_ALEN); - addr = (!compare_ether_addr(mac->bssid, (ufc & IEEE80211_FCTL_TODS) ? - hdr->addr1 : (ufc & IEEE80211_FCTL_FROMDS) ? - hdr->addr2 : hdr->addr3)); + addr = ether_addr_equal(mac->bssid, + (ufc & IEEE80211_FCTL_TODS) ? hdr->addr1 : + (ufc & IEEE80211_FCTL_FROMDS) ? hdr->addr2 : + hdr->addr3); match_bssid = ((IEEE80211_FTYPE_CTL != type) && (!pstatus->hwerror) && (!pstatus->crc) && (!pstatus->icv)) && addr; - addr = (!compare_ether_addr(praddr, rtlefuse->dev_addr)); + addr = ether_addr_equal(praddr, rtlefuse->dev_addr); packet_toself = match_bssid && addr; if (ieee80211_is_beacon(fc)) diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h index 8e3ec1e..0f7812e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h @@ -109,5 +109,8 @@ void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); bool rtl92cu_phy_mac_config(struct ieee80211_hw *hw); +void rtl92cu_update_hal_rate_tbl(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u8 rssi_level); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.h b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.h index 262e1e4..a1310ab 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.h @@ -49,8 +49,5 @@ bool rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw, u32 rtl92cu_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask); void rtl92cu_phy_set_bw_mode_callback(struct ieee80211_hw *hw); -void rtl92cu_update_hal_rate_tbl(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, - u8 rssi_level); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index c72758d..bcd82a1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -255,16 +255,16 @@ static void _rtl8723ae_translate_rx_signal_stuff(struct ieee80211_hw *hw, type = WLAN_FC_GET_TYPE(fc); praddr = hdr->addr1; - packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) && - (!compare_ether_addr(mac->bssid, - (le16_to_cpu(fc) & IEEE80211_FCTL_TODS) ? - hdr->addr1 : (le16_to_cpu(fc) & - IEEE80211_FCTL_FROMDS) ? - hdr->addr2 : hdr->addr3)) && (!pstatus->hwerror) && - (!pstatus->crc) && (!pstatus->icv)); - - packet_toself = packet_matchbssid && - (!compare_ether_addr(praddr, rtlefuse->dev_addr)); + packet_matchbssid = + ((IEEE80211_FTYPE_CTL != type) && + ether_addr_equal(mac->bssid, + (le16_to_cpu(fc) & IEEE80211_FCTL_TODS) ? hdr->addr1 : + (le16_to_cpu(fc) & IEEE80211_FCTL_FROMDS) ? hdr->addr2 : + hdr->addr3) && + (!pstatus->hwerror) && (!pstatus->crc) && (!pstatus->icv)); + + packet_toself = (packet_matchbssid && + ether_addr_equal(praddr, rtlefuse->dev_addr)); if (ieee80211_is_beacon(fc)) packet_beacon = true; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index b8db55c..38995f9 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1315,7 +1315,7 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) #ifdef CONFIG_PM static int -wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p) +wl1271_validate_wowlan_pattern(struct cfg80211_pkt_pattern *p) { int num_fields = 0, in_field = 0, fields_size = 0; int i, pattern_len = 0; @@ -1458,9 +1458,9 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, * Allocates an RX filter returned through f * which needs to be freed using rx_filter_free() */ -static int wl1271_convert_wowlan_pattern_to_rx_filter( - struct cfg80211_wowlan_trig_pkt_pattern *p, - struct wl12xx_rx_filter **f) +static int +wl1271_convert_wowlan_pattern_to_rx_filter(struct cfg80211_pkt_pattern *p, + struct wl12xx_rx_filter **f) { int i, j, ret = 0; struct wl12xx_rx_filter *filter; @@ -1562,7 +1562,7 @@ static int wl1271_configure_wowlan(struct wl1271 *wl, /* Translate WoWLAN patterns into filters */ for (i = 0; i < wow->n_patterns; i++) { - struct cfg80211_wowlan_trig_pkt_pattern *p; + struct cfg80211_pkt_pattern *p; struct wl12xx_rx_filter *filter = NULL; p = &wow->patterns[i]; @@ -5623,7 +5623,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_remain_on_channel_duration = 5000; wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_SUPPORTS_SCHED_SCAN; /* make sure all our channels fit in the scanned_ch bitmask */ BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + diff --git a/drivers/net/wireless/ti/wlcore/testmode.c b/drivers/net/wireless/ti/wlcore/testmode.c index f344276..527590f 100644 --- a/drivers/net/wireless/ti/wlcore/testmode.c +++ b/drivers/net/wireless/ti/wlcore/testmode.c @@ -356,7 +356,8 @@ out: return ret; } -int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len) +int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) { struct wl1271 *wl = hw->priv; struct nlattr *tb[WL1271_TM_ATTR_MAX + 1]; diff --git a/drivers/net/wireless/ti/wlcore/testmode.h b/drivers/net/wireless/ti/wlcore/testmode.h index 8071654..61d8434 100644 --- a/drivers/net/wireless/ti/wlcore/testmode.h +++ b/drivers/net/wireless/ti/wlcore/testmode.h @@ -26,6 +26,7 @@ #include <net/mac80211.h> -int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len); +int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); #endif /* __WL1271_TESTMODE_H__ */ diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index b8ba1f9..d39c417 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -75,8 +75,10 @@ static int zd1201_fw_upload(struct usb_device *dev, int apfw) len = fw_entry->size; buf = kmalloc(1024, GFP_ATOMIC); - if (!buf) + if (!buf) { + err = -ENOMEM; goto exit; + } while (len > 0) { int translen = (len > 1024) ? 1024 : len; @@ -1764,8 +1766,10 @@ static int zd1201_probe(struct usb_interface *interface, zd->endp_out2 = 2; zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL); zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!zd->rx_urb || !zd->tx_urb) + if (!zd->rx_urb || !zd->tx_urb) { + err = -ENOMEM; goto err_zd; + } mdelay(100); err = zd1201_drvr_start(zd); |