Commit 1f807827 authored by John W. Linville's avatar John W. Linville

Merge tag 'for-linville-20130730' of git://github.com/kvalo/ath6kl

parents 20416229 424121c3
...@@ -20,6 +20,12 @@ ...@@ -20,6 +20,12 @@
#include "debug.h" #include "debug.h"
#include "htc.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) int ath10k_bmi_done(struct ath10k *ar)
{ {
struct bmi_cmd cmd; struct bmi_cmd cmd;
...@@ -105,7 +111,8 @@ int ath10k_bmi_read_memory(struct ath10k *ar, ...@@ -105,7 +111,8 @@ int ath10k_bmi_read_memory(struct ath10k *ar,
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
&resp, &rxlen); &resp, &rxlen);
if (ret) { if (ret) {
ath10k_warn("unable to read from the device\n"); ath10k_warn("unable to read from the device (%d)\n",
ret);
return ret; return ret;
} }
...@@ -149,7 +156,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar, ...@@ -149,7 +156,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar,
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
NULL, NULL); NULL, NULL);
if (ret) { if (ret) {
ath10k_warn("unable to write to the device\n"); ath10k_warn("unable to write to the device (%d)\n",
ret);
return ret; return ret;
} }
......
...@@ -184,6 +184,7 @@ struct bmi_target_info { ...@@ -184,6 +184,7 @@ struct bmi_target_info {
#define BMI_CE_NUM_TO_TARG 0 #define BMI_CE_NUM_TO_TARG 0
#define BMI_CE_NUM_TO_HOST 1 #define BMI_CE_NUM_TO_HOST 1
void ath10k_bmi_start(struct ath10k *ar);
int ath10k_bmi_done(struct ath10k *ar); int ath10k_bmi_done(struct ath10k *ar);
int ath10k_bmi_get_target_info(struct ath10k *ar, int ath10k_bmi_get_target_info(struct ath10k *ar,
struct bmi_target_info *target_info); struct bmi_target_info *target_info);
......
...@@ -79,7 +79,7 @@ static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar, ...@@ -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); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
void __iomem *indicator_addr; 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); ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
return; return;
} }
......
...@@ -100,7 +100,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar) ...@@ -100,7 +100,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
goto conn_fail; goto conn_fail;
/* Start HTC */ /* Start HTC */
status = ath10k_htc_start(ar->htc); status = ath10k_htc_start(&ar->htc);
if (status) if (status)
goto conn_fail; goto conn_fail;
...@@ -116,7 +116,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar) ...@@ -116,7 +116,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
return 0; return 0;
timeout: timeout:
ath10k_htc_stop(ar->htc); ath10k_htc_stop(&ar->htc);
conn_fail: conn_fail:
return status; return status;
} }
...@@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, ...@@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
static int ath10k_download_board_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 board_data_size = QCA988X_BOARD_DATA_SZ;
u32 address; u32 address;
const struct firmware *fw;
int ret; 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); ret = ath10k_push_board_ext_data(ar, fw);
if (ret) { if (ret) {
ath10k_err("could not push board ext data (%d)\n", 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) ...@@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar)
} }
exit: exit:
release_firmware(fw);
return ret; return ret;
} }
static int ath10k_download_and_run_otp(struct ath10k *ar) static int ath10k_download_and_run_otp(struct ath10k *ar)
{ {
const struct firmware *fw; const struct firmware *fw = ar->otp;
u32 address; u32 address = ar->hw_params.patch_load_addr;
u32 exec_param; u32 exec_param;
int ret; int ret;
/* OTP is optional */ /* OTP is optional */
if (ar->hw_params.fw.otp == NULL) { if (!ar->otp)
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));
return 0; return 0;
}
ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
if (ret) { if (ret) {
...@@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) ...@@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
} }
exit: exit:
release_firmware(fw);
return ret; return ret;
} }
static int ath10k_download_fw(struct ath10k *ar) static int ath10k_download_fw(struct ath10k *ar)
{ {
const struct firmware *fw; const struct firmware *fw = ar->firmware;
u32 address; u32 address;
int ret; int ret;
if (ar->hw_params.fw.fw == NULL)
return -EINVAL;
address = ar->hw_params.patch_load_addr; 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); ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
if (ret) { if (ret) {
ath10k_err("could not write fw (%d)\n", ret); ath10k_err("could not write fw (%d)\n", ret);
...@@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar) ...@@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar)
} }
exit: 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; return ret;
} }
...@@ -440,8 +476,35 @@ static int ath10k_init_hw_params(struct ath10k *ar) ...@@ -440,8 +476,35 @@ static int ath10k_init_hw_params(struct ath10k *ar)
return 0; 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, struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
enum ath10k_bus bus,
const struct ath10k_hif_ops *hif_ops) const struct ath10k_hif_ops *hif_ops)
{ {
struct ath10k *ar; struct ath10k *ar;
...@@ -458,9 +521,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, ...@@ -458,9 +521,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
ar->hif.priv = hif_priv; ar->hif.priv = hif_priv;
ar->hif.ops = hif_ops; 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.started);
init_completion(&ar->scan.completed); init_completion(&ar->scan.completed);
...@@ -487,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, ...@@ -487,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
init_waitqueue_head(&ar->event_queue); init_waitqueue_head(&ar->event_queue);
INIT_WORK(&ar->restart_work, ath10k_core_restart);
return ar; return ar;
err_wq: err_wq:
...@@ -504,24 +566,11 @@ void ath10k_core_destroy(struct ath10k *ar) ...@@ -504,24 +566,11 @@ void ath10k_core_destroy(struct ath10k *ar)
} }
EXPORT_SYMBOL(ath10k_core_destroy); EXPORT_SYMBOL(ath10k_core_destroy);
int ath10k_core_start(struct ath10k *ar)
int ath10k_core_register(struct ath10k *ar)
{ {
struct ath10k_htc_ops htc_ops;
struct bmi_target_info target_info;
int status; int status;
memset(&target_info, 0, sizeof(target_info)); ath10k_bmi_start(ar);
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;
if (ath10k_init_configure_target(ar)) { if (ath10k_init_configure_target(ar)) {
status = -EINVAL; status = -EINVAL;
...@@ -536,32 +585,32 @@ int ath10k_core_register(struct ath10k *ar) ...@@ -536,32 +585,32 @@ int ath10k_core_register(struct ath10k *ar)
if (status) if (status)
goto err; 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); status = ath10k_htc_init(ar);
if (IS_ERR(ar->htc)) { if (status) {
status = PTR_ERR(ar->htc); ath10k_err("could not init HTC (%d)\n", status);
ath10k_err("could not create HTC (%d)\n", status);
goto err; goto err;
} }
status = ath10k_bmi_done(ar); status = ath10k_bmi_done(ar);
if (status) if (status)
goto err_htc_destroy; goto err;
status = ath10k_wmi_attach(ar); status = ath10k_wmi_attach(ar);
if (status) { if (status) {
ath10k_err("WMI attach failed: %d\n", 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) if (status)
goto err_wmi_detach; goto err_wmi_detach;
ar->htt = ath10k_htt_attach(ar); status = ath10k_htt_attach(ar);
if (!ar->htt) { if (status) {
status = -ENOMEM; ath10k_err("could not attach htt (%d)\n", status);
goto err_wmi_detach; goto err_wmi_detach;
} }
...@@ -588,77 +637,127 @@ int ath10k_core_register(struct ath10k *ar) ...@@ -588,77 +637,127 @@ int ath10k_core_register(struct ath10k *ar)
goto err_disconnect_htc; goto err_disconnect_htc;
} }
status = ath10k_htt_attach_target(ar->htt); status = ath10k_htt_attach_target(&ar->htt);
if (status)
goto err_disconnect_htc;
status = ath10k_mac_register(ar);
if (status) if (status)
goto err_disconnect_htc; goto err_disconnect_htc;
status = ath10k_debug_create(ar); ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
if (status) {
ath10k_err("unable to initialize debugfs\n");
goto err_unregister_mac;
}
return 0; return 0;
err_unregister_mac:
ath10k_mac_unregister(ar);
err_disconnect_htc: err_disconnect_htc:
ath10k_htc_stop(ar->htc); ath10k_htc_stop(&ar->htc);
err_htt_detach: err_htt_detach:
ath10k_htt_detach(ar->htt); ath10k_htt_detach(&ar->htt);
err_wmi_detach: err_wmi_detach:
ath10k_wmi_detach(ar); ath10k_wmi_detach(ar);
err_htc_destroy:
ath10k_htc_destroy(ar->htc);
err: err:
return status; 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. ath10k_htc_stop(&ar->htc);
* Otherwise we will fail to submit commands to FW and mac80211 will be ath10k_htt_detach(&ar->htt);
* unhappy about callback failures. */
ath10k_mac_unregister(ar);
ath10k_htc_stop(ar->htc);
ath10k_htt_detach(ar->htt);
ath10k_wmi_detach(ar); 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;
ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__); ret = ath10k_hif_power_up(ar);
if (ret) {
ath10k_err("could not start pci hif (%d)\n", ret);
return ret;
}
ret = ath10k_wmi_pdev_suspend_target(ar); memset(&target_info, 0, sizeof(target_info));
if (ret) ret = ath10k_bmi_get_target_info(ar, &target_info);
ath10k_warn("could not suspend target (%d)\n", ret); if (ret) {
ath10k_err("could not get target info (%d)\n", ret);
ath10k_hif_power_down(ar);
return ret;
}
ar->target_version = target_info.version;
ar->hw->wiphy->hw_version = target_info.version;
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; 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); status = ath10k_mac_register(ar);
if (ret) if (status) {
ath10k_warn("could not resume target (%d)\n", ret); 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_target_resume); 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_unregister);
MODULE_AUTHOR("Qualcomm Atheros"); MODULE_AUTHOR("Qualcomm Atheros");
MODULE_DESCRIPTION("Core module for QCA988X PCIe devices."); MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/pci.h> #include <linux/pci.h>
#include "htt.h"
#include "htc.h" #include "htc.h"
#include "hw.h" #include "hw.h"
#include "targaddrs.h" #include "targaddrs.h"
...@@ -43,10 +44,6 @@ ...@@ -43,10 +44,6 @@
struct ath10k; struct ath10k;
enum ath10k_bus {
ATH10K_BUS_PCI,
};
struct ath10k_skb_cb { struct ath10k_skb_cb {
dma_addr_t paddr; dma_addr_t paddr;
bool is_mapped; bool is_mapped;
...@@ -250,6 +247,28 @@ struct ath10k_debug { ...@@ -250,6 +247,28 @@ struct ath10k_debug {
struct completion event_stats_compl; 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 ath10k {
struct ath_common ath_common; struct ath_common ath_common;
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
...@@ -274,19 +293,16 @@ struct ath10k { ...@@ -274,19 +293,16 @@ struct ath10k {
struct { struct {
void *priv; void *priv;
enum ath10k_bus bus;
const struct ath10k_hif_ops *ops; const struct ath10k_hif_ops *ops;
} hif; } hif;
struct ath10k_wmi wmi;
wait_queue_head_t event_queue; wait_queue_head_t event_queue;
bool is_target_paused; bool is_target_paused;
struct ath10k_bmi bmi; struct ath10k_bmi bmi;
struct ath10k_wmi wmi;
struct ath10k_htc *htc; struct ath10k_htc htc;
struct ath10k_htt *htt; struct ath10k_htt htt;
struct ath10k_hw_params { struct ath10k_hw_params {
u32 id; u32 id;
...@@ -301,6 +317,10 @@ struct ath10k { ...@@ -301,6 +317,10 @@ struct ath10k {
} fw; } fw;
} hw_params; } hw_params;
const struct firmware *board_data;
const struct firmware *otp;
const struct firmware *firmware;
struct { struct {
struct completion started; struct completion started;
struct completion completed; struct completion completed;
...@@ -350,20 +370,22 @@ struct ath10k { ...@@ -350,20 +370,22 @@ struct ath10k {
struct completion offchan_tx_completed; struct completion offchan_tx_completed;
struct sk_buff *offchan_tx_skb; struct sk_buff *offchan_tx_skb;
enum ath10k_state state;
struct work_struct restart_work;
#ifdef CONFIG_ATH10K_DEBUGFS #ifdef CONFIG_ATH10K_DEBUGFS
struct ath10k_debug debug; struct ath10k_debug debug;
#endif #endif
}; };
struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
enum ath10k_bus bus,
const struct ath10k_hif_ops *hif_ops); const struct ath10k_hif_ops *hif_ops);
void ath10k_core_destroy(struct ath10k *ar); 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); int ath10k_core_register(struct ath10k *ar);
void ath10k_core_unregister(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_ */ #endif /* _CORE_H_ */
...@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, ...@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
struct wmi_pdev_stats *ps; struct wmi_pdev_stats *ps;
int i; int i;
mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock);
stats = &ar->debug.target_stats; stats = &ar->debug.target_stats;
...@@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, ...@@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
} }
} }
spin_unlock_bh(&ar->data_lock);
mutex_unlock(&ar->conf_mutex); mutex_unlock(&ar->conf_mutex);
complete(&ar->debug.event_stats_compl); complete(&ar->debug.event_stats_compl);
} }
...@@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, ...@@ -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 *ar = file->private_data;
struct ath10k_target_stats *fw_stats; struct ath10k_target_stats *fw_stats;
char *buf; char *buf = NULL;
unsigned int len = 0, buf_len = 2500; unsigned int len = 0, buf_len = 2500;
ssize_t ret_cnt; ssize_t ret_cnt = 0;
long left; long left;
int i; int i;
int ret; int ret;
fw_stats = &ar->debug.target_stats; 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); buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; goto exit;
ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT); ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
if (ret) { if (ret) {
ath10k_warn("could not request stats (%d)\n", ret); ath10k_warn("could not request stats (%d)\n", ret);
kfree(buf); goto exit;
return -EIO;
} }
left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ); left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
if (left <= 0)
goto exit;
if (left <= 0) { spin_lock_bh(&ar->data_lock);
kfree(buf);
return -ETIMEDOUT;
}
mutex_lock(&ar->conf_mutex);
len += scnprintf(buf + len, buf_len - len, "\n"); len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n", len += scnprintf(buf + len, buf_len - len, "%30s\n",
"ath10k PDEV stats"); "ath10k PDEV stats");
...@@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, ...@@ -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); fw_stats->peer_stat[i].peer_tx_rate);
len += scnprintf(buf + len, buf_len - len, "\n"); len += scnprintf(buf + len, buf_len - len, "\n");
} }
spin_unlock_bh(&ar->data_lock);
if (len > buf_len) if (len > buf_len)
len = buf_len; len = buf_len;
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
exit:
mutex_unlock(&ar->conf_mutex); mutex_unlock(&ar->conf_mutex);
kfree(buf); kfree(buf);
return ret_cnt; return ret_cnt;
} }
...@@ -443,6 +445,60 @@ static const struct file_operations fops_fw_stats = { ...@@ -443,6 +445,60 @@ static const struct file_operations fops_fw_stats = {
.llseek = default_llseek, .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) int ath10k_debug_create(struct ath10k *ar)
{ {
ar->debug.debugfs_phy = debugfs_create_dir("ath10k", ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
...@@ -459,6 +515,9 @@ int ath10k_debug_create(struct ath10k *ar) ...@@ -459,6 +515,9 @@ int ath10k_debug_create(struct ath10k *ar)
debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar, debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_wmi_services); &fops_wmi_services);
debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_simulate_fw_crash);
return 0; return 0;
} }
#endif /* CONFIG_ATH10K_DEBUGFS */ #endif /* CONFIG_ATH10K_DEBUGFS */
......
...@@ -46,8 +46,11 @@ struct ath10k_hif_ops { ...@@ -46,8 +46,11 @@ struct ath10k_hif_ops {
void *request, u32 request_len, void *request, u32 request_len,
void *response, u32 *response_len); void *response, u32 *response_len);
/* Post BMI phase, after FW is loaded. Starts regular operation */
int (*start)(struct ath10k *ar); 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); void (*stop)(struct ath10k *ar);
int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id, int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
...@@ -66,10 +69,20 @@ struct ath10k_hif_ops { ...@@ -66,10 +69,20 @@ struct ath10k_hif_ops {
*/ */
void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force); void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
void (*init)(struct ath10k *ar, void (*set_callbacks)(struct ath10k *ar,
struct ath10k_hif_cb *callbacks); struct ath10k_hif_cb *callbacks);
u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id); 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, ...@@ -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); ar->hif.ops->send_complete_check(ar, pipe_id, force);
} }
static inline void ath10k_hif_init(struct ath10k *ar, static inline void ath10k_hif_set_callbacks(struct ath10k *ar,
struct ath10k_hif_cb *callbacks) 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, 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, ...@@ -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); 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_ */ #endif /* _HIF_H_ */
...@@ -246,15 +246,22 @@ int ath10k_htc_send(struct ath10k_htc *htc, ...@@ -246,15 +246,22 @@ int ath10k_htc_send(struct ath10k_htc *htc,
{ {
struct ath10k_htc_ep *ep = &htc->endpoint[eid]; struct ath10k_htc_ep *ep = &htc->endpoint[eid];
if (htc->ar->state == ATH10K_STATE_WEDGED)
return -ECOMM;
if (eid >= ATH10K_HTC_EP_COUNT) { if (eid >= ATH10K_HTC_EP_COUNT) {
ath10k_warn("Invalid endpoint id: %d\n", eid); ath10k_warn("Invalid endpoint id: %d\n", eid);
return -ENOENT; return -ENOENT;
} }
skb_push(skb, sizeof(struct ath10k_htc_hdr));
spin_lock_bh(&htc->tx_lock); 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_queue_tail(&ep->tx_queue, skb);
skb_push(skb, sizeof(struct ath10k_htc_hdr));
spin_unlock_bh(&htc->tx_lock); spin_unlock_bh(&htc->tx_lock);
queue_work(htc->ar->workqueue, &ep->send_work); queue_work(htc->ar->workqueue, &ep->send_work);
...@@ -265,25 +272,19 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar, ...@@ -265,25 +272,19 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
struct sk_buff *skb, struct sk_buff *skb,
unsigned int eid) unsigned int eid)
{ {
struct ath10k_htc *htc = ar->htc; struct ath10k_htc *htc = &ar->htc;
struct ath10k_htc_ep *ep = &htc->endpoint[eid]; struct ath10k_htc_ep *ep = &htc->endpoint[eid];
bool stopping;
ath10k_htc_notify_tx_completion(ep, skb); ath10k_htc_notify_tx_completion(ep, skb);
/* the skb now belongs to the completion handler */ /* 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); spin_lock_bh(&htc->tx_lock);
stopping = htc->stopping; if (!ep->tx_credit_flow_enabled && !htc->stopped)
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
*/
queue_work(ar->workqueue, &ep->send_work); queue_work(ar->workqueue, &ep->send_work);
spin_unlock_bh(&htc->tx_lock);
return 0; return 0;
} }
...@@ -414,7 +415,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, ...@@ -414,7 +415,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
u8 pipe_id) u8 pipe_id)
{ {
int status = 0; int status = 0;
struct ath10k_htc *htc = ar->htc; struct ath10k_htc *htc = &ar->htc;
struct ath10k_htc_hdr *hdr; struct ath10k_htc_hdr *hdr;
struct ath10k_htc_ep *ep; struct ath10k_htc_ep *ep;
u16 payload_len; u16 payload_len;
...@@ -751,7 +752,8 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, ...@@ -751,7 +752,8 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
tx_alloc = ath10k_htc_get_credit_allocation(htc, tx_alloc = ath10k_htc_get_credit_allocation(htc,
conn_req->service_id); conn_req->service_id);
if (!tx_alloc) if (!tx_alloc)
ath10k_warn("HTC Service %s does not allocate target credits\n", ath10k_dbg(ATH10K_DBG_HTC,
"HTC Service %s does not allocate target credits\n",
htc_service_name(conn_req->service_id)); htc_service_name(conn_req->service_id));
skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
...@@ -947,7 +949,7 @@ void ath10k_htc_stop(struct ath10k_htc *htc) ...@@ -947,7 +949,7 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
struct ath10k_htc_ep *ep; struct ath10k_htc_ep *ep;
spin_lock_bh(&htc->tx_lock); spin_lock_bh(&htc->tx_lock);
htc->stopping = true; htc->stopped = true;
spin_unlock_bh(&htc->tx_lock); spin_unlock_bh(&htc->tx_lock);
for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) { for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
...@@ -956,26 +958,18 @@ void ath10k_htc_stop(struct ath10k_htc *htc) ...@@ -956,26 +958,18 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
} }
ath10k_hif_stop(htc->ar); ath10k_hif_stop(htc->ar);
ath10k_htc_reset_endpoint_states(htc);
} }
/* registered target arrival callback from the HIF layer */ /* registered target arrival callback from the HIF layer */
struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, int ath10k_htc_init(struct ath10k *ar)
struct ath10k_htc_ops *htc_ops)
{ {
struct ath10k_hif_cb htc_callbacks; struct ath10k_hif_cb htc_callbacks;
struct ath10k_htc_ep *ep = NULL; struct ath10k_htc_ep *ep = NULL;
struct ath10k_htc *htc = NULL; struct ath10k_htc *htc = &ar->htc;
/* FIXME: use struct ath10k instead */
htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL);
if (!htc)
return ERR_PTR(-ENOMEM);
spin_lock_init(&htc->tx_lock); 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); ath10k_htc_reset_endpoint_states(htc);
/* setup HIF layer callbacks */ /* setup HIF layer callbacks */
...@@ -986,15 +980,10 @@ struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, ...@@ -986,15 +980,10 @@ struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
/* Get HIF default pipe for HTC message exchange */ /* Get HIF default pipe for HTC message exchange */
ep = &htc->endpoint[ATH10K_HTC_EP_0]; 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); ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
init_completion(&htc->ctl_resp); init_completion(&htc->ctl_resp);
return htc; return 0;
}
void ath10k_htc_destroy(struct ath10k_htc *htc)
{
kfree(htc);
} }
...@@ -335,7 +335,7 @@ struct ath10k_htc { ...@@ -335,7 +335,7 @@ struct ath10k_htc {
struct ath10k *ar; struct ath10k *ar;
struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT]; struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
/* protects endpoint and stopping fields */ /* protects endpoint and stopped fields */
spinlock_t tx_lock; spinlock_t tx_lock;
struct ath10k_htc_ops htc_ops; struct ath10k_htc_ops htc_ops;
...@@ -349,11 +349,10 @@ struct ath10k_htc { ...@@ -349,11 +349,10 @@ struct ath10k_htc {
struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT]; struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
int target_credit_size; int target_credit_size;
bool stopping; bool stopped;
}; };
struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, int ath10k_htc_init(struct ath10k *ar);
struct ath10k_htc_ops *htc_ops);
int ath10k_htc_wait_target(struct ath10k_htc *htc); int ath10k_htc_wait_target(struct ath10k_htc *htc);
int ath10k_htc_start(struct ath10k_htc *htc); int ath10k_htc_start(struct ath10k_htc *htc);
int ath10k_htc_connect_service(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, ...@@ -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, int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
struct sk_buff *packet); struct sk_buff *packet);
void ath10k_htc_stop(struct ath10k_htc *htc); 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); struct sk_buff *ath10k_htc_alloc_skb(int size);
#endif #endif
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
*/ */
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/if_ether.h>
#include "htt.h" #include "htt.h"
#include "core.h" #include "core.h"
...@@ -36,7 +37,7 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt) ...@@ -36,7 +37,7 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
/* connect to control service */ /* connect to control service */
conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG; 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); &conn_resp);
if (status) if (status)
...@@ -47,15 +48,11 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt) ...@@ -47,15 +48,11 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
return 0; 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; int ret;
htt = kzalloc(sizeof(*htt), GFP_KERNEL);
if (!htt)
return NULL;
htt->ar = ar; htt->ar = ar;
htt->max_throughput_mbps = 800; htt->max_throughput_mbps = 800;
...@@ -65,8 +62,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) ...@@ -65,8 +62,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
* since ath10k_htt_rx_attach involves sending a rx ring configure * since ath10k_htt_rx_attach involves sending a rx ring configure
* message to the target. * 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; goto err_htc_attach;
}
ret = ath10k_htt_tx_attach(htt); ret = ath10k_htt_tx_attach(htt);
if (ret) { if (ret) {
...@@ -74,8 +74,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) ...@@ -74,8 +74,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
goto err_htc_attach; 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; goto err_rx_attach;
}
/* /*
* Prefetch enough data to satisfy target * Prefetch enough data to satisfy target
...@@ -89,13 +92,12 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) ...@@ -89,13 +92,12 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
8 + /* llc snap */ 8 + /* llc snap */
2; /* ip4 dscp or ip6 priority */ 2; /* ip4 dscp or ip6 priority */
return htt; return 0;
err_rx_attach: err_rx_attach:
ath10k_htt_tx_detach(htt); ath10k_htt_tx_detach(htt);
err_htc_attach: err_htc_attach:
kfree(htt); return ret;
return NULL;
} }
#define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ) #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
...@@ -148,5 +150,4 @@ void ath10k_htt_detach(struct ath10k_htt *htt) ...@@ -148,5 +150,4 @@ void ath10k_htt_detach(struct ath10k_htt *htt)
{ {
ath10k_htt_rx_detach(htt); ath10k_htt_rx_detach(htt);
ath10k_htt_tx_detach(htt); ath10k_htt_tx_detach(htt);
kfree(htt);
} }
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include <linux/bug.h> #include <linux/bug.h>
#include "core.h"
#include "htc.h" #include "htc.h"
#include "rx_desc.h" #include "rx_desc.h"
...@@ -1317,7 +1316,7 @@ struct htt_rx_desc { ...@@ -1317,7 +1316,7 @@ struct htt_rx_desc {
#define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */ #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) #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); int ath10k_htt_attach_target(struct ath10k_htt *htt);
void ath10k_htt_detach(struct ath10k_htt *htt); void ath10k_htt_detach(struct ath10k_htt *htt);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "core.h"
#include "htc.h" #include "htc.h"
#include "htt.h" #include "htt.h"
#include "txrx.h" #include "txrx.h"
...@@ -1036,7 +1037,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, ...@@ -1036,7 +1037,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) 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; struct htt_resp *resp = (struct htt_resp *)skb->data;
/* confirm alignment */ /* confirm alignment */
......
...@@ -92,7 +92,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt) ...@@ -92,7 +92,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
/* At the beginning free queue number should hint us the maximum /* At the beginning free queue number should hint us the maximum
* queue length */ * 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, htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
pipe); pipe);
...@@ -153,7 +153,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt) ...@@ -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) 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_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) { if (skb_cb->htt.is_conf) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
...@@ -194,7 +194,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) ...@@ -194,7 +194,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
ATH10K_SKB_CB(skb)->htt.is_conf = true; 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) { if (ret) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
return ret; return ret;
...@@ -281,7 +281,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) ...@@ -281,7 +281,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
ATH10K_SKB_CB(skb)->htt.is_conf = true; 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) { if (ret) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
return ret; return ret;
...@@ -346,7 +346,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ...@@ -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.refcount = 2;
skb_cb->htt.msdu = msdu; 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) if (res)
goto err; goto err;
...@@ -486,7 +486,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ...@@ -486,7 +486,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
skb_cb->htt.txfrag = txfrag; skb_cb->htt.txfrag = txfrag;
skb_cb->htt.msdu = msdu; 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) if (res)
goto err; goto err;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <net/mac80211.h> #include <net/mac80211.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include "hif.h"
#include "core.h" #include "core.h"
#include "debug.h" #include "debug.h"
#include "wmi.h" #include "wmi.h"
...@@ -43,6 +44,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif, ...@@ -43,6 +44,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
.macaddr = macaddr, .macaddr = macaddr,
}; };
lockdep_assert_held(&arvif->ar->conf_mutex);
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
arg.key_flags = WMI_KEY_PAIRWISE; arg.key_flags = WMI_KEY_PAIRWISE;
else else
...@@ -87,6 +90,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif, ...@@ -87,6 +90,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
struct ath10k *ar = arvif->ar; struct ath10k *ar = arvif->ar;
int ret; int ret;
lockdep_assert_held(&ar->conf_mutex);
INIT_COMPLETION(ar->install_key_done); INIT_COMPLETION(ar->install_key_done);
ret = ath10k_send_key(arvif, key, cmd, macaddr); 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) ...@@ -327,6 +332,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
return 0; 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) static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
{ {
int ret; int ret;
...@@ -364,6 +392,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) ...@@ -364,6 +392,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
spin_unlock_bh(&ar->data_lock); 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 */ /* Interface management */
/************************/ /************************/
...@@ -372,6 +414,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar) ...@@ -372,6 +414,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
{ {
int ret; int ret;
lockdep_assert_held(&ar->conf_mutex);
ret = wait_for_completion_timeout(&ar->vdev_setup_done, ret = wait_for_completion_timeout(&ar->vdev_setup_done,
ATH10K_VDEV_SETUP_TIMEOUT_HZ); ATH10K_VDEV_SETUP_TIMEOUT_HZ);
if (ret == 0) if (ret == 0)
...@@ -605,6 +649,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, ...@@ -605,6 +649,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
{ {
int ret = 0; int ret = 0;
lockdep_assert_held(&arvif->ar->conf_mutex);
if (!info->enable_beacon) { if (!info->enable_beacon) {
ath10k_vdev_stop(arvif); ath10k_vdev_stop(arvif);
return; return;
...@@ -631,6 +677,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, ...@@ -631,6 +677,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
{ {
int ret = 0; int ret = 0;
lockdep_assert_held(&arvif->ar->conf_mutex);
if (!info->ibss_joined) { if (!info->ibss_joined) {
ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer); ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
if (ret) if (ret)
...@@ -680,6 +728,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) ...@@ -680,6 +728,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
enum wmi_sta_ps_mode psmode; enum wmi_sta_ps_mode psmode;
int ret; int ret;
lockdep_assert_held(&arvif->ar->conf_mutex);
if (vif->type != NL80211_IFTYPE_STATION) if (vif->type != NL80211_IFTYPE_STATION)
return; return;
...@@ -722,6 +772,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar, ...@@ -722,6 +772,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
struct ieee80211_bss_conf *bss_conf, struct ieee80211_bss_conf *bss_conf,
struct wmi_peer_assoc_complete_arg *arg) struct wmi_peer_assoc_complete_arg *arg)
{ {
lockdep_assert_held(&ar->conf_mutex);
memcpy(arg->addr, sta->addr, ETH_ALEN); memcpy(arg->addr, sta->addr, ETH_ALEN);
arg->vdev_id = arvif->vdev_id; arg->vdev_id = arvif->vdev_id;
arg->peer_aid = sta->aid; arg->peer_aid = sta->aid;
...@@ -764,6 +816,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, ...@@ -764,6 +816,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
const u8 *rsnie = NULL; const u8 *rsnie = NULL;
const u8 *wpaie = NULL; const u8 *wpaie = NULL;
lockdep_assert_held(&ar->conf_mutex);
bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan, bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
info->bssid, NULL, 0, 0, 0); info->bssid, NULL, 0, 0, 0);
if (bss) { if (bss) {
...@@ -804,6 +858,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar, ...@@ -804,6 +858,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
u32 ratemask; u32 ratemask;
int i; int i;
lockdep_assert_held(&ar->conf_mutex);
sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band]; sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band]; ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
rates = sband->bitrates; rates = sband->bitrates;
...@@ -827,6 +883,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, ...@@ -827,6 +883,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
int smps; int smps;
int i, n; int i, n;
lockdep_assert_held(&ar->conf_mutex);
if (!ht_cap->ht_supported) if (!ht_cap->ht_supported)
return; return;
...@@ -905,6 +963,8 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, ...@@ -905,6 +963,8 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
u32 uapsd = 0; u32 uapsd = 0;
u32 max_sp = 0; u32 max_sp = 0;
lockdep_assert_held(&ar->conf_mutex);
if (sta->wme) if (sta->wme)
arg->peer_flags |= WMI_PEER_QOS; arg->peer_flags |= WMI_PEER_QOS;
...@@ -1056,6 +1116,8 @@ static int ath10k_peer_assoc(struct ath10k *ar, ...@@ -1056,6 +1116,8 @@ static int ath10k_peer_assoc(struct ath10k *ar,
{ {
struct wmi_peer_assoc_complete_arg arg; struct wmi_peer_assoc_complete_arg arg;
lockdep_assert_held(&ar->conf_mutex);
memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg)); memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &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, ...@@ -1079,6 +1141,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
struct ieee80211_sta *ap_sta; struct ieee80211_sta *ap_sta;
int ret; int ret;
lockdep_assert_held(&ar->conf_mutex);
rcu_read_lock(); rcu_read_lock();
ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
...@@ -1119,6 +1183,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, ...@@ -1119,6 +1183,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
int ret; int ret;
lockdep_assert_held(&ar->conf_mutex);
/* /*
* For some reason, calling VDEV-DOWN before VDEV-STOP * For some reason, calling VDEV-DOWN before VDEV-STOP
* makes the FW to send frames via HTT after disassociation. * 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, ...@@ -1152,6 +1218,8 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
{ {
int ret = 0; int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
ret = ath10k_peer_assoc(ar, arvif, sta, NULL); ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
if (ret) { if (ret) {
ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr); 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, ...@@ -1172,6 +1240,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
{ {
int ret = 0; int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
ret = ath10k_clear_peer_keys(arvif, sta->addr); ret = ath10k_clear_peer_keys(arvif, sta->addr);
if (ret) { if (ret) {
ath10k_warn("could not clear all peer wep keys (%d)\n", 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) ...@@ -1198,6 +1268,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
int ret; int ret;
int i; int i;
lockdep_assert_held(&ar->conf_mutex);
bands = hw->wiphy->bands; bands = hw->wiphy->bands;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) { for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!bands[band]) if (!bands[band])
...@@ -1276,21 +1348,19 @@ static int ath10k_update_channel_list(struct ath10k *ar) ...@@ -1276,21 +1348,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
return ret; return ret;
} }
static void ath10k_reg_notifier(struct wiphy *wiphy, static void ath10k_regd_update(struct ath10k *ar)
struct regulatory_request *request)
{ {
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct reg_dmn_pair_mapping *regpair; struct reg_dmn_pair_mapping *regpair;
struct ath10k *ar = hw->priv;
int ret; int ret;
ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); lockdep_assert_held(&ar->conf_mutex);
ret = ath10k_update_channel_list(ar); ret = ath10k_update_channel_list(ar);
if (ret) if (ret)
ath10k_warn("could not update channel list (%d)\n", ret); ath10k_warn("could not update channel list (%d)\n", ret);
regpair = ar->ath_common.regulatory.regpair; regpair = ar->ath_common.regulatory.regpair;
/* Target allows setting up per-band regdomain but ath_common provides /* Target allows setting up per-band regdomain but ath_common provides
* a combined one only */ * a combined one only */
ret = ath10k_wmi_pdev_set_regdomain(ar, ret = ath10k_wmi_pdev_set_regdomain(ar,
...@@ -1303,6 +1373,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, ...@@ -1303,6 +1373,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
ath10k_warn("could not set pdev regdomain (%d)\n", ret); 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 */ /* TX handlers */
/***************/ /***************/
...@@ -1397,15 +1481,15 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) ...@@ -1397,15 +1481,15 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
int ret; int ret;
if (ieee80211_is_mgmt(hdr->frame_control)) 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)) else if (ieee80211_is_nullfunc(hdr->frame_control))
/* FW does not report tx status properly for NullFunc frames /* FW does not report tx status properly for NullFunc frames
* unless they are sent through mgmt tx path. mac80211 sends * unless they are sent through mgmt tx path. mac80211 sends
* those frames when it detects link/beacon loss and depends on * those frames when it detects link/beacon loss and depends on
* the tx status to be correct. */ * the tx status to be correct. */
ret = ath10k_htt_mgmt_tx(ar->htt, skb); ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
else else
ret = ath10k_htt_tx(ar->htt, skb); ret = ath10k_htt_tx(&ar->htt, skb);
if (ret) { if (ret) {
ath10k_warn("tx failed (%d). dropping packet.\n", ret); ath10k_warn("tx failed (%d). dropping packet.\n", ret);
...@@ -1552,6 +1636,10 @@ static int ath10k_abort_scan(struct ath10k *ar) ...@@ -1552,6 +1636,10 @@ static int ath10k_abort_scan(struct ath10k *ar)
ret = ath10k_wmi_stop_scan(ar, &arg); ret = ath10k_wmi_stop_scan(ar, &arg);
if (ret) { if (ret) {
ath10k_warn("could not submit wmi stop scan (%d)\n", 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; return -EIO;
} }
...@@ -1645,10 +1733,14 @@ static void ath10k_tx(struct ieee80211_hw *hw, ...@@ -1645,10 +1733,14 @@ static void ath10k_tx(struct ieee80211_hw *hw,
tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
} }
/* 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_qos_workaround(hw, control, skb);
ath10k_tx_h_update_wep_key(skb); ath10k_tx_h_update_wep_key(skb);
ath10k_tx_h_add_p2p_noa_ie(ar, skb); ath10k_tx_h_add_p2p_noa_ie(ar, skb);
ath10k_tx_h_seq_no(skb); ath10k_tx_h_seq_no(skb);
}
memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb))); memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id; ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
...@@ -1673,10 +1765,57 @@ static void ath10k_tx(struct ieee80211_hw *hw, ...@@ -1673,10 +1765,57 @@ static void ath10k_tx(struct ieee80211_hw *hw,
/* /*
* Initialize various parameters with default vaules. * 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) static int ath10k_start(struct ieee80211_hw *hw)
{ {
struct ath10k *ar = hw->priv; 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); ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
if (ret) if (ret)
...@@ -1688,6 +1827,10 @@ static int ath10k_start(struct ieee80211_hw *hw) ...@@ -1688,6 +1827,10 @@ static int ath10k_start(struct ieee80211_hw *hw)
ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
ret); ret);
ath10k_regd_update(ar);
exit:
mutex_unlock(&ar->conf_mutex);
return 0; return 0;
} }
...@@ -1695,18 +1838,48 @@ static void ath10k_stop(struct ieee80211_hw *hw) ...@@ -1695,18 +1838,48 @@ static void ath10k_stop(struct ieee80211_hw *hw)
{ {
struct ath10k *ar = hw->priv; 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); 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; 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 ath10k *ar = hw->priv;
struct ieee80211_conf *conf = &hw->conf; struct ieee80211_conf *conf = &hw->conf;
int ret = 0; int ret = 0;
u32 flags;
mutex_lock(&ar->conf_mutex); mutex_lock(&ar->conf_mutex);
...@@ -1718,18 +1891,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) ...@@ -1718,18 +1891,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
spin_unlock_bh(&ar->data_lock); spin_unlock_bh(&ar->data_lock);
} }
if (changed & IEEE80211_CONF_CHANGE_PS) { if (changed & IEEE80211_CONF_CHANGE_PS)
memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); ath10k_config_ps(ar);
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_MONITOR) { if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
if (conf->flags & IEEE80211_CONF_MONITOR) if (conf->flags & IEEE80211_CONF_MONITOR)
...@@ -1738,6 +1901,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) ...@@ -1738,6 +1901,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
ret = ath10k_monitor_destroy(ar); ret = ath10k_monitor_destroy(ar);
} }
ath10k_wmi_flush_tx(ar);
mutex_unlock(&ar->conf_mutex); mutex_unlock(&ar->conf_mutex);
return ret; return ret;
} }
...@@ -1859,6 +2023,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ...@@ -1859,6 +2023,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
ath10k_warn("Failed to set PSPOLL count: %d\n", ret); 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) if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
ar->monitor_present = true; ar->monitor_present = true;
...@@ -2363,6 +2537,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, ...@@ -2363,6 +2537,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
u32 value = 0; u32 value = 0;
int ret = 0; int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
if (arvif->vdev_type != WMI_VDEV_TYPE_STA) if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
return 0; return 0;
...@@ -2558,11 +2734,16 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) ...@@ -2558,11 +2734,16 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
u32 rts = ar_iter->ar->hw->wiphy->rts_threshold; 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, /* During HW reconfiguration mac80211 reports all interfaces that were
WMI_VDEV_PARAM_RTS_THRESHOLD, * running until reconfiguration was started. Since FW doesn't have any
rts); * 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) if (ar_iter->ret)
ath10k_warn("Failed to set RTS threshold for VDEV: %d\n", ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
arvif->vdev_id); arvif->vdev_id);
...@@ -2581,7 +2762,8 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) ...@@ -2581,7 +2762,8 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
ar_iter.ar = ar; ar_iter.ar = ar;
mutex_lock(&ar->conf_mutex); mutex_lock(&ar->conf_mutex);
ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, ieee80211_iterate_active_interfaces_atomic(
hw, IEEE80211_IFACE_ITER_NORMAL,
ath10k_set_rts_iter, &ar_iter); ath10k_set_rts_iter, &ar_iter);
mutex_unlock(&ar->conf_mutex); mutex_unlock(&ar->conf_mutex);
...@@ -2593,17 +2775,17 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) ...@@ -2593,17 +2775,17 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
struct ath10k_generic_iter *ar_iter = data; struct ath10k_generic_iter *ar_iter = data;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
u32 frag = ar_iter->ar->hw->wiphy->frag_threshold; u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
int ret;
frag = clamp_t(u32, frag, lockdep_assert_held(&arvif->ar->conf_mutex);
ATH10K_FRAGMT_THRESHOLD_MIN,
ATH10K_FRAGMT_THRESHOLD_MAX);
ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, /* During HW reconfiguration mac80211 reports all interfaces that were
WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, * running until reconfiguration was started. Since FW doesn't have any
frag); * 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) if (ar_iter->ret)
ath10k_warn("Failed to set frag threshold for VDEV: %d\n", ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
arvif->vdev_id); arvif->vdev_id);
...@@ -2622,7 +2804,8 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) ...@@ -2622,7 +2804,8 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
ar_iter.ar = ar; ar_iter.ar = ar;
mutex_lock(&ar->conf_mutex); mutex_lock(&ar->conf_mutex);
ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, ieee80211_iterate_active_interfaces_atomic(
hw, IEEE80211_IFACE_ITER_NORMAL,
ath10k_set_frag_iter, &ar_iter); ath10k_set_frag_iter, &ar_iter);
mutex_unlock(&ar->conf_mutex); mutex_unlock(&ar->conf_mutex);
...@@ -2632,6 +2815,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) ...@@ -2632,6 +2815,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) static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
{ {
struct ath10k *ar = hw->priv; struct ath10k *ar = hw->priv;
bool skip;
int ret; int ret;
/* mac80211 doesn't care if we really xmit queued frames or not /* mac80211 doesn't care if we really xmit queued frames or not
...@@ -2639,16 +2823,29 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) ...@@ -2639,16 +2823,29 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
if (drop) if (drop)
return; 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; bool empty;
spin_lock_bh(&ar->htt->tx_lock);
empty = bitmap_empty(ar->htt->used_msdu_ids, spin_lock_bh(&ar->htt.tx_lock);
ar->htt->max_num_pending_tx); empty = bitmap_empty(ar->htt.used_msdu_ids,
spin_unlock_bh(&ar->htt->tx_lock); ar->htt.max_num_pending_tx);
(empty); spin_unlock_bh(&ar->htt.tx_lock);
skip = (ar->state == ATH10K_STATE_WEDGED);
(empty || skip);
}), ATH10K_FLUSH_TIMEOUT_HZ); }), ATH10K_FLUSH_TIMEOUT_HZ);
if (ret <= 0)
if (ret <= 0 || skip)
ath10k_warn("tx not flushed\n"); ath10k_warn("tx not flushed\n");
skip:
mutex_unlock(&ar->conf_mutex);
} }
/* TODO: Implement this function properly /* TODO: Implement this function properly
...@@ -2660,6 +2857,83 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw) ...@@ -2660,6 +2857,83 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
return 1; 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 const struct ieee80211_ops ath10k_ops = { static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_tx, .tx = ath10k_tx,
.start = ath10k_start, .start = ath10k_start,
...@@ -2680,6 +2954,11 @@ static const struct ieee80211_ops ath10k_ops = { ...@@ -2680,6 +2954,11 @@ static const struct ieee80211_ops ath10k_ops = {
.set_frag_threshold = ath10k_set_frag_threshold, .set_frag_threshold = ath10k_set_frag_threshold,
.flush = ath10k_flush, .flush = ath10k_flush,
.tx_last_beacon = ath10k_tx_last_beacon, .tx_last_beacon = ath10k_tx_last_beacon,
.restart_complete = ath10k_restart_complete,
#ifdef CONFIG_PM
.suspend = ath10k_suspend,
.resume = ath10k_resume,
#endif
}; };
#define RATETAB_ENT(_rate, _rateid, _flags) { \ #define RATETAB_ENT(_rate, _rateid, _flags) { \
...@@ -2948,8 +3227,10 @@ int ath10k_mac_register(struct ath10k *ar) ...@@ -2948,8 +3227,10 @@ int ath10k_mac_register(struct ath10k *ar)
channels = kmemdup(ath10k_2ghz_channels, channels = kmemdup(ath10k_2ghz_channels,
sizeof(ath10k_2ghz_channels), sizeof(ath10k_2ghz_channels),
GFP_KERNEL); GFP_KERNEL);
if (!channels) if (!channels) {
return -ENOMEM; ret = -ENOMEM;
goto err_free;
}
band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels); band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
...@@ -2968,11 +3249,8 @@ int ath10k_mac_register(struct ath10k *ar) ...@@ -2968,11 +3249,8 @@ int ath10k_mac_register(struct ath10k *ar)
sizeof(ath10k_5ghz_channels), sizeof(ath10k_5ghz_channels),
GFP_KERNEL); GFP_KERNEL);
if (!channels) { if (!channels) {
if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) { ret = -ENOMEM;
band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; goto err_free;
kfree(band->channels);
}
return -ENOMEM;
} }
band = &ar->mac.sbands[IEEE80211_BAND_5GHZ]; band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
...@@ -3036,25 +3314,30 @@ int ath10k_mac_register(struct ath10k *ar) ...@@ -3036,25 +3314,30 @@ int ath10k_mac_register(struct ath10k *ar)
ath10k_reg_notifier); ath10k_reg_notifier);
if (ret) { if (ret) {
ath10k_err("Regulatory initialization failed\n"); ath10k_err("Regulatory initialization failed\n");
return ret; goto err_free;
} }
ret = ieee80211_register_hw(ar->hw); ret = ieee80211_register_hw(ar->hw);
if (ret) { if (ret) {
ath10k_err("ieee80211 registration failed: %d\n", ret); ath10k_err("ieee80211 registration failed: %d\n", ret);
return ret; goto err_free;
} }
if (!ath_is_world_regd(&ar->ath_common.regulatory)) { if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
ret = regulatory_hint(ar->hw->wiphy, ret = regulatory_hint(ar->hw->wiphy,
ar->ath_common.regulatory.alpha2); ar->ath_common.regulatory.alpha2);
if (ret) if (ret)
goto exit; goto err_unregister;
} }
return 0; return 0;
exit:
err_unregister:
ieee80211_unregister_hw(ar->hw); 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; return ret;
} }
......
...@@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); ...@@ -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_reset_scan(unsigned long ptr);
void ath10k_offchan_tx_purge(struct ath10k *ar); void ath10k_offchan_tx_purge(struct ath10k *ar);
void ath10k_offchan_tx_work(struct work_struct *work); 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) static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{ {
......
...@@ -54,6 +54,8 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, ...@@ -54,6 +54,8 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
int num); int num);
static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info); 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_stop_ce(struct ath10k *ar);
static void ath10k_pci_device_reset(struct ath10k *ar);
static int ath10k_pci_reset_target(struct ath10k *ar);
static const struct ce_attr host_ce_config_wlan[] = { static const struct ce_attr host_ce_config_wlan[] = {
/* host->target HTC control and raw streams */ /* host->target HTC control and raw streams */
...@@ -718,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar) ...@@ -718,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
reg_dump_values[i + 1], reg_dump_values[i + 1],
reg_dump_values[i + 2], reg_dump_values[i + 2],
reg_dump_values[i + 3]); 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, static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
...@@ -744,7 +748,7 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, ...@@ -744,7 +748,7 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
ath10k_ce_per_engine_service(ar, pipe); ath10k_ce_per_engine_service(ar, pipe);
} }
static void ath10k_pci_hif_post_init(struct ath10k *ar, static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
struct ath10k_hif_cb *callbacks) struct ath10k_hif_cb *callbacks)
{ {
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
...@@ -1263,7 +1267,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ...@@ -1263,7 +1267,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
ath10k_pci_process_ce(ar); ath10k_pci_process_ce(ar);
ath10k_pci_cleanup_ce(ar); ath10k_pci_cleanup_ce(ar);
ath10k_pci_buffer_cleanup(ar); ath10k_pci_buffer_cleanup(ar);
ath10k_pci_ce_deinit(ar);
} }
static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
...@@ -1735,6 +1738,115 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) ...@@ -1735,6 +1738,115 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
ath10k_pci_sleep(ar); ath10k_pci_sleep(ar);
} }
static int ath10k_pci_hif_power_up(struct ath10k *ar)
{
int ret;
/*
* 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;
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_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 (!ath10k_target_ps)
ath10k_do_pci_sleep(ar);
err:
return ret;
}
static void ath10k_pci_hif_power_down(struct ath10k *ar)
{
ath10k_pci_ce_deinit(ar);
if (!ath10k_target_ps)
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 = { static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
.send_head = ath10k_pci_hif_send_head, .send_head = ath10k_pci_hif_send_head,
.exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg, .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg,
...@@ -1743,8 +1855,14 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = { ...@@ -1743,8 +1855,14 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
.map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe, .map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe,
.get_default_pipe = ath10k_pci_hif_get_default_pipe, .get_default_pipe = ath10k_pci_hif_get_default_pipe,
.send_complete_check = ath10k_pci_hif_send_complete_check, .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, .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) static void ath10k_pci_ce_tasklet(unsigned long ptr)
...@@ -2059,9 +2177,9 @@ static int ath10k_pci_reset_target(struct ath10k *ar) ...@@ -2059,9 +2177,9 @@ static int ath10k_pci_reset_target(struct ath10k *ar)
return 0; 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; void __iomem *mem = ar_pci->mem;
int i; int i;
u32 val; u32 val;
...@@ -2118,7 +2236,7 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) ...@@ -2118,7 +2236,7 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
case ATH10K_PCI_FEATURE_MSI_X: case ATH10K_PCI_FEATURE_MSI_X:
ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n"); ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
break; 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"); ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
break; break;
} }
...@@ -2145,7 +2263,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ...@@ -2145,7 +2263,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
switch (pci_dev->device) { switch (pci_dev->device) {
case QCA988X_1_0_DEVICE_ID: 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; break;
case QCA988X_2_0_DEVICE_ID: case QCA988X_2_0_DEVICE_ID:
set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features); set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
...@@ -2158,8 +2276,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ...@@ -2158,8 +2276,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
ath10k_pci_dump_features(ar_pci); ath10k_pci_dump_features(ar_pci);
ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI, ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops);
&ath10k_pci_hif_ops);
if (!ar) { if (!ar) {
ath10k_err("ath10k_core_create failed!\n"); ath10k_err("ath10k_core_create failed!\n");
ret = -EINVAL; ret = -EINVAL;
...@@ -2167,7 +2284,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ...@@ -2167,7 +2284,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
} }
/* Enable QCA988X_1.0 HW workarounds */ /* 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); spin_lock_init(&ar_pci->hw_v1_workaround_lock);
ar_pci->ar = ar; ar_pci->ar = ar;
...@@ -2247,54 +2364,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ...@@ -2247,54 +2364,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
goto err_iomap; 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); ret = ath10k_core_register(ar);
if (ret) { if (ret) {
ath10k_err("could not register driver core (%d)\n", ret); ath10k_err("could not register driver core (%d)\n", ret);
goto err_ce; goto err_intr;
} }
return 0; return 0;
err_ce:
ath10k_pci_ce_deinit(ar);
err_intr: err_intr:
ath10k_pci_stop_intr(ar); ath10k_pci_stop_intr(ar);
err_iomap: err_iomap:
...@@ -2345,128 +2422,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev) ...@@ -2345,128 +2422,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
kfree(ar_pci); 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); MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
static struct pci_driver ath10k_pci_driver = { static struct pci_driver ath10k_pci_driver = {
...@@ -2474,7 +2429,6 @@ static struct pci_driver ath10k_pci_driver = { ...@@ -2474,7 +2429,6 @@ static struct pci_driver ath10k_pci_driver = {
.id_table = ath10k_pci_id_table, .id_table = ath10k_pci_id_table,
.probe = ath10k_pci_probe, .probe = ath10k_pci_probe,
.remove = ath10k_pci_remove, .remove = ath10k_pci_remove,
.driver.pm = ATH10K_PCI_PM_OPS,
}; };
static int __init ath10k_pci_init(void) static int __init ath10k_pci_init(void)
......
...@@ -152,7 +152,7 @@ struct service_to_pipe { ...@@ -152,7 +152,7 @@ struct service_to_pipe {
enum ath10k_pci_features { enum ath10k_pci_features {
ATH10K_PCI_FEATURE_MSI_X = 0, ATH10K_PCI_FEATURE_MSI_X = 0,
ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND = 1, ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND = 1,
/* keep last */ /* keep last */
ATH10K_PCI_FEATURE_COUNT ATH10K_PCI_FEATURE_COUNT
...@@ -311,7 +311,7 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, ...@@ -311,7 +311,7 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
void __iomem *addr = ar_pci->mem; 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; unsigned long irq_flags;
spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags); spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
......
...@@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar) ...@@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar)
{ {
int ret; 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, ret = wait_event_timeout(ar->wmi.wq,
atomic_read(&ar->wmi.pending_tx_count) == 0, atomic_read(&ar->wmi.pending_tx_count) == 0,
5*HZ); 5*HZ);
...@@ -111,7 +118,7 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, ...@@ -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); 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) { if (status) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
atomic_dec(&ar->wmi.pending_tx_count); atomic_dec(&ar->wmi.pending_tx_count);
...@@ -501,7 +508,7 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, ...@@ -501,7 +508,7 @@ static void ath10k_wmi_update_tim(struct ath10k *ar,
ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies, ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
(u8 *)skb_tail_pointer(bcn) - ies); (u8 *)skb_tail_pointer(bcn) - ies);
if (!ie) { if (!ie) {
/* highly unlikely for mac80211 */ if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
ath10k_warn("no tim ie found;\n"); ath10k_warn("no tim ie found;\n");
return; return;
} }
...@@ -1114,7 +1121,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar) ...@@ -1114,7 +1121,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
/* connect to control service */ /* connect to control service */
conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; 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) { if (status) {
ath10k_warn("failed to connect to WMI CONTROL service status: %d\n", ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
status); status);
...@@ -1748,6 +1755,9 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, ...@@ -1748,6 +1755,9 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar,
if (arg->key_data) if (arg->key_data)
memcpy(cmd->key_data, arg->key_data, arg->key_len); 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); return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
} }
...@@ -2011,6 +2021,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, ...@@ -2011,6 +2021,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
cmd->peer_vht_rates.tx_mcs_set = cmd->peer_vht_rates.tx_mcs_set =
__cpu_to_le32(arg->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); return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
} }
...@@ -2079,3 +2092,22 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) ...@@ -2079,3 +2092,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); ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID); 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);
}
...@@ -416,6 +416,7 @@ enum wmi_cmd_id { ...@@ -416,6 +416,7 @@ enum wmi_cmd_id {
WMI_PDEV_FTM_INTG_CMDID, WMI_PDEV_FTM_INTG_CMDID,
WMI_VDEV_SET_KEEPALIVE_CMDID, WMI_VDEV_SET_KEEPALIVE_CMDID,
WMI_VDEV_GET_KEEPALIVE_CMDID, WMI_VDEV_GET_KEEPALIVE_CMDID,
WMI_FORCE_FW_HANG_CMDID,
/* GPIO Configuration */ /* GPIO Configuration */
WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO), WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
...@@ -2972,6 +2973,22 @@ struct wmi_sta_keepalive_cmd { ...@@ -2972,6 +2973,22 @@ struct wmi_sta_keepalive_cmd {
struct wmi_sta_keepalive_arp_resp arp_resp; struct wmi_sta_keepalive_arp_resp arp_resp;
} __packed; } __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_RTS_MAX 2347
#define ATH10K_FRAGMT_THRESHOLD_MIN 540 #define ATH10K_FRAGMT_THRESHOLD_MIN 540
#define ATH10K_FRAGMT_THRESHOLD_MAX 2346 #define ATH10K_FRAGMT_THRESHOLD_MAX 2346
...@@ -3048,5 +3065,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg); ...@@ -3048,5 +3065,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, int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
const struct wmi_pdev_set_wmm_params_arg *arg); 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_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_ */ #endif /* _WMI_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment