aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r--drivers/net/wireless/ath/ath.h13
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c92
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.h21
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c154
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h100
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c1154
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.h46
-rw-r--r--drivers/net/wireless/ath/ath10k/hif.h53
-rw-r--r--drivers/net/wireless/ath/ath10k/htc.c13
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h9
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c1256
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c11
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.h32
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c746
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.h6
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c579
-rw-r--r--drivers/net/wireless/ath/ath10k/spectral.c34
-rw-r--r--drivers/net/wireless/ath/ath10k/spectral.h8
-rw-r--r--drivers/net/wireless/ath/ath10k/trace.h207
-rw-r--r--drivers/net/wireless/ath/ath10k/txrx.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c1179
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h401
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c6
-rw-r--r--drivers/net/wireless/ath/ath5k/qcu.c8
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c4
-rw-r--r--drivers/net/wireless/ath/ath6kl/common.h2
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.c28
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.h13
-rw-r--r--drivers/net/wireless/ath/ath6kl/usb.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig7
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile9
-rw-r--r--drivers/net/wireless/ath/ath9k/ar5008_phy.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_calib.c42
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_mac.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_phy.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_calib.c11
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c56
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_hw.c54
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_mac.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c181
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_rtt.h36
-rw-r--r--drivers/net/wireless/ath/ath9k/ar953x_initvals.h498
-rw-r--r--drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h8
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h144
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h27
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/calib.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/calib.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/channel.c338
-rw-r--r--drivers/net/wireless/ath/ath9k/common-spectral.c (renamed from drivers/net/wireless/ath/ath9k/spectral.c)165
-rw-r--r--drivers/net/wireless/ath/ath9k/common-spectral.h (renamed from drivers/net/wireless/ath/ath9k/spectral.h)29
-rw-r--r--drivers/net/wireless/ath/ath9k/common.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/common.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c60
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_def.c31
-rw-r--r--drivers/net/wireless/ath/ath9k/gpio.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h5
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_debug.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c25
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c13
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c16
-rw-r--r--drivers/net/wireless/ath/ath9k/hw-ops.h7
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c98
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h43
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c73
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c12
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c361
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c10
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/reg.h38
-rw-r--r--drivers/net/wireless/ath/ath9k/tx99.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c37
-rw-r--r--drivers/net/wireless/ath/carl9170/phy.c4
-rw-r--r--drivers/net/wireless/ath/dfs_pattern_detector.c4
-rw-r--r--drivers/net/wireless/ath/wcn36xx/main.c7
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c5
-rw-r--r--drivers/net/wireless/ath/wil6210/debug.c17
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c8
-rw-r--r--drivers/net/wireless/ath/wil6210/fw.c1
-rw-r--r--drivers/net/wireless/ath/wil6210/fw_inc.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c30
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c136
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c18
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.h4
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h26
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c10
90 files changed, 6035 insertions, 2944 deletions
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index 86907e5ba6ca..ccba4fea7269 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -135,6 +135,11 @@ struct ath_ops {
struct ath_common;
struct ath_bus_ops;
+struct ath_ps_ops {
+ void (*wakeup)(struct ath_common *common);
+ void (*restore)(struct ath_common *common);
+};
+
struct ath_common {
void *ah;
void *priv;
@@ -148,7 +153,7 @@ struct ath_common {
u16 cachelsz;
u16 curaid;
u8 macaddr[ETH_ALEN];
- u8 curbssid[ETH_ALEN];
+ u8 curbssid[ETH_ALEN] __aligned(2);
u8 bssidmask[ETH_ALEN];
u32 rx_bufsize;
@@ -169,6 +174,7 @@ struct ath_common {
struct ath_regulatory reg_world_copy;
const struct ath_ops *ops;
const struct ath_bus_ops *bus_ops;
+ const struct ath_ps_ops *ps_ops;
bool btcoex_enabled;
bool disable_ani;
@@ -178,6 +184,11 @@ struct ath_common {
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
};
+static inline const struct ath_ps_ops *ath_ps_ops(struct ath_common *common)
+{
+ return common->ps_ops;
+}
+
struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
u32 len,
gfp_t gfp_mask);
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index 101cadb6e4ba..a156e6e48708 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -443,12 +443,12 @@ int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr)
* Guts of ath10k_ce_completed_recv_next.
* The caller takes responsibility for any necessary locking.
*/
-static int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
- void **per_transfer_contextp,
- u32 *bufferp,
- unsigned int *nbytesp,
- unsigned int *transfer_idp,
- unsigned int *flagsp)
+int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
+ void **per_transfer_contextp,
+ u32 *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ unsigned int *flagsp)
{
struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
unsigned int nentries_mask = dest_ring->nentries_mask;
@@ -558,6 +558,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
/* sanity */
dest_ring->per_transfer_context[sw_index] = NULL;
+ desc->nbytes = 0;
/* Update sw_index */
sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
@@ -576,11 +577,11 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
* Guts of ath10k_ce_completed_send_next.
* The caller takes responsibility for any necessary locking.
*/
-static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
- void **per_transfer_contextp,
- u32 *bufferp,
- unsigned int *nbytesp,
- unsigned int *transfer_idp)
+int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
+ void **per_transfer_contextp,
+ u32 *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp)
{
struct ath10k_ce_ring *src_ring = ce_state->src_ring;
u32 ctrl_addr = ce_state->ctrl_addr;
@@ -817,7 +818,10 @@ void ath10k_ce_enable_interrupts(struct ath10k *ar)
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ce_id;
- for (ce_id = 0; ce_id < CE_COUNT; ce_id++)
+ /* Skip the last copy engine, CE7 the diagnostic window, as that
+ * uses polling and isn't initialized for interrupts.
+ */
+ for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++)
ath10k_ce_per_engine_handler_adjust(&ar_pci->ce_states[ce_id]);
}
@@ -832,8 +836,8 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
nentries = roundup_pow_of_two(attr->src_nentries);
- memset(src_ring->per_transfer_context, 0,
- nentries * sizeof(*src_ring->per_transfer_context));
+ memset(src_ring->base_addr_owner_space, 0,
+ nentries * sizeof(struct ce_desc));
src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
src_ring->sw_index &= src_ring->nentries_mask;
@@ -869,8 +873,8 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
nentries = roundup_pow_of_two(attr->dest_nentries);
- memset(dest_ring->per_transfer_context, 0,
- nentries * sizeof(*dest_ring->per_transfer_context));
+ memset(dest_ring->base_addr_owner_space, 0,
+ nentries * sizeof(struct ce_desc));
dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
dest_ring->sw_index &= dest_ring->nentries_mask;
@@ -1020,37 +1024,10 @@ ath10k_ce_alloc_dest_ring(struct ath10k *ar, unsigned int ce_id,
* initialized by software/firmware.
*/
int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
- const struct ce_attr *attr,
- void (*send_cb)(struct ath10k_ce_pipe *),
- void (*recv_cb)(struct ath10k_ce_pipe *))
+ const struct ce_attr *attr)
{
- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
int ret;
- /*
- * Make sure there's enough CE ringbuffer entries for HTT TX to avoid
- * additional TX locking checks.
- *
- * For the lack of a better place do the check here.
- */
- BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC >
- (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
- BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC >
- (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
-
- spin_lock_bh(&ar_pci->ce_lock);
- ce_state->ar = ar;
- ce_state->id = ce_id;
- ce_state->ctrl_addr = ath10k_ce_base_address(ce_id);
- ce_state->attr_flags = attr->flags;
- ce_state->src_sz_max = attr->src_sz_max;
- if (attr->src_nentries)
- ce_state->send_cb = send_cb;
- if (attr->dest_nentries)
- ce_state->recv_cb = recv_cb;
- spin_unlock_bh(&ar_pci->ce_lock);
-
if (attr->src_nentries) {
ret = ath10k_ce_init_src_ring(ar, ce_id, attr);
if (ret) {
@@ -1098,12 +1075,37 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id)
}
int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
- const struct ce_attr *attr)
+ const struct ce_attr *attr,
+ void (*send_cb)(struct ath10k_ce_pipe *),
+ void (*recv_cb)(struct ath10k_ce_pipe *))
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
int ret;
+ /*
+ * Make sure there's enough CE ringbuffer entries for HTT TX to avoid
+ * additional TX locking checks.
+ *
+ * For the lack of a better place do the check here.
+ */
+ BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC >
+ (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
+ BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC >
+ (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
+
+ ce_state->ar = ar;
+ ce_state->id = ce_id;
+ ce_state->ctrl_addr = ath10k_ce_base_address(ce_id);
+ ce_state->attr_flags = attr->flags;
+ ce_state->src_sz_max = attr->src_sz_max;
+
+ if (attr->src_nentries)
+ ce_state->send_cb = send_cb;
+
+ if (attr->dest_nentries)
+ ce_state->recv_cb = recv_cb;
+
if (attr->src_nentries) {
ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr);
if (IS_ERR(ce_state->src_ring)) {
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
index 329b7340fa72..617a151e8ce4 100644
--- a/drivers/net/wireless/ath/ath10k/ce.h
+++ b/drivers/net/wireless/ath/ath10k/ce.h
@@ -192,15 +192,21 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
unsigned int *nbytesp,
unsigned int *transfer_idp);
+int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
+ void **per_transfer_contextp,
+ u32 *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp);
+
/*==================CE Engine Initialization=======================*/
int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
- const struct ce_attr *attr,
- void (*send_cb)(struct ath10k_ce_pipe *),
- void (*recv_cb)(struct ath10k_ce_pipe *));
+ const struct ce_attr *attr);
void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id);
int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
- const struct ce_attr *attr);
+ const struct ce_attr *attr,
+ void (*send_cb)(struct ath10k_ce_pipe *),
+ void (*recv_cb)(struct ath10k_ce_pipe *));
void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id);
/*==================CE Engine Shutdown=======================*/
@@ -213,6 +219,13 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
u32 *bufferp);
+int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
+ void **per_transfer_contextp,
+ u32 *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ unsigned int *flagsp);
+
/*
* Support clean shutdown by allowing the caller to cancel
* pending sends. Target DMA must be stopped before using
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index cee18c89d7f2..7762061a1944 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -31,12 +31,17 @@
unsigned int ath10k_debug_mask;
static bool uart_print;
static unsigned int ath10k_p2p;
+static bool skip_otp;
+
module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
module_param(uart_print, bool, 0644);
module_param_named(p2p, ath10k_p2p, uint, 0644);
+module_param(skip_otp, bool, 0644);
+
MODULE_PARM_DESC(debug_mask, "Debugging mask");
MODULE_PARM_DESC(uart_print, "Uart target debugging");
MODULE_PARM_DESC(p2p, "Enable ath10k P2P support");
+MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
static const struct ath10k_hw_params ath10k_hw_params_list[] = {
{
@@ -138,7 +143,8 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
return fw;
}
-static int ath10k_push_board_ext_data(struct ath10k *ar)
+static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data,
+ size_t data_len)
{
u32 board_data_size = QCA988X_BOARD_DATA_SZ;
u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
@@ -159,14 +165,14 @@ static int ath10k_push_board_ext_data(struct ath10k *ar)
if (board_ext_data_addr == 0)
return 0;
- if (ar->board_len != (board_data_size + board_ext_data_size)) {
+ if (data_len != (board_data_size + board_ext_data_size)) {
ath10k_err(ar, "invalid board (ext) data sizes %zu != %d+%d\n",
- ar->board_len, board_data_size, board_ext_data_size);
+ data_len, board_data_size, board_ext_data_size);
return -EINVAL;
}
ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
- ar->board_data + board_data_size,
+ data + board_data_size,
board_ext_data_size);
if (ret) {
ath10k_err(ar, "could not write board ext data (%d)\n", ret);
@@ -184,13 +190,14 @@ static int ath10k_push_board_ext_data(struct ath10k *ar)
return 0;
}
-static int ath10k_download_board_data(struct ath10k *ar)
+static int ath10k_download_board_data(struct ath10k *ar, const void *data,
+ size_t data_len)
{
u32 board_data_size = QCA988X_BOARD_DATA_SZ;
u32 address;
int ret;
- ret = ath10k_push_board_ext_data(ar);
+ ret = ath10k_push_board_ext_data(ar, data, data_len);
if (ret) {
ath10k_err(ar, "could not push board ext data (%d)\n", ret);
goto exit;
@@ -202,9 +209,9 @@ static int ath10k_download_board_data(struct ath10k *ar)
goto exit;
}
- ret = ath10k_bmi_write_memory(ar, address, ar->board_data,
+ ret = ath10k_bmi_write_memory(ar, address, data,
min_t(u32, board_data_size,
- ar->board_len));
+ data_len));
if (ret) {
ath10k_err(ar, "could not write board data (%d)\n", ret);
goto exit;
@@ -220,11 +227,39 @@ exit:
return ret;
}
+static int ath10k_download_cal_file(struct ath10k *ar)
+{
+ int ret;
+
+ if (!ar->cal_file)
+ return -ENOENT;
+
+ if (IS_ERR(ar->cal_file))
+ return PTR_ERR(ar->cal_file);
+
+ ret = ath10k_download_board_data(ar, ar->cal_file->data,
+ ar->cal_file->size);
+ if (ret) {
+ ath10k_err(ar, "failed to download cal_file data: %d\n", ret);
+ return ret;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cal file downloaded\n");
+
+ return 0;
+}
+
static int ath10k_download_and_run_otp(struct ath10k *ar)
{
u32 result, address = ar->hw_params.patch_load_addr;
int ret;
+ ret = ath10k_download_board_data(ar, ar->board_data, ar->board_len);
+ if (ret) {
+ ath10k_err(ar, "failed to download board data: %d\n", ret);
+ return ret;
+ }
+
/* OTP is optional */
if (!ar->otp_data || !ar->otp_len) {
@@ -250,7 +285,7 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
- if (result != 0) {
+ if (!skip_otp && result != 0) {
ath10k_err(ar, "otp calibration failed: %d", result);
return -EINVAL;
}
@@ -308,6 +343,9 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar)
if (ar->firmware && !IS_ERR(ar->firmware))
release_firmware(ar->firmware);
+ if (ar->cal_file && !IS_ERR(ar->cal_file))
+ release_firmware(ar->cal_file);
+
ar->board = NULL;
ar->board_data = NULL;
ar->board_len = 0;
@@ -319,6 +357,27 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar)
ar->firmware = NULL;
ar->firmware_data = NULL;
ar->firmware_len = 0;
+
+ ar->cal_file = NULL;
+}
+
+static int ath10k_fetch_cal_file(struct ath10k *ar)
+{
+ char filename[100];
+
+ /* cal-<bus>-<id>.bin */
+ scnprintf(filename, sizeof(filename), "cal-%s-%s.bin",
+ ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));
+
+ ar->cal_file = ath10k_fetch_fw_file(ar, ATH10K_FW_DIR, filename);
+ if (IS_ERR(ar->cal_file))
+ /* calibration file is optional, don't print any warnings */
+ return PTR_ERR(ar->cal_file);
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "found calibration file %s/%s\n",
+ ATH10K_FW_DIR, filename);
+
+ return 0;
}
static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar)
@@ -562,6 +621,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
{
int ret;
+ /* calibration file is optional, don't check for any errors */
+ ath10k_fetch_cal_file(ar);
+
ar->fw_api = 3;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
@@ -589,30 +651,32 @@ success:
return 0;
}
-static int ath10k_init_download_firmware(struct ath10k *ar,
- enum ath10k_firmware_mode mode)
+static int ath10k_download_cal_data(struct ath10k *ar)
{
int ret;
- ret = ath10k_download_board_data(ar);
- if (ret) {
- ath10k_err(ar, "failed to download board data: %d\n", ret);
- return ret;
+ ret = ath10k_download_cal_file(ar);
+ if (ret == 0) {
+ ar->cal_mode = ATH10K_CAL_MODE_FILE;
+ goto done;
}
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "boot did not find a calibration file, try OTP next: %d\n",
+ ret);
+
ret = ath10k_download_and_run_otp(ar);
if (ret) {
ath10k_err(ar, "failed to run otp: %d\n", ret);
return ret;
}
- ret = ath10k_download_fw(ar, mode);
- if (ret) {
- ath10k_err(ar, "failed to download firmware: %d\n", ret);
- return ret;
- }
+ ar->cal_mode = ATH10K_CAL_MODE_OTP;
- return ret;
+done:
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using calibration mode %s\n",
+ ath10k_cal_mode_str(ar->cal_mode));
+ return 0;
}
static int ath10k_init_uart(struct ath10k *ar)
@@ -685,6 +749,25 @@ static void ath10k_core_restart(struct work_struct *work)
{
struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+ set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
+
+ /* Place a barrier to make sure the compiler doesn't reorder
+ * CRASH_FLUSH and calling other functions.
+ */
+ barrier();
+
+ ieee80211_stop_queues(ar->hw);
+ ath10k_drain_tx(ar);
+ complete_all(&ar->scan.started);
+ complete_all(&ar->scan.completed);
+ complete_all(&ar->scan.on_channel);
+ complete_all(&ar->offchan_tx_completed);
+ complete_all(&ar->install_key_done);
+ complete_all(&ar->vdev_setup_done);
+ wake_up(&ar->htt.empty_tx_wq);
+ wake_up(&ar->wmi.tx_credits_wq);
+ wake_up(&ar->peer_mapping_wq);
+
mutex_lock(&ar->conf_mutex);
switch (ar->state) {
@@ -716,12 +799,25 @@ static void ath10k_core_restart(struct work_struct *work)
mutex_unlock(&ar->conf_mutex);
}
+static void ath10k_core_init_max_sta_count(struct ath10k *ar)
+{
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+ ar->max_num_peers = TARGET_10X_NUM_PEERS;
+ ar->max_num_stations = TARGET_10X_NUM_STATIONS;
+ } else {
+ ar->max_num_peers = TARGET_NUM_PEERS;
+ ar->max_num_stations = TARGET_NUM_STATIONS;
+ }
+}
+
int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
{
int status;
lockdep_assert_held(&ar->conf_mutex);
+ clear_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
+
ath10k_bmi_start(ar);
if (ath10k_init_configure_target(ar)) {
@@ -729,7 +825,11 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
goto err;
}
- status = ath10k_init_download_firmware(ar, mode);
+ status = ath10k_download_cal_data(ar);
+ if (status)
+ goto err;
+
+ status = ath10k_download_fw(ar, mode);
if (status)
goto err;
@@ -846,9 +946,9 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
goto err_hif_stop;
if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
- ar->free_vdev_map = (1 << TARGET_10X_NUM_VDEVS) - 1;
+ ar->free_vdev_map = (1LL << TARGET_10X_NUM_VDEVS) - 1;
else
- ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+ ar->free_vdev_map = (1LL << TARGET_NUM_VDEVS) - 1;
INIT_LIST_HEAD(&ar->arvifs);
@@ -946,6 +1046,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
return ret;
}
+ ath10k_core_init_max_sta_count(ar);
+
mutex_lock(&ar->conf_mutex);
ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL);
@@ -1084,6 +1186,7 @@ void ath10k_core_unregister(struct ath10k *ar)
EXPORT_SYMBOL(ath10k_core_unregister);
struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
+ enum ath10k_bus bus,
const struct ath10k_hif_ops *hif_ops)
{
struct ath10k *ar;
@@ -1100,6 +1203,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
ar->dev = dev;
ar->hif.ops = hif_ops;
+ ar->hif.bus = bus;
init_completion(&ar->scan.started);
init_completion(&ar->scan.completed);
@@ -1120,6 +1224,8 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
INIT_LIST_HEAD(&ar->peers);
init_waitqueue_head(&ar->peer_mapping_wq);
+ init_waitqueue_head(&ar->htt.empty_tx_wq);
+ init_waitqueue_head(&ar->wmi.tx_credits_wq);
init_completion(&ar->offchan_tx_completed);
INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index fe531ea6926c..514c219263a5 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -63,12 +63,28 @@
struct ath10k;
+enum ath10k_bus {
+ ATH10K_BUS_PCI,
+};
+
+static inline const char *ath10k_bus_str(enum ath10k_bus bus)
+{
+ switch (bus) {
+ case ATH10K_BUS_PCI:
+ return "pci";
+ }
+
+ return "unknown";
+}
+
struct ath10k_skb_cb {
dma_addr_t paddr;
+ u8 eid;
u8 vdev_id;
struct {
u8 tid;
+ u16 freq;
bool is_offchan;
struct ath10k_htt_txbuf *txbuf;
u32 txbuf_paddr;
@@ -96,8 +112,6 @@ struct ath10k_bmi {
bool done_sent;
};
-#define ATH10K_MAX_MEM_REQS 16
-
struct ath10k_mem_chunk {
void *vaddr;
dma_addr_t paddr;
@@ -110,22 +124,27 @@ struct ath10k_wmi {
struct completion service_ready;
struct completion unified_ready;
wait_queue_head_t tx_credits_wq;
+ DECLARE_BITMAP(svc_map, WMI_SERVICE_MAX);
struct wmi_cmd_map *cmd;
struct wmi_vdev_param_map *vdev_param;
struct wmi_pdev_param_map *pdev_param;
u32 num_mem_chunks;
- struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS];
+ struct ath10k_mem_chunk mem_chunks[WMI_MAX_MEM_REQS];
};
-struct ath10k_peer_stat {
+struct ath10k_fw_stats_peer {
+ struct list_head list;
+
u8 peer_macaddr[ETH_ALEN];
u32 peer_rssi;
u32 peer_tx_rate;
u32 peer_rx_rate; /* 10x only */
};
-struct ath10k_target_stats {
+struct ath10k_fw_stats_pdev {
+ struct list_head list;
+
/* PDEV stats */
s32 ch_noise_floor;
u32 tx_frame_count;
@@ -180,15 +199,11 @@ struct ath10k_target_stats {
s32 phy_errs;
s32 phy_err_drop;
s32 mpdu_errs;
+};
- /* VDEV STATS */
-
- /* PEER STATS */
- u8 peers;
- struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS];
-
- /* TODO: Beacon filter stats */
-
+struct ath10k_fw_stats {
+ struct list_head pdevs;
+ struct list_head peers;
};
struct ath10k_dfs_stats {
@@ -206,6 +221,8 @@ struct ath10k_peer {
int vdev_id;
u8 addr[ETH_ALEN];
DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
+
+ /* protected by ar->data_lock */
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
};
@@ -234,6 +251,8 @@ struct ath10k_vif {
struct sk_buff *beacon;
/* protected by data_lock */
bool beacon_sent;
+ void *beacon_buf;
+ dma_addr_t beacon_paddr;
struct ath10k *ar;
struct ieee80211_vif *vif;
@@ -273,6 +292,7 @@ struct ath10k_vif {
u8 force_sgi;
bool use_cts_prot;
int num_legacy_stations;
+ int txpower;
};
struct ath10k_vif_iter {
@@ -292,17 +312,19 @@ struct ath10k_fw_crash_data {
struct ath10k_debug {
struct dentry *debugfs_phy;
- struct ath10k_target_stats target_stats;
- DECLARE_BITMAP(wmi_service_bitmap, WMI_SERVICE_MAX);
-
- struct completion event_stats_compl;
+ struct ath10k_fw_stats fw_stats;
+ struct completion fw_stats_complete;
+ bool fw_stats_done;
unsigned long htt_stats_mask;
struct delayed_work htt_stats_dwork;
struct ath10k_dfs_stats dfs_stats;
struct ath_dfs_pool_stats dfs_pool_stats;
+ /* protected by conf_mutex */
u32 fw_dbglog_mask;
+ u32 pktlog_filter;
+ u32 reg_addr;
u8 htt_max_amsdu;
u8 htt_max_ampdu;
@@ -321,7 +343,7 @@ enum ath10k_state {
* 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(). */
+ * set the state to STATE_ON in reconfig_complete(). */
ATH10K_STATE_RESTARTING,
ATH10K_STATE_RESTARTED,
@@ -369,8 +391,30 @@ enum ath10k_dev_flags {
/* Indicates that ath10k device is during CAC phase of DFS */
ATH10K_CAC_RUNNING,
ATH10K_FLAG_CORE_REGISTERED,
+
+ /* Device has crashed and needs to restart. This indicates any pending
+ * waiters should immediately cancel instead of waiting for a time out.
+ */
+ ATH10K_FLAG_CRASH_FLUSH,
};
+enum ath10k_cal_mode {
+ ATH10K_CAL_MODE_FILE,
+ ATH10K_CAL_MODE_OTP,
+};
+
+static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode)
+{
+ switch (mode) {
+ case ATH10K_CAL_MODE_FILE:
+ return "file";
+ case ATH10K_CAL_MODE_OTP:
+ return "otp";
+ }
+
+ return "unknown";
+}
+
enum ath10k_scan_state {
ATH10K_SCAN_IDLE,
ATH10K_SCAN_STARTING,
@@ -421,6 +465,7 @@ struct ath10k {
bool p2p;
struct {
+ enum ath10k_bus bus;
const struct ath10k_hif_ops *ops;
} hif;
@@ -456,7 +501,10 @@ struct ath10k {
const void *firmware_data;
size_t firmware_len;
+ const struct firmware *cal_file;
+
int fw_api;
+ enum ath10k_cal_mode cal_mode;
struct {
struct completion started;
@@ -482,7 +530,7 @@ struct ath10k {
/* current operating channel definition */
struct cfg80211_chan_def chandef;
- int free_vdev_map;
+ unsigned long long free_vdev_map;
bool monitor;
int monitor_vdev_id;
bool monitor_started;
@@ -517,8 +565,12 @@ struct ath10k {
struct list_head peers;
wait_queue_head_t peer_mapping_wq;
- /* number of created peers; protected by data_lock */
+ /* protected by conf_mutex */
int num_peers;
+ int num_stations;
+
+ int max_num_peers;
+ int max_num_stations;
struct work_struct offchan_tx_work;
struct sk_buff_head offchan_tx_queue;
@@ -563,11 +615,19 @@ struct ath10k {
bool utf_monitor;
} testmode;
+ struct {
+ /* protected by data_lock */
+ u32 fw_crash_counter;
+ u32 fw_warm_reset_counter;
+ u32 fw_cold_reset_counter;
+ } stats;
+
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
+ enum ath10k_bus bus,
const struct ath10k_hif_ops *hif_ops);
void ath10k_core_destroy(struct ath10k *ar);
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 3756feba3223..a716758f14b0 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -17,12 +17,12 @@
#include <linux/module.h>
#include <linux/debugfs.h>
-#include <linux/version.h>
-#include <linux/vermagic.h>
#include <linux/vmalloc.h>
+#include <linux/utsname.h>
#include "core.h"
#include "debug.h"
+#include "hif.h"
/* ms */
#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
@@ -106,34 +106,37 @@ struct ath10k_dump_file_data {
u8 data[0];
} __packed;
-int ath10k_info(struct ath10k *ar, const char *fmt, ...)
+void ath10k_info(struct ath10k *ar, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = dev_info(ar->dev, "%pV", &vaf);
+ dev_info(ar->dev, "%pV", &vaf);
trace_ath10k_log_info(ar, &vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath10k_info);
void ath10k_print_driver_info(struct ath10k *ar)
{
- ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n",
+ ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d.%d.%d.%d cal %s max_sta %d\n",
ar->hw_params.name,
ar->target_version,
ar->chip_id,
ar->hw->wiphy->fw_version,
ar->fw_api,
ar->htt.target_version_major,
- ar->htt.target_version_minor);
+ ar->htt.target_version_minor,
+ ar->fw_version_major,
+ ar->fw_version_minor,
+ ar->fw_version_release,
+ ar->fw_version_build,
+ ath10k_cal_mode_str(ar->cal_mode),
+ ar->max_num_stations);
ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n",
config_enabled(CONFIG_ATH10K_DEBUG),
config_enabled(CONFIG_ATH10K_DEBUGFS),
@@ -143,25 +146,22 @@ void ath10k_print_driver_info(struct ath10k *ar)
}
EXPORT_SYMBOL(ath10k_print_driver_info);
-int ath10k_err(struct ath10k *ar, const char *fmt, ...)
+void ath10k_err(struct ath10k *ar, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = dev_err(ar->dev, "%pV", &vaf);
+ dev_err(ar->dev, "%pV", &vaf);
trace_ath10k_log_err(ar, &vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath10k_err);
-int ath10k_warn(struct ath10k *ar, const char *fmt, ...)
+void ath10k_warn(struct ath10k *ar, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
@@ -174,20 +174,11 @@ int ath10k_warn(struct ath10k *ar, const char *fmt, ...)
trace_ath10k_log_warn(ar, &vaf);
va_end(args);
-
- return 0;
}
EXPORT_SYMBOL(ath10k_warn);
#ifdef CONFIG_ATH10K_DEBUGFS
-void ath10k_debug_read_service_map(struct ath10k *ar,
- void *service_map,
- size_t map_size)
-{
- memcpy(ar->debug.wmi_service_bitmap, service_map, map_size);
-}
-
static ssize_t ath10k_read_wmi_services(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
@@ -209,8 +200,9 @@ static ssize_t ath10k_read_wmi_services(struct file *file,
if (len > buf_len)
len = buf_len;
+ spin_lock_bh(&ar->data_lock);
for (i = 0; i < WMI_SERVICE_MAX; i++) {
- enabled = test_bit(i, ar->debug.wmi_service_bitmap);
+ enabled = test_bit(i, ar->wmi.svc_map);
name = wmi_service_name(i);
if (!name) {
@@ -226,6 +218,7 @@ static ssize_t ath10k_read_wmi_services(struct file *file,
"%-40s %s\n",
name, enabled ? "enabled" : "-");
}
+ spin_unlock_bh(&ar->data_lock);
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
@@ -242,169 +235,182 @@ static const struct file_operations fops_wmi_services = {
.llseek = default_llseek,
};
-void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct wmi_stats_event *ev)
+static void ath10k_debug_fw_stats_pdevs_free(struct list_head *head)
{
- u8 *tmp = ev->data;
- struct ath10k_target_stats *stats;
- int num_pdev_stats, num_vdev_stats, num_peer_stats;
- struct wmi_pdev_stats_10x *ps;
- int i;
+ struct ath10k_fw_stats_pdev *i, *tmp;
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+static void ath10k_debug_fw_stats_peers_free(struct list_head *head)
+{
+ struct ath10k_fw_stats_peer *i, *tmp;
+
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
+{
spin_lock_bh(&ar->data_lock);
+ ar->debug.fw_stats_done = false;
+ ath10k_debug_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
+ ath10k_debug_fw_stats_peers_free(&ar->debug.fw_stats.peers);
+ spin_unlock_bh(&ar->data_lock);
+}
- stats = &ar->debug.target_stats;
-
- num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */
- num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */
- num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
-
- if (num_pdev_stats) {
- ps = (struct wmi_pdev_stats_10x *)tmp;
-
- stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
- stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
- stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count);
- stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count);
- stats->cycle_count = __le32_to_cpu(ps->cycle_count);
- stats->phy_err_count = __le32_to_cpu(ps->phy_err_count);
- stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr);
-
- stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued);
- stats->comp_delivered =
- __le32_to_cpu(ps->wal.tx.comp_delivered);
- stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued);
- stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued);
- stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop);
- stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued);
- stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed);
- stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued);
- stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped);
- stats->underrun = __le32_to_cpu(ps->wal.tx.underrun);
- stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort);
- stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed);
- stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko);
- stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc);
- stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers);
- stats->sw_retry_failure =
- __le32_to_cpu(ps->wal.tx.sw_retry_failure);
- stats->illgl_rate_phy_err =
- __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err);
- stats->pdev_cont_xretry =
- __le32_to_cpu(ps->wal.tx.pdev_cont_xretry);
- stats->pdev_tx_timeout =
- __le32_to_cpu(ps->wal.tx.pdev_tx_timeout);
- stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets);
- stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun);
- stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf);
-
- stats->mid_ppdu_route_change =
- __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change);
- stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd);
- stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags);
- stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags);
- stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags);
- stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags);
- stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus);
- stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus);
- stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus);
- stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus);
- stats->oversize_amsdu =
- __le32_to_cpu(ps->wal.rx.oversize_amsdu);
- stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs);
- stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
- stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
-
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
- ar->fw_features)) {
- stats->ack_rx_bad = __le32_to_cpu(ps->ack_rx_bad);
- stats->rts_bad = __le32_to_cpu(ps->rts_bad);
- stats->rts_good = __le32_to_cpu(ps->rts_good);
- stats->fcs_bad = __le32_to_cpu(ps->fcs_bad);
- stats->no_beacons = __le32_to_cpu(ps->no_beacons);
- stats->mib_int_count = __le32_to_cpu(ps->mib_int_count);
- tmp += sizeof(struct wmi_pdev_stats_10x);
- } else {
- tmp += sizeof(struct wmi_pdev_stats_old);
- }
+static size_t ath10k_debug_fw_stats_num_peers(struct list_head *head)
+{
+ struct ath10k_fw_stats_peer *i;
+ size_t num = 0;
+
+ list_for_each_entry(i, head, list)
+ ++num;
+
+ return num;
+}
+
+void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
+{
+ struct ath10k_fw_stats stats = {};
+ bool is_start, is_started, is_end;
+ size_t num_peers;
+ int ret;
+
+ INIT_LIST_HEAD(&stats.pdevs);
+ INIT_LIST_HEAD(&stats.peers);
+
+ spin_lock_bh(&ar->data_lock);
+ ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);
+ if (ret) {
+ ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);
+ goto unlock;
}
- /* 0 or max vdevs */
- /* Currently firmware does not support VDEV stats */
- if (num_vdev_stats) {
- struct wmi_vdev_stats *vdev_stats;
+ /* Stat data may exceed htc-wmi buffer limit. In such case firmware
+ * splits the stats data and delivers it in a ping-pong fashion of
+ * request cmd-update event.
+ *
+ * However there is no explicit end-of-data. Instead start-of-data is
+ * used as an implicit one. This works as follows:
+ * a) discard stat update events until one with pdev stats is
+ * delivered - this skips session started at end of (b)
+ * b) consume stat update events until another one with pdev stats is
+ * delivered which is treated as end-of-data and is itself discarded
+ */
- for (i = 0; i < num_vdev_stats; i++) {
- vdev_stats = (struct wmi_vdev_stats *)tmp;
- tmp += sizeof(struct wmi_vdev_stats);
- }
+ if (ar->debug.fw_stats_done) {
+ ath10k_warn(ar, "received unsolicited stats update event\n");
+ goto free;
}
- if (num_peer_stats) {
- struct wmi_peer_stats_10x *peer_stats;
- struct ath10k_peer_stat *s;
-
- stats->peers = num_peer_stats;
-
- for (i = 0; i < num_peer_stats; i++) {
- peer_stats = (struct wmi_peer_stats_10x *)tmp;
- s = &stats->peer_stat[i];
-
- memcpy(s->peer_macaddr, &peer_stats->peer_macaddr.addr,
- ETH_ALEN);
- s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
- s->peer_tx_rate =
- __le32_to_cpu(peer_stats->peer_tx_rate);
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
- ar->fw_features)) {
- s->peer_rx_rate =
- __le32_to_cpu(peer_stats->peer_rx_rate);
- tmp += sizeof(struct wmi_peer_stats_10x);
-
- } else {
- tmp += sizeof(struct wmi_peer_stats_old);
- }
+ num_peers = ath10k_debug_fw_stats_num_peers(&ar->debug.fw_stats.peers);
+ is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&
+ !list_empty(&stats.pdevs));
+ is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&
+ !list_empty(&stats.pdevs));
+
+ if (is_start)
+ list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);
+
+ if (is_end)
+ ar->debug.fw_stats_done = true;
+
+ is_started = !list_empty(&ar->debug.fw_stats.pdevs);
+
+ if (is_started && !is_end) {
+ if (num_peers >= ATH10K_MAX_NUM_PEER_IDS) {
+ /* Although this is unlikely impose a sane limit to
+ * prevent firmware from DoS-ing the host.
+ */
+ ath10k_warn(ar, "dropping fw peer stats\n");
+ goto free;
}
+
+ list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
}
+ complete(&ar->debug.fw_stats_complete);
+
+free:
+ /* In some cases lists have been spliced and cleared. Free up
+ * resources if that is not the case.
+ */
+ ath10k_debug_fw_stats_pdevs_free(&stats.pdevs);
+ ath10k_debug_fw_stats_peers_free(&stats.peers);
+
+unlock:
spin_unlock_bh(&ar->data_lock);
- complete(&ar->debug.event_stats_compl);
}
-static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int ath10k_debug_fw_stats_request(struct ath10k *ar)
{
- struct ath10k *ar = file->private_data;
- struct ath10k_target_stats *fw_stats;
- char *buf = NULL;
- unsigned int len = 0, buf_len = 8000;
- ssize_t ret_cnt = 0;
- long left;
- int i;
+ unsigned long timeout;
int ret;
- fw_stats = &ar->debug.target_stats;
+ lockdep_assert_held(&ar->conf_mutex);
- mutex_lock(&ar->conf_mutex);
+ timeout = jiffies + msecs_to_jiffies(1*HZ);
- if (ar->state != ATH10K_STATE_ON)
- goto exit;
+ ath10k_debug_fw_stats_reset(ar);
- buf = kzalloc(buf_len, GFP_KERNEL);
- if (!buf)
- goto exit;
+ for (;;) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
- ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
- if (ret) {
- ath10k_warn(ar, "could not request stats (%d)\n", ret);
- goto exit;
+ reinit_completion(&ar->debug.fw_stats_complete);
+
+ ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+ if (ret) {
+ ath10k_warn(ar, "could not request stats (%d)\n", ret);
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&ar->debug.fw_stats_complete,
+ 1*HZ);
+ if (ret <= 0)
+ return -ETIMEDOUT;
+
+ spin_lock_bh(&ar->data_lock);
+ if (ar->debug.fw_stats_done) {
+ spin_unlock_bh(&ar->data_lock);
+ break;
+ }
+ spin_unlock_bh(&ar->data_lock);
}
- left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
- if (left <= 0)
- goto exit;
+ return 0;
+}
+
+/* FIXME: How to calculate the buffer size sanely? */
+#define ATH10K_FW_STATS_BUF_SIZE (1024*1024)
+
+static void ath10k_fw_stats_fill(struct ath10k *ar,
+ struct ath10k_fw_stats *fw_stats,
+ char *buf)
+{
+ unsigned int len = 0;
+ unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
+ const struct ath10k_fw_stats_pdev *pdev;
+ const struct ath10k_fw_stats_peer *peer;
+ size_t num_peers;
spin_lock_bh(&ar->data_lock);
+
+ pdev = list_first_entry_or_null(&fw_stats->pdevs,
+ struct ath10k_fw_stats_pdev, list);
+ if (!pdev) {
+ ath10k_warn(ar, "failed to get pdev stats\n");
+ goto unlock;
+ }
+
+ num_peers = ath10k_debug_fw_stats_num_peers(&fw_stats->peers);
+
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
"ath10k PDEV stats");
@@ -412,29 +418,29 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
"=================");
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Channel noise floor", fw_stats->ch_noise_floor);
+ "Channel noise floor", pdev->ch_noise_floor);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "Channel TX power", fw_stats->chan_tx_power);
+ "Channel TX power", pdev->chan_tx_power);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "TX frame count", fw_stats->tx_frame_count);
+ "TX frame count", pdev->tx_frame_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RX frame count", fw_stats->rx_frame_count);
+ "RX frame count", pdev->rx_frame_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RX clear count", fw_stats->rx_clear_count);
+ "RX clear count", pdev->rx_clear_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "Cycle count", fw_stats->cycle_count);
+ "Cycle count", pdev->cycle_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "PHY error count", fw_stats->phy_err_count);
+ "PHY error count", pdev->phy_err_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RTS bad count", fw_stats->rts_bad);
+ "RTS bad count", pdev->rts_bad);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RTS good count", fw_stats->rts_good);
+ "RTS good count", pdev->rts_good);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "FCS bad count", fw_stats->fcs_bad);
+ "FCS bad count", pdev->fcs_bad);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "No beacon count", fw_stats->no_beacons);
+ "No beacon count", pdev->no_beacons);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "MIB int count", fw_stats->mib_int_count);
+ "MIB int count", pdev->mib_int_count);
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -443,51 +449,51 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
"=================");
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HTT cookies queued", fw_stats->comp_queued);
+ "HTT cookies queued", pdev->comp_queued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HTT cookies disp.", fw_stats->comp_delivered);
+ "HTT cookies disp.", pdev->comp_delivered);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDU queued", fw_stats->msdu_enqued);
+ "MSDU queued", pdev->msdu_enqued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDU queued", fw_stats->mpdu_enqued);
+ "MPDU queued", pdev->mpdu_enqued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDUs dropped", fw_stats->wmm_drop);
+ "MSDUs dropped", pdev->wmm_drop);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Local enqued", fw_stats->local_enqued);
+ "Local enqued", pdev->local_enqued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Local freed", fw_stats->local_freed);
+ "Local freed", pdev->local_freed);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HW queued", fw_stats->hw_queued);
+ "HW queued", pdev->hw_queued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PPDUs reaped", fw_stats->hw_reaped);
+ "PPDUs reaped", pdev->hw_reaped);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Num underruns", fw_stats->underrun);
+ "Num underruns", pdev->underrun);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PPDUs cleaned", fw_stats->tx_abort);
+ "PPDUs cleaned", pdev->tx_abort);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDUs requed", fw_stats->mpdus_requed);
+ "MPDUs requed", pdev->mpdus_requed);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Excessive retries", fw_stats->tx_ko);
+ "Excessive retries", pdev->tx_ko);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HW rate", fw_stats->data_rc);
+ "HW rate", pdev->data_rc);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Sched self tiggers", fw_stats->self_triggers);
+ "Sched self tiggers", pdev->self_triggers);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
"Dropped due to SW retries",
- fw_stats->sw_retry_failure);
+ pdev->sw_retry_failure);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
"Illegal rate phy errors",
- fw_stats->illgl_rate_phy_err);
+ pdev->illgl_rate_phy_err);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Pdev continous xretry", fw_stats->pdev_cont_xretry);
+ "Pdev continous xretry", pdev->pdev_cont_xretry);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "TX timeout", fw_stats->pdev_tx_timeout);
+ "TX timeout", pdev->pdev_tx_timeout);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PDEV resets", fw_stats->pdev_resets);
+ "PDEV resets", pdev->pdev_resets);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PHY underrun", fw_stats->phy_underrun);
+ "PHY underrun", pdev->phy_underrun);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDU is more than txop limit", fw_stats->txop_ovf);
+ "MPDU is more than txop limit", pdev->txop_ovf);
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -497,70 +503,161 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
"Mid PPDU route change",
- fw_stats->mid_ppdu_route_change);
+ pdev->mid_ppdu_route_change);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Tot. number of statuses", fw_stats->status_rcvd);
+ "Tot. number of statuses", pdev->status_rcvd);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 0", fw_stats->r0_frags);
+ "Extra frags on rings 0", pdev->r0_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 1", fw_stats->r1_frags);
+ "Extra frags on rings 1", pdev->r1_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 2", fw_stats->r2_frags);
+ "Extra frags on rings 2", pdev->r2_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 3", fw_stats->r3_frags);
+ "Extra frags on rings 3", pdev->r3_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDUs delivered to HTT", fw_stats->htt_msdus);
+ "MSDUs delivered to HTT", pdev->htt_msdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDUs delivered to HTT", fw_stats->htt_mpdus);
+ "MPDUs delivered to HTT", pdev->htt_mpdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDUs delivered to stack", fw_stats->loc_msdus);
+ "MSDUs delivered to stack", pdev->loc_msdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDUs delivered to stack", fw_stats->loc_mpdus);
+ "MPDUs delivered to stack", pdev->loc_mpdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Oversized AMSUs", fw_stats->oversize_amsdu);
+ "Oversized AMSUs", pdev->oversize_amsdu);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PHY errors", fw_stats->phy_errs);
+ "PHY errors", pdev->phy_errs);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PHY errors drops", fw_stats->phy_err_drop);
+ "PHY errors drops", pdev->phy_err_drop);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
+ "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs);
len += scnprintf(buf + len, buf_len - len, "\n");
- len += scnprintf(buf + len, buf_len - len, "%30s (%d)\n",
- "ath10k PEER stats", fw_stats->peers);
+ len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+ "ath10k PEER stats", num_peers);
len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
"=================");
- for (i = 0; i < fw_stats->peers; i++) {
+ list_for_each_entry(peer, &fw_stats->peers, list) {
len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
- "Peer MAC address",
- fw_stats->peer_stat[i].peer_macaddr);
+ "Peer MAC address", peer->peer_macaddr);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
- "Peer RSSI", fw_stats->peer_stat[i].peer_rssi);
+ "Peer RSSI", peer->peer_rssi);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
- "Peer TX rate",
- fw_stats->peer_stat[i].peer_tx_rate);
+ "Peer TX rate", peer->peer_tx_rate);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
- "Peer RX rate",
- fw_stats->peer_stat[i].peer_rx_rate);
+ "Peer RX rate", peer->peer_rx_rate);
len += scnprintf(buf + len, buf_len - len, "\n");
}
+
+unlock:
spin_unlock_bh(&ar->data_lock);
- if (len > buf_len)
- len = buf_len;
+ if (len >= buf_len)
+ buf[len - 1] = 0;
+ else
+ buf[len] = 0;
+}
- ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+static int ath10k_fw_stats_open(struct inode *inode, struct file *file)
+{
+ struct ath10k *ar = inode->i_private;
+ void *buf = NULL;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ret = -ENETDOWN;
+ goto err_unlock;
+ }
+
+ buf = vmalloc(ATH10K_FW_STATS_BUF_SIZE);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err_unlock;
+ }
+
+ ret = ath10k_debug_fw_stats_request(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to request fw stats: %d\n", ret);
+ goto err_free;
+ }
+
+ ath10k_fw_stats_fill(ar, &ar->debug.fw_stats, buf);
+ file->private_data = buf;
-exit:
mutex_unlock(&ar->conf_mutex);
- kfree(buf);
- return ret_cnt;
+ return 0;
+
+err_free:
+ vfree(buf);
+
+err_unlock:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static int ath10k_fw_stats_release(struct inode *inode, struct file *file)
+{
+ vfree(file->private_data);
+
+ return 0;
+}
+
+static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *buf = file->private_data;
+ unsigned int len = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static const struct file_operations fops_fw_stats = {
- .read = ath10k_read_fw_stats,
+ .open = ath10k_fw_stats_open,
+ .release = ath10k_fw_stats_release,
+ .read = ath10k_fw_stats_read,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ int ret, len, buf_len;
+ char *buf;
+
+ buf_len = 500;
+ buf = kmalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ spin_lock_bh(&ar->data_lock);
+
+ len = 0;
+ len += scnprintf(buf + len, buf_len - len,
+ "fw_crash_counter\t\t%d\n", ar->stats.fw_crash_counter);
+ len += scnprintf(buf + len, buf_len - len,
+ "fw_warm_reset_counter\t\t%d\n",
+ ar->stats.fw_warm_reset_counter);
+ len += scnprintf(buf + len, buf_len - len,
+ "fw_cold_reset_counter\t\t%d\n",
+ ar->stats.fw_cold_reset_counter);
+
+ spin_unlock_bh(&ar->data_lock);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static const struct file_operations fops_fw_reset_stats = {
.open = simple_open,
+ .read = ath10k_debug_fw_reset_stats_read,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
@@ -593,7 +690,8 @@ static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
"To simulate firmware crash write one of the keywords to this file:\n"
"`soft` - this will send WMI_FORCE_FW_HANG_ASSERT to firmware if FW supports that command.\n"
"`hard` - this will send to firmware command with illegal parameters causing firmware crash.\n"
- "`assert` - this will send special illegal parameter to firmware to cause assert failure and crash.\n";
+ "`assert` - this will send special illegal parameter to firmware to cause assert failure and crash.\n"
+ "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
}
@@ -646,6 +744,10 @@ static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
} else if (!strcmp(buf, "assert")) {
ath10k_info(ar, "simulating firmware assert crash\n");
ret = ath10k_debug_fw_assert(ar);
+ } else if (!strcmp(buf, "hw-restart")) {
+ ath10k_info(ar, "user requested hw restart\n");
+ queue_work(ar->workqueue, &ar->restart_work);
+ ret = 0;
} else {
ret = -EINVAL;
goto exit;
@@ -759,8 +861,8 @@ static struct ath10k_dump_file_data *ath10k_build_dump_file(struct ath10k *ar)
strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version,
sizeof(dump_data->fw_ver));
- dump_data->kernel_ver_code = cpu_to_le32(LINUX_VERSION_CODE);
- strlcpy(dump_data->kernel_ver, VERMAGIC_STRING,
+ dump_data->kernel_ver_code = 0;
+ strlcpy(dump_data->kernel_ver, init_utsname()->release,
sizeof(dump_data->kernel_ver));
dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec);
@@ -822,6 +924,236 @@ static const struct file_operations fops_fw_crash_dump = {
.llseek = default_llseek,
};
+static ssize_t ath10k_reg_addr_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u8 buf[32];
+ unsigned int len = 0;
+ u32 reg_addr;
+
+ mutex_lock(&ar->conf_mutex);
+ reg_addr = ar->debug.reg_addr;
+ mutex_unlock(&ar->conf_mutex);
+
+ len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_addr);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_reg_addr_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u32 reg_addr;
+ int ret;
+
+ ret = kstrtou32_from_user(user_buf, count, 0, &reg_addr);
+ if (ret)
+ return ret;
+
+ if (!IS_ALIGNED(reg_addr, 4))
+ return -EFAULT;
+
+ mutex_lock(&ar->conf_mutex);
+ ar->debug.reg_addr = reg_addr;
+ mutex_unlock(&ar->conf_mutex);
+
+ return count;
+}
+
+static const struct file_operations fops_reg_addr = {
+ .read = ath10k_reg_addr_read,
+ .write = ath10k_reg_addr_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath10k_reg_value_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u8 buf[48];
+ unsigned int len;
+ u32 reg_addr, reg_val;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ reg_addr = ar->debug.reg_addr;
+
+ reg_val = ath10k_hif_read32(ar, reg_addr);
+ len = scnprintf(buf, sizeof(buf), "0x%08x:0x%08x\n", reg_addr, reg_val);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static ssize_t ath10k_reg_value_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u32 reg_addr, reg_val;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ reg_addr = ar->debug.reg_addr;
+
+ ret = kstrtou32_from_user(user_buf, count, 0, &reg_val);
+ if (ret)
+ goto exit;
+
+ ath10k_hif_write32(ar, reg_addr, reg_val);
+
+ ret = count;
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static const struct file_operations fops_reg_value = {
+ .read = ath10k_reg_value_read,
+ .write = ath10k_reg_value_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath10k_mem_value_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u8 *buf;
+ int ret;
+
+ if (*ppos < 0)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ mutex_lock(&ar->conf_mutex);
+
+ buf = vmalloc(count);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ ret = ath10k_hif_diag_read(ar, *ppos, buf, count);
+ if (ret) {
+ ath10k_warn(ar, "failed to read address 0x%08x via diagnose window fnrom debugfs: %d\n",
+ (u32)(*ppos), ret);
+ goto exit;
+ }
+
+ ret = copy_to_user(user_buf, buf, count);
+ if (ret) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ count -= ret;
+ *ppos += count;
+ ret = count;
+
+exit:
+ vfree(buf);
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static ssize_t ath10k_mem_value_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u8 *buf;
+ int ret;
+
+ if (*ppos < 0)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ mutex_lock(&ar->conf_mutex);
+
+ buf = vmalloc(count);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ ret = copy_from_user(buf, user_buf, count);
+ if (ret) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ ret = ath10k_hif_diag_write(ar, *ppos, buf, count);
+ if (ret) {
+ ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",
+ (u32)(*ppos), ret);
+ goto exit;
+ }
+
+ *ppos += count;
+ ret = count;
+
+exit:
+ vfree(buf);
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static const struct file_operations fops_mem_value = {
+ .read = ath10k_mem_value_read,
+ .write = ath10k_mem_value_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
static int ath10k_debug_htt_stats_req(struct ath10k *ar)
{
u64 cookie;
@@ -1029,6 +1361,166 @@ exit:
return ret;
}
+/* TODO: Would be nice to always support ethtool stats, would need to
+ * move the stats storage out of ath10k_debug, or always have ath10k_debug
+ * struct available..
+ */
+
+/* This generally cooresponds to the debugfs fw_stats file */
+static const char ath10k_gstrings_stats[][ETH_GSTRING_LEN] = {
+ "tx_pkts_nic",
+ "tx_bytes_nic",
+ "rx_pkts_nic",
+ "rx_bytes_nic",
+ "d_noise_floor",
+ "d_cycle_count",
+ "d_phy_error",
+ "d_rts_bad",
+ "d_rts_good",
+ "d_tx_power", /* in .5 dbM I think */
+ "d_rx_crc_err", /* fcs_bad */
+ "d_no_beacon",
+ "d_tx_mpdus_queued",
+ "d_tx_msdu_queued",
+ "d_tx_msdu_dropped",
+ "d_local_enqued",
+ "d_local_freed",
+ "d_tx_ppdu_hw_queued",
+ "d_tx_ppdu_reaped",
+ "d_tx_fifo_underrun",
+ "d_tx_ppdu_abort",
+ "d_tx_mpdu_requed",
+ "d_tx_excessive_retries",
+ "d_tx_hw_rate",
+ "d_tx_dropped_sw_retries",
+ "d_tx_illegal_rate",
+ "d_tx_continuous_xretries",
+ "d_tx_timeout",
+ "d_tx_mpdu_txop_limit",
+ "d_pdev_resets",
+ "d_rx_mid_ppdu_route_change",
+ "d_rx_status",
+ "d_rx_extra_frags_ring0",
+ "d_rx_extra_frags_ring1",
+ "d_rx_extra_frags_ring2",
+ "d_rx_extra_frags_ring3",
+ "d_rx_msdu_htt",
+ "d_rx_mpdu_htt",
+ "d_rx_msdu_stack",
+ "d_rx_mpdu_stack",
+ "d_rx_phy_err",
+ "d_rx_phy_err_drops",
+ "d_rx_mpdu_errors", /* FCS, MIC, ENC */
+ "d_fw_crash_count",
+ "d_fw_warm_reset_count",
+ "d_fw_cold_reset_count",
+};
+
+#define ATH10K_SSTATS_LEN ARRAY_SIZE(ath10k_gstrings_stats)
+
+void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 sset, u8 *data)
+{
+ if (sset == ETH_SS_STATS)
+ memcpy(data, *ath10k_gstrings_stats,
+ sizeof(ath10k_gstrings_stats));
+}
+
+int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int sset)
+{
+ if (sset == ETH_SS_STATS)
+ return ATH10K_SSTATS_LEN;
+
+ return 0;
+}
+
+void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct ath10k *ar = hw->priv;
+ static const struct ath10k_fw_stats_pdev zero_stats = {};
+ const struct ath10k_fw_stats_pdev *pdev_stats;
+ int i = 0, ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state == ATH10K_STATE_ON) {
+ ret = ath10k_debug_fw_stats_request(ar);
+ if (ret) {
+ /* just print a warning and try to use older results */
+ ath10k_warn(ar,
+ "failed to get fw stats for ethtool: %d\n",
+ ret);
+ }
+ }
+
+ pdev_stats = list_first_entry_or_null(&ar->debug.fw_stats.pdevs,
+ struct ath10k_fw_stats_pdev,
+ list);
+ if (!pdev_stats) {
+ /* no results available so just return zeroes */
+ pdev_stats = &zero_stats;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+
+ data[i++] = pdev_stats->hw_reaped; /* ppdu reaped */
+ data[i++] = 0; /* tx bytes */
+ data[i++] = pdev_stats->htt_mpdus;
+ data[i++] = 0; /* rx bytes */
+ data[i++] = pdev_stats->ch_noise_floor;
+ data[i++] = pdev_stats->cycle_count;
+ data[i++] = pdev_stats->phy_err_count;
+ data[i++] = pdev_stats->rts_bad;
+ data[i++] = pdev_stats->rts_good;
+ data[i++] = pdev_stats->chan_tx_power;
+ data[i++] = pdev_stats->fcs_bad;
+ data[i++] = pdev_stats->no_beacons;
+ data[i++] = pdev_stats->mpdu_enqued;
+ data[i++] = pdev_stats->msdu_enqued;
+ data[i++] = pdev_stats->wmm_drop;
+ data[i++] = pdev_stats->local_enqued;
+ data[i++] = pdev_stats->local_freed;
+ data[i++] = pdev_stats->hw_queued;
+ data[i++] = pdev_stats->hw_reaped;
+ data[i++] = pdev_stats->underrun;
+ data[i++] = pdev_stats->tx_abort;
+ data[i++] = pdev_stats->mpdus_requed;
+ data[i++] = pdev_stats->tx_ko;
+ data[i++] = pdev_stats->data_rc;
+ data[i++] = pdev_stats->sw_retry_failure;
+ data[i++] = pdev_stats->illgl_rate_phy_err;
+ data[i++] = pdev_stats->pdev_cont_xretry;
+ data[i++] = pdev_stats->pdev_tx_timeout;
+ data[i++] = pdev_stats->txop_ovf;
+ data[i++] = pdev_stats->pdev_resets;
+ data[i++] = pdev_stats->mid_ppdu_route_change;
+ data[i++] = pdev_stats->status_rcvd;
+ data[i++] = pdev_stats->r0_frags;
+ data[i++] = pdev_stats->r1_frags;
+ data[i++] = pdev_stats->r2_frags;
+ data[i++] = pdev_stats->r3_frags;
+ data[i++] = pdev_stats->htt_msdus;
+ data[i++] = pdev_stats->htt_mpdus;
+ data[i++] = pdev_stats->loc_msdus;
+ data[i++] = pdev_stats->loc_mpdus;
+ data[i++] = pdev_stats->phy_errs;
+ data[i++] = pdev_stats->phy_err_drop;
+ data[i++] = pdev_stats->mpdu_errs;
+ data[i++] = ar->stats.fw_crash_counter;
+ data[i++] = ar->stats.fw_warm_reset_counter;
+ data[i++] = ar->stats.fw_cold_reset_counter;
+
+ spin_unlock_bh(&ar->data_lock);
+
+ mutex_unlock(&ar->conf_mutex);
+
+ WARN_ON(i != ATH10K_SSTATS_LEN);
+}
+
static const struct file_operations fops_fw_dbglog = {
.read = ath10k_read_fw_dbglog,
.write = ath10k_write_fw_dbglog,
@@ -1037,6 +1529,84 @@ static const struct file_operations fops_fw_dbglog = {
.llseek = default_llseek,
};
+static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)
+{
+ struct ath10k *ar = inode->i_private;
+ void *buf;
+ u32 hi_addr;
+ __le32 addr;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto err;
+ }
+
+ buf = vmalloc(QCA988X_CAL_DATA_LEN);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ hi_addr = host_interest_item_address(HI_ITEM(hi_board_data));
+
+ ret = ath10k_hif_diag_read(ar, hi_addr, &addr, sizeof(addr));
+ if (ret) {
+ ath10k_warn(ar, "failed to read hi_board_data address: %d\n", ret);
+ goto err_vfree;
+ }
+
+ ret = ath10k_hif_diag_read(ar, le32_to_cpu(addr), buf,
+ QCA988X_CAL_DATA_LEN);
+ if (ret) {
+ ath10k_warn(ar, "failed to read calibration data: %d\n", ret);
+ goto err_vfree;
+ }
+
+ file->private_data = buf;
+
+ mutex_unlock(&ar->conf_mutex);
+
+ return 0;
+
+err_vfree:
+ vfree(buf);
+
+err:
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static ssize_t ath10k_debug_cal_data_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ void *buf = file->private_data;
+
+ return simple_read_from_buffer(user_buf, count, ppos,
+ buf, QCA988X_CAL_DATA_LEN);
+}
+
+static int ath10k_debug_cal_data_release(struct inode *inode,
+ struct file *file)
+{
+ vfree(file->private_data);
+
+ return 0;
+}
+
+static const struct file_operations fops_cal_data = {
+ .open = ath10k_debug_cal_data_open,
+ .read = ath10k_debug_cal_data_read,
+ .release = ath10k_debug_cal_data_release,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
int ath10k_debug_start(struct ath10k *ar)
{
int ret;
@@ -1057,7 +1627,22 @@ int ath10k_debug_start(struct ath10k *ar)
ret);
}
- return 0;
+ if (ar->debug.pktlog_filter) {
+ ret = ath10k_wmi_pdev_pktlog_enable(ar,
+ ar->debug.pktlog_filter);
+ if (ret)
+ /* not serious */
+ ath10k_warn(ar,
+ "failed to enable pktlog filter %x: %d\n",
+ ar->debug.pktlog_filter, ret);
+ } else {
+ ret = ath10k_wmi_pdev_pktlog_disable(ar);
+ if (ret)
+ /* not serious */
+ ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
+ }
+
+ return ret;
}
void ath10k_debug_stop(struct ath10k *ar)
@@ -1072,6 +1657,8 @@ void ath10k_debug_stop(struct ath10k *ar)
ar->debug.htt_max_amsdu = 0;
ar->debug.htt_max_ampdu = 0;
+
+ ath10k_wmi_pdev_pktlog_disable(ar);
}
static ssize_t ath10k_write_simulate_radar(struct file *file,
@@ -1154,12 +1741,78 @@ static const struct file_operations fops_dfs_stats = {
.llseek = default_llseek,
};
+static ssize_t ath10k_write_pktlog_filter(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u32 filter;
+ int ret;
+
+ if (kstrtouint_from_user(ubuf, count, 0, &filter))
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ar->debug.pktlog_filter = filter;
+ ret = count;
+ goto out;
+ }
+
+ if (filter && (filter != ar->debug.pktlog_filter)) {
+ ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);
+ if (ret) {
+ ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",
+ ar->debug.pktlog_filter, ret);
+ goto out;
+ }
+ } else {
+ ret = ath10k_wmi_pdev_pktlog_disable(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
+ goto out;
+ }
+ }
+
+ ar->debug.pktlog_filter = filter;
+ ret = count;
+
+out:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static ssize_t ath10k_read_pktlog_filter(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ struct ath10k *ar = file->private_data;
+ int len = 0;
+
+ mutex_lock(&ar->conf_mutex);
+ len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
+ ar->debug.pktlog_filter);
+ mutex_unlock(&ar->conf_mutex);
+
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_pktlog_filter = {
+ .read = ath10k_read_pktlog_filter,
+ .write = ath10k_write_pktlog_filter,
+ .open = simple_open
+};
+
int ath10k_debug_create(struct ath10k *ar)
{
ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data));
if (!ar->debug.fw_crash_data)
return -ENOMEM;
+ INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
+ INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
+
return 0;
}
@@ -1167,6 +1820,8 @@ void ath10k_debug_destroy(struct ath10k *ar)
{
vfree(ar->debug.fw_crash_data);
ar->debug.fw_crash_data = NULL;
+
+ ath10k_debug_fw_stats_reset(ar);
}
int ath10k_debug_register(struct ath10k *ar)
@@ -1183,11 +1838,14 @@ int ath10k_debug_register(struct ath10k *ar)
INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,
ath10k_debug_htt_stats_dwork);
- init_completion(&ar->debug.event_stats_compl);
+ init_completion(&ar->debug.fw_stats_complete);
debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_fw_stats);
+ debugfs_create_file("fw_reset_stats", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_fw_reset_stats);
+
debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_wmi_services);
@@ -1197,6 +1855,15 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_fw_crash_dump);
+ debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_reg_addr);
+
+ debugfs_create_file("reg_value", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_reg_value);
+
+ debugfs_create_file("mem_value", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_mem_value);
+
debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_chip_id);
@@ -1210,6 +1877,9 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_fw_dbglog);
+ debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_cal_data);
+
if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) {
debugfs_create_file("dfs_simulate_radar", S_IWUSR,
ar->debug.debugfs_phy, ar,
@@ -1224,6 +1894,9 @@ int ath10k_debug_register(struct ath10k *ar)
&fops_dfs_stats);
}
+ debugfs_create_file("pktlog_filter", S_IRUGO | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_pktlog_filter);
+
return 0;
}
@@ -1260,11 +1933,26 @@ void ath10k_dbg_dump(struct ath10k *ar,
const char *msg, const char *prefix,
const void *buf, size_t len)
{
+ char linebuf[256];
+ unsigned int linebuflen;
+ const void *ptr;
+
if (ath10k_debug_mask & mask) {
if (msg)
ath10k_dbg(ar, mask, "%s\n", msg);
- print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
+ for (ptr = buf; (ptr - buf) < len; ptr += 16) {
+ linebuflen = 0;
+ linebuflen += scnprintf(linebuf + linebuflen,
+ sizeof(linebuf) - linebuflen,
+ "%s%08x: ",
+ (prefix ? prefix : ""),
+ (unsigned int)(ptr - buf));
+ hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1,
+ linebuf + linebuflen,
+ sizeof(linebuf) - linebuflen, true);
+ dev_printk(KERN_DEBUG, ar->dev, "%s\n", linebuf);
+ }
}
/* tracing code doesn't like null strings :/ */
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index b3774f7f492c..1b87a5dbec53 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -35,14 +35,24 @@ enum ath10k_debug_mask {
ATH10K_DBG_BMI = 0x00000400,
ATH10K_DBG_REGULATORY = 0x00000800,
ATH10K_DBG_TESTMODE = 0x00001000,
+ ATH10K_DBG_WMI_PRINT = 0x00002000,
ATH10K_DBG_ANY = 0xffffffff,
};
+enum ath10k_pktlog_filter {
+ ATH10K_PKTLOG_RX = 0x000000001,
+ ATH10K_PKTLOG_TX = 0x000000002,
+ ATH10K_PKTLOG_RCFIND = 0x000000004,
+ ATH10K_PKTLOG_RCUPDATE = 0x000000008,
+ ATH10K_PKTLOG_DBG_PRINT = 0x000000010,
+ ATH10K_PKTLOG_ANY = 0x00000001f,
+};
+
extern unsigned int ath10k_debug_mask;
-__printf(2, 3) int ath10k_info(struct ath10k *ar, const char *fmt, ...);
-__printf(2, 3) int ath10k_err(struct ath10k *ar, const char *fmt, ...);
-__printf(2, 3) int ath10k_warn(struct ath10k *ar, const char *fmt, ...);
+__printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
+__printf(2, 3) void ath10k_err(struct ath10k *ar, const char *fmt, ...);
+__printf(2, 3) void ath10k_warn(struct ath10k *ar, const char *fmt, ...);
void ath10k_print_driver_info(struct ath10k *ar);
#ifdef CONFIG_ATH10K_DEBUGFS
@@ -52,18 +62,22 @@ int ath10k_debug_create(struct ath10k *ar);
void ath10k_debug_destroy(struct ath10k *ar);
int ath10k_debug_register(struct ath10k *ar);
void ath10k_debug_unregister(struct ath10k *ar);
-void ath10k_debug_read_service_map(struct ath10k *ar,
- void *service_map,
- size_t map_size);
-void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct wmi_stats_event *ev);
+void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
struct ath10k_fw_crash_data *
ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len);
-
#define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
+void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 sset, u8 *data);
+int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int sset);
+void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ethtool_stats *stats, u64 *data);
+
#else
static inline int ath10k_debug_start(struct ath10k *ar)
{
@@ -92,14 +106,8 @@ static inline void ath10k_debug_unregister(struct ath10k *ar)
{
}
-static inline void ath10k_debug_read_service_map(struct ath10k *ar,
- void *service_map,
- size_t map_size)
-{
-}
-
-static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct wmi_stats_event *ev)
+static inline void ath10k_debug_fw_stats_process(struct ath10k *ar,
+ struct sk_buff *skb)
{
}
@@ -116,6 +124,10 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
#define ATH10K_DFS_STAT_INC(ar, c) do { } while (0)
+#define ath10k_debug_get_et_strings NULL
+#define ath10k_debug_get_et_sset_count NULL
+#define ath10k_debug_get_et_stats NULL
+
#endif /* CONFIG_ATH10K_DEBUGFS */
#ifdef CONFIG_ATH10K_DEBUG
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 62323fea27e1..0c92e0251e84 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include "core.h"
+#include "debug.h"
struct ath10k_hif_sg_item {
u16 transfer_id;
@@ -31,11 +32,9 @@ struct ath10k_hif_sg_item {
struct ath10k_hif_cb {
int (*tx_completion)(struct ath10k *ar,
- struct sk_buff *wbuf,
- unsigned transfer_id);
+ struct sk_buff *wbuf);
int (*rx_completion)(struct ath10k *ar,
- struct sk_buff *wbuf,
- u8 pipe_id);
+ struct sk_buff *wbuf);
};
struct ath10k_hif_ops {
@@ -43,6 +42,12 @@ struct ath10k_hif_ops {
int (*tx_sg)(struct ath10k *ar, u8 pipe_id,
struct ath10k_hif_sg_item *items, int n_items);
+ /* read firmware memory through the diagnose interface */
+ int (*diag_read)(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len);
+
+ int (*diag_write)(struct ath10k *ar, u32 address, const void *data,
+ int nbytes);
/*
* API to handle HIF-specific BMI message exchanges, this API is
* synchronous and only allowed to be called from a context that
@@ -80,6 +85,10 @@ struct ath10k_hif_ops {
u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+ u32 (*read32)(struct ath10k *ar, u32 address);
+
+ void (*write32)(struct ath10k *ar, u32 address, u32 value);
+
/* Power up the device and enter BMI transfer mode for FW download */
int (*power_up)(struct ath10k *ar);
@@ -98,6 +107,21 @@ static inline int ath10k_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
return ar->hif.ops->tx_sg(ar, pipe_id, items, n_items);
}
+static inline int ath10k_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len)
+{
+ return ar->hif.ops->diag_read(ar, address, buf, buf_len);
+}
+
+static inline int ath10k_hif_diag_write(struct ath10k *ar, u32 address,
+ const void *data, int nbytes)
+{
+ if (!ar->hif.ops->diag_write)
+ return -EOPNOTSUPP;
+
+ return ar->hif.ops->diag_write(ar, address, data, nbytes);
+}
+
static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
void *request, u32 request_len,
void *response, u32 *response_len)
@@ -177,4 +201,25 @@ static inline int ath10k_hif_resume(struct ath10k *ar)
return ar->hif.ops->resume(ar);
}
+static inline u32 ath10k_hif_read32(struct ath10k *ar, u32 address)
+{
+ if (!ar->hif.ops->read32) {
+ ath10k_warn(ar, "hif read32 not supported\n");
+ return 0xdeaddead;
+ }
+
+ return ar->hif.ops->read32(ar, address);
+}
+
+static inline void ath10k_hif_write32(struct ath10k *ar,
+ u32 address, u32 data)
+{
+ if (!ar->hif.ops->write32) {
+ ath10k_warn(ar, "hif write32 not supported\n");
+ return;
+ }
+
+ ar->hif.ops->write32(ar, address, data);
+}
+
#endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 676bd4ed969b..f1946a6be442 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -160,6 +160,7 @@ int ath10k_htc_send(struct ath10k_htc *htc,
ath10k_htc_prepare_tx_skb(ep, skb);
+ skb_cb->eid = eid;
skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
ret = dma_mapping_error(dev, skb_cb->paddr);
if (ret)
@@ -197,15 +198,18 @@ err_pull:
}
static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
- struct sk_buff *skb,
- unsigned int eid)
+ struct sk_buff *skb)
{
struct ath10k_htc *htc = &ar->htc;
- struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+ struct ath10k_skb_cb *skb_cb;
+ struct ath10k_htc_ep *ep;
if (WARN_ON_ONCE(!skb))
return 0;
+ skb_cb = ATH10K_SKB_CB(skb);
+ ep = &htc->endpoint[skb_cb->eid];
+
ath10k_htc_notify_tx_completion(ep, skb);
/* the skb now belongs to the completion handler */
@@ -317,8 +321,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc,
}
static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
- struct sk_buff *skb,
- u8 pipe_id)
+ struct sk_buff *skb)
{
int status = 0;
struct ath10k_htc *htc = &ar->htc;
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 3b44217a6c19..1bd5545af903 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -126,6 +126,7 @@ enum htt_data_tx_ext_tid {
* (HL hosts manage queues on the host )
* more_in_batch: only for HL hosts. indicates if more packets are
* pending. this allows target to wait and aggregate
+ * freq: 0 means home channel of given vdev. intended for offchannel
*/
struct htt_data_tx_desc {
u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */
@@ -133,7 +134,8 @@ struct htt_data_tx_desc {
__le16 len;
__le16 id;
__le32 frags_paddr;
- __le32 peerid;
+ __le16 peerid;
+ __le16 freq;
u8 prefetch[0]; /* start of frame, for FW classification engine */
} __packed;
@@ -156,6 +158,9 @@ enum htt_rx_ring_flags {
HTT_RX_RING_FLAGS_PHY_DATA_RX = 1 << 15
};
+#define HTT_RX_RING_SIZE_MIN 128
+#define HTT_RX_RING_SIZE_MAX 2048
+
struct htt_rx_ring_setup_ring {
__le32 fw_idx_shadow_reg_paddr;
__le32 rx_ring_base_paddr;
@@ -725,7 +730,7 @@ static inline u8 *htt_rx_test_get_chars(struct htt_rx_test *rx_test)
*/
struct htt_pktlog_msg {
u8 pad[3];
- __le32 payload[1 /* or more */];
+ u8 payload[0];
} __packed;
struct htt_dbg_stats_rx_reorder_stats {
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 60d40a04508b..9c782a42665e 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -25,19 +25,8 @@
#include <linux/log2.h>
-/* slightly larger than one large A-MPDU */
-#define HTT_RX_RING_SIZE_MIN 128
-
-/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */
-#define HTT_RX_RING_SIZE_MAX 2048
-
-#define HTT_RX_AVG_FRM_BYTES 1000
-
-/* ms, very conservative */
-#define HTT_RX_HOST_LATENCY_MAX_MS 20
-
-/* ms, conservative */
-#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10
+#define HTT_RX_RING_SIZE 1024
+#define HTT_RX_RING_FILL_LEVEL 1000
/* when under memory pressure rx ring refill may fail and needs a retry */
#define HTT_RX_RING_REFILL_RETRY_MS 50
@@ -45,68 +34,6 @@
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
static void ath10k_htt_txrx_compl_task(unsigned long ptr);
-static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
-{
- int size;
-
- /*
- * It is expected that the host CPU will typically be able to
- * service the rx indication from one A-MPDU before the rx
- * indication from the subsequent A-MPDU happens, roughly 1-2 ms
- * later. However, the rx ring should be sized very conservatively,
- * to accomodate the worst reasonable delay before the host CPU
- * services a rx indication interrupt.
- *
- * The rx ring need not be kept full of empty buffers. In theory,
- * the htt host SW can dynamically track the low-water mark in the
- * rx ring, and dynamically adjust the level to which the rx ring
- * is filled with empty buffers, to dynamically meet the desired
- * low-water mark.
- *
- * In contrast, it's difficult to resize the rx ring itself, once
- * it's in use. Thus, the ring itself should be sized very
- * conservatively, while the degree to which the ring is filled
- * with empty buffers should be sized moderately conservatively.
- */
-
- /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
- size =
- htt->max_throughput_mbps +
- 1000 /
- (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS;
-
- if (size < HTT_RX_RING_SIZE_MIN)
- size = HTT_RX_RING_SIZE_MIN;
-
- if (size > HTT_RX_RING_SIZE_MAX)
- size = HTT_RX_RING_SIZE_MAX;
-
- size = roundup_pow_of_two(size);
-
- return size;
-}
-
-static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt)
-{
- int size;
-
- /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
- size =
- htt->max_throughput_mbps *
- 1000 /
- (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS;
-
- /*
- * Make sure the fill level is at least 1 less than the ring size.
- * Leaving 1 element empty allows the SW to easily distinguish
- * between a full ring vs. an empty ring.
- */
- if (size >= htt->rx_ring.size)
- size = htt->rx_ring.size - 1;
-
- return size;
-}
-
static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
{
struct sk_buff *skb;
@@ -291,50 +218,38 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
htt->rx_ring.sw_rd_idx.msdu_payld = idx;
htt->rx_ring.fill_cnt--;
- return msdu;
-}
+ dma_unmap_single(htt->ar->dev,
+ ATH10K_SKB_CB(msdu)->paddr,
+ msdu->len + skb_tailroom(msdu),
+ DMA_FROM_DEVICE);
+ ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
+ msdu->data, msdu->len + skb_tailroom(msdu));
-static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
-{
- struct sk_buff *next;
-
- while (skb) {
- next = skb->next;
- dev_kfree_skb_any(skb);
- skb = next;
- }
+ return msdu;
}
/* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */
static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
u8 **fw_desc, int *fw_desc_len,
- struct sk_buff **head_msdu,
- struct sk_buff **tail_msdu,
- u32 *attention)
+ struct sk_buff_head *amsdu)
{
struct ath10k *ar = htt->ar;
int msdu_len, msdu_chaining = 0;
- struct sk_buff *msdu, *next;
+ struct sk_buff *msdu;
struct htt_rx_desc *rx_desc;
lockdep_assert_held(&htt->rx_ring.lock);
- if (htt->rx_confused) {
- ath10k_warn(ar, "htt is confused. refusing rx\n");
- return -1;
- }
-
- msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
- while (msdu) {
+ for (;;) {
int last_msdu, msdu_len_invalid, msdu_chained;
- dma_unmap_single(htt->ar->dev,
- ATH10K_SKB_CB(msdu)->paddr,
- msdu->len + skb_tailroom(msdu),
- DMA_FROM_DEVICE);
+ msdu = ath10k_htt_rx_netbuf_pop(htt);
+ if (!msdu) {
+ __skb_queue_purge(amsdu);
+ return -ENOENT;
+ }
- ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ",
- msdu->data, msdu->len + skb_tailroom(msdu));
+ __skb_queue_tail(amsdu, msdu);
rx_desc = (struct htt_rx_desc *)msdu->data;
@@ -353,19 +268,10 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
*/
if (!(__le32_to_cpu(rx_desc->attention.flags)
& RX_ATTENTION_FLAGS_MSDU_DONE)) {
- ath10k_htt_rx_free_msdu_chain(*head_msdu);
- *head_msdu = NULL;
- msdu = NULL;
- ath10k_err(ar, "htt rx stopped. cannot recover\n");
- htt->rx_confused = true;
- break;
+ __skb_queue_purge(amsdu);
+ return -EIO;
}
- *attention |= __le32_to_cpu(rx_desc->attention.flags) &
- (RX_ATTENTION_FLAGS_TKIP_MIC_ERR |
- RX_ATTENTION_FLAGS_DECRYPT_ERR |
- RX_ATTENTION_FLAGS_FCS_ERR |
- RX_ATTENTION_FLAGS_MGMT_TYPE);
/*
* Copy the FW rx descriptor for this MSDU from the rx
* indication message into the MSDU's netbuf. HL uses the
@@ -422,43 +328,32 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE));
msdu_len -= msdu->len;
- /* FIXME: Do chained buffers include htt_rx_desc or not? */
+ /* Note: Chained buffers do not contain rx descriptor */
while (msdu_chained--) {
- struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
-
- dma_unmap_single(htt->ar->dev,
- ATH10K_SKB_CB(next)->paddr,
- next->len + skb_tailroom(next),
- DMA_FROM_DEVICE);
-
- ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL,
- "htt rx chained: ", next->data,
- next->len + skb_tailroom(next));
-
- skb_trim(next, 0);
- skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE));
- msdu_len -= next->len;
+ msdu = ath10k_htt_rx_netbuf_pop(htt);
+ if (!msdu) {
+ __skb_queue_purge(amsdu);
+ return -ENOENT;
+ }
- msdu->next = next;
- msdu = next;
+ __skb_queue_tail(amsdu, msdu);
+ skb_trim(msdu, 0);
+ skb_put(msdu, min(msdu_len, HTT_RX_BUF_SIZE));
+ msdu_len -= msdu->len;
msdu_chaining = 1;
}
last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
RX_MSDU_END_INFO0_LAST_MSDU;
- if (last_msdu) {
- msdu->next = NULL;
- break;
- }
+ trace_ath10k_htt_rx_desc(ar, &rx_desc->attention,
+ sizeof(*rx_desc) - sizeof(u32));
- next = ath10k_htt_rx_netbuf_pop(htt);
- msdu->next = next;
- msdu = next;
+ if (last_msdu)
+ break;
}
- *tail_msdu = msdu;
- if (*head_msdu == NULL)
+ if (skb_queue_empty(amsdu))
msdu_chaining = -1;
/*
@@ -492,25 +387,20 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
size_t size;
struct timer_list *timer = &htt->rx_ring.refill_retry_timer;
- htt->rx_ring.size = ath10k_htt_rx_ring_size(htt);
+ htt->rx_confused = false;
+
+ /* XXX: The fill level could be changed during runtime in response to
+ * the host processing latency. Is this really worth it?
+ */
+ htt->rx_ring.size = HTT_RX_RING_SIZE;
+ htt->rx_ring.size_mask = htt->rx_ring.size - 1;
+ htt->rx_ring.fill_level = HTT_RX_RING_FILL_LEVEL;
+
if (!is_power_of_2(htt->rx_ring.size)) {
ath10k_warn(ar, "htt rx ring size is not power of 2\n");
return -EINVAL;
}
- htt->rx_ring.size_mask = htt->rx_ring.size - 1;
-
- /*
- * Set the initial value for the level to which the rx ring
- * should be filled, based on the max throughput and the
- * worst likely latency for the host to fill the rx ring
- * with new buffers. In theory, this fill level can be
- * dynamically adjusted from the initial value set here, to
- * reflect the actual host latency rather than a
- * conservative assumption about the host latency.
- */
- htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt);
-
htt->rx_ring.netbufs_ring =
kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
GFP_KERNEL);
@@ -581,73 +471,50 @@ static int ath10k_htt_rx_crypto_param_len(struct ath10k *ar,
enum htt_rx_mpdu_encrypt_type type)
{
switch (type) {
+ case HTT_RX_MPDU_ENCRYPT_NONE:
+ return 0;
case HTT_RX_MPDU_ENCRYPT_WEP40:
case HTT_RX_MPDU_ENCRYPT_WEP104:
- return 4;
+ return IEEE80211_WEP_IV_LEN;
case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
- case HTT_RX_MPDU_ENCRYPT_WEP128: /* not tested */
case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
- case HTT_RX_MPDU_ENCRYPT_WAPI: /* not tested */
+ return IEEE80211_TKIP_IV_LEN;
case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
- return 8;
- case HTT_RX_MPDU_ENCRYPT_NONE:
- return 0;
+ return IEEE80211_CCMP_HDR_LEN;
+ case HTT_RX_MPDU_ENCRYPT_WEP128:
+ case HTT_RX_MPDU_ENCRYPT_WAPI:
+ break;
}
- ath10k_warn(ar, "unknown encryption type %d\n", type);
+ ath10k_warn(ar, "unsupported encryption type %d\n", type);
return 0;
}
+#define MICHAEL_MIC_LEN 8
+
static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar,
enum htt_rx_mpdu_encrypt_type type)
{
switch (type) {
case HTT_RX_MPDU_ENCRYPT_NONE:
+ return 0;
case HTT_RX_MPDU_ENCRYPT_WEP40:
case HTT_RX_MPDU_ENCRYPT_WEP104:
- case HTT_RX_MPDU_ENCRYPT_WEP128:
- case HTT_RX_MPDU_ENCRYPT_WAPI:
- return 0;
+ return IEEE80211_WEP_ICV_LEN;
case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
- return 4;
+ return IEEE80211_TKIP_ICV_LEN;
case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
- return 8;
+ return IEEE80211_CCMP_MIC_LEN;
+ case HTT_RX_MPDU_ENCRYPT_WEP128:
+ case HTT_RX_MPDU_ENCRYPT_WAPI:
+ break;
}
- ath10k_warn(ar, "unknown encryption type %d\n", type);
+ ath10k_warn(ar, "unsupported encryption type %d\n", type);
return 0;
}
-/* Applies for first msdu in chain, before altering it. */
-static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb)
-{
- struct htt_rx_desc *rxd;
- enum rx_msdu_decap_format fmt;
-
- rxd = (void *)skb->data - sizeof(*rxd);
- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
- RX_MSDU_START_INFO1_DECAP_FORMAT);
-
- if (fmt == RX_MSDU_DECAP_RAW)
- return (void *)skb->data;
-
- return (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
-}
-
-/* This function only applies for first msdu in an msdu chain */
-static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
-{
- u8 *qc;
-
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- qc = ieee80211_get_qos_ctl(hdr);
- if (qc[0] & 0x80)
- return true;
- }
- return false;
-}
-
struct rfc1042_hdr {
u8 llc_dsap;
u8 llc_ssap;
@@ -682,23 +549,34 @@ static const u8 rx_legacy_rate_idx[] = {
};
static void ath10k_htt_rx_h_rates(struct ath10k *ar,
- enum ieee80211_band band,
- u8 info0, u32 info1, u32 info2,
- struct ieee80211_rx_status *status)
+ struct ieee80211_rx_status *status,
+ struct htt_rx_desc *rxd)
{
+ enum ieee80211_band band;
u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
u8 preamble = 0;
+ u32 info1, info2, info3;
- /* Check if valid fields */
- if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
+ /* Band value can't be set as undefined but freq can be 0 - use that to
+ * determine whether band is provided.
+ *
+ * FIXME: Perhaps this can go away if CCK rate reporting is a little
+ * reworked?
+ */
+ if (!status->freq)
return;
- preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
+ band = status->band;
+ info1 = __le32_to_cpu(rxd->ppdu_start.info1);
+ info2 = __le32_to_cpu(rxd->ppdu_start.info2);
+ info3 = __le32_to_cpu(rxd->ppdu_start.info3);
+
+ preamble = MS(info1, RX_PPDU_START_INFO1_PREAMBLE_TYPE);
switch (preamble) {
case HTT_RX_LEGACY:
- cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK;
- rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE);
+ cck = info1 & RX_PPDU_START_INFO1_L_SIG_RATE_SELECT;
+ rate = MS(info1, RX_PPDU_START_INFO1_L_SIG_RATE);
rate_idx = 0;
if (rate < 0x08 || rate > 0x0F)
@@ -725,11 +603,11 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
break;
case HTT_RX_HT:
case HTT_RX_HT_WITH_TXBF:
- /* HT-SIG - Table 20-11 in info1 and info2 */
- mcs = info1 & 0x1F;
+ /* HT-SIG - Table 20-11 in info2 and info3 */
+ mcs = info2 & 0x1F;
nss = mcs >> 3;
- bw = (info1 >> 7) & 1;
- sgi = (info2 >> 7) & 1;
+ bw = (info2 >> 7) & 1;
+ sgi = (info3 >> 7) & 1;
status->rate_idx = mcs;
status->flag |= RX_FLAG_HT;
@@ -740,12 +618,12 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
break;
case HTT_RX_VHT:
case HTT_RX_VHT_WITH_TXBF:
- /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
+ /* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3
TODO check this */
- mcs = (info2 >> 4) & 0x0F;
- nss = ((info1 >> 10) & 0x07) + 1;
- bw = info1 & 3;
- sgi = info2 & 1;
+ mcs = (info3 >> 4) & 0x0F;
+ nss = ((info2 >> 10) & 0x07) + 1;
+ bw = info2 & 3;
+ sgi = info3 & 1;
status->rate_idx = mcs;
status->vht_nss = nss;
@@ -773,41 +651,6 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
}
}
-static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt,
- struct ieee80211_rx_status *rx_status,
- struct sk_buff *skb,
- enum htt_rx_mpdu_encrypt_type enctype,
- enum rx_msdu_decap_format fmt,
- bool dot11frag)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
- rx_status->flag &= ~(RX_FLAG_DECRYPTED |
- RX_FLAG_IV_STRIPPED |
- RX_FLAG_MMIC_STRIPPED);
-
- if (enctype == HTT_RX_MPDU_ENCRYPT_NONE)
- return;
-
- /*
- * There's no explicit rx descriptor flag to indicate whether a given
- * frame has been decrypted or not. We're forced to use the decap
- * format as an implicit indication. However fragmentation rx is always
- * raw and it probably never reports undecrypted raws.
- *
- * This makes sure sniffed frames are reported as-is without stripping
- * the protected flag.
- */
- if (fmt == RX_MSDU_DECAP_RAW && !dot11frag)
- return;
-
- rx_status->flag |= RX_FLAG_DECRYPTED |
- RX_FLAG_IV_STRIPPED |
- RX_FLAG_MMIC_STRIPPED;
- hdr->frame_control = __cpu_to_le16(__le16_to_cpu(hdr->frame_control) &
- ~IEEE80211_FCTL_PROTECTED);
-}
-
static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
struct ieee80211_rx_status *status)
{
@@ -828,6 +671,72 @@ static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
return true;
}
+static void ath10k_htt_rx_h_signal(struct ath10k *ar,
+ struct ieee80211_rx_status *status,
+ struct htt_rx_desc *rxd)
+{
+ /* FIXME: Get real NF */
+ status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
+ rxd->ppdu_start.rssi_comb;
+ status->flag &= ~RX_FLAG_NO_SIGNAL_VAL;
+}
+
+static void ath10k_htt_rx_h_mactime(struct ath10k *ar,
+ struct ieee80211_rx_status *status,
+ struct htt_rx_desc *rxd)
+{
+ /* FIXME: TSF is known only at the end of PPDU, in the last MPDU. This
+ * means all prior MSDUs in a PPDU are reported to mac80211 without the
+ * TSF. Is it worth holding frames until end of PPDU is known?
+ *
+ * FIXME: Can we get/compute 64bit TSF?
+ */
+ status->mactime = __le32_to_cpu(rxd->ppdu_end.tsf_timestamp);
+ status->flag |= RX_FLAG_MACTIME_END;
+}
+
+static void ath10k_htt_rx_h_ppdu(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *status)
+{
+ struct sk_buff *first;
+ struct htt_rx_desc *rxd;
+ bool is_first_ppdu;
+ bool is_last_ppdu;
+
+ if (skb_queue_empty(amsdu))
+ return;
+
+ first = skb_peek(amsdu);
+ rxd = (void *)first->data - sizeof(*rxd);
+
+ is_first_ppdu = !!(rxd->attention.flags &
+ __cpu_to_le32(RX_ATTENTION_FLAGS_FIRST_MPDU));
+ is_last_ppdu = !!(rxd->attention.flags &
+ __cpu_to_le32(RX_ATTENTION_FLAGS_LAST_MPDU));
+
+ if (is_first_ppdu) {
+ /* New PPDU starts so clear out the old per-PPDU status. */
+ status->freq = 0;
+ status->rate_idx = 0;
+ status->vht_nss = 0;
+ status->vht_flag &= ~RX_VHT_FLAG_80MHZ;
+ status->flag &= ~(RX_FLAG_HT |
+ RX_FLAG_VHT |
+ RX_FLAG_SHORT_GI |
+ RX_FLAG_40MHZ |
+ RX_FLAG_MACTIME_END);
+ status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+
+ ath10k_htt_rx_h_signal(ar, status, rxd);
+ ath10k_htt_rx_h_channel(ar, status);
+ ath10k_htt_rx_h_rates(ar, status, rxd);
+ }
+
+ if (is_last_ppdu)
+ ath10k_htt_rx_h_mactime(ar, status, rxd);
+}
+
static const char * const tid_to_ac[] = {
"BE",
"BK",
@@ -892,6 +801,8 @@ static void ath10k_process_rx(struct ath10k *ar,
!!(status->flag & RX_FLAG_AMSDU_MORE));
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
skb->data, skb->len);
+ trace_ath10k_rx_hdr(ar, skb->data, skb->len);
+ trace_ath10k_rx_payload(ar, skb->data, skb->len);
ieee80211_rx(ar->hw, skb);
}
@@ -902,187 +813,263 @@ static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr)
return round_up(ieee80211_hdrlen(hdr->frame_control), 4);
}
-static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
- struct ieee80211_rx_status *rx_status,
- struct sk_buff *skb_in)
+static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ enum htt_rx_mpdu_encrypt_type enctype,
+ bool is_decrypted)
{
- struct ath10k *ar = htt->ar;
+ struct ieee80211_hdr *hdr;
struct htt_rx_desc *rxd;
- struct sk_buff *skb = skb_in;
- struct sk_buff *first;
- enum rx_msdu_decap_format fmt;
- enum htt_rx_mpdu_encrypt_type enctype;
+ size_t hdr_len;
+ size_t crypto_len;
+ bool is_first;
+ bool is_last;
+
+ rxd = (void *)msdu->data - sizeof(*rxd);
+ is_first = !!(rxd->msdu_end.info0 &
+ __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
+ is_last = !!(rxd->msdu_end.info0 &
+ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
+
+ /* Delivered decapped frame:
+ * [802.11 header]
+ * [crypto param] <-- can be trimmed if !fcs_err &&
+ * !decrypt_err && !peer_idx_invalid
+ * [amsdu header] <-- only if A-MSDU
+ * [rfc1042/llc]
+ * [payload]
+ * [FCS] <-- at end, needs to be trimmed
+ */
+
+ /* This probably shouldn't happen but warn just in case */
+ if (unlikely(WARN_ON_ONCE(!is_first)))
+ return;
+
+ /* This probably shouldn't happen but warn just in case */
+ if (unlikely(WARN_ON_ONCE(!(is_first && is_last))))
+ return;
+
+ skb_trim(msdu, msdu->len - FCS_LEN);
+
+ /* In most cases this will be true for sniffed frames. It makes sense
+ * to deliver them as-is without stripping the crypto param. This would
+ * also make sense for software based decryption (which is not
+ * implemented in ath10k).
+ *
+ * If there's no error then the frame is decrypted. At least that is
+ * the case for frames that come in via fragmented rx indication.
+ */
+ if (!is_decrypted)
+ return;
+
+ /* The payload is decrypted so strip crypto params. Start from tail
+ * since hdr is used to compute some stuff.
+ */
+
+ hdr = (void *)msdu->data;
+
+ /* Tail */
+ skb_trim(msdu, msdu->len - ath10k_htt_rx_crypto_tail_len(ar, enctype));
+
+ /* MMIC */
+ if (!ieee80211_has_morefrags(hdr->frame_control) &&
+ enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+ skb_trim(msdu, msdu->len - 8);
+
+ /* Head */
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
+
+ memmove((void *)msdu->data + crypto_len,
+ (void *)msdu->data, hdr_len);
+ skb_pull(msdu, crypto_len);
+}
+
+static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ const u8 first_hdr[64])
+{
struct ieee80211_hdr *hdr;
- u8 hdr_buf[64], da[ETH_ALEN], sa[ETH_ALEN], *qos;
- unsigned int hdr_len;
+ size_t hdr_len;
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
- rxd = (void *)skb->data - sizeof(*rxd);
- enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
- RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+ /* Delivered decapped frame:
+ * [nwifi 802.11 header] <-- replaced with 802.11 hdr
+ * [rfc1042/llc]
+ *
+ * Note: The nwifi header doesn't have QoS Control and is
+ * (always?) a 3addr frame.
+ *
+ * Note2: There's no A-MSDU subframe header. Even if it's part
+ * of an A-MSDU.
+ */
+
+ /* pull decapped header and copy SA & DA */
+ hdr = (struct ieee80211_hdr *)msdu->data;
+ hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
+ ether_addr_copy(da, ieee80211_get_DA(hdr));
+ ether_addr_copy(sa, ieee80211_get_SA(hdr));
+ skb_pull(msdu, hdr_len);
- hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+ /* push original 802.11 header */
+ hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
- memcpy(hdr_buf, hdr, hdr_len);
- hdr = (struct ieee80211_hdr *)hdr_buf;
-
- first = skb;
- while (skb) {
- void *decap_hdr;
- int len;
-
- rxd = (void *)skb->data - sizeof(*rxd);
- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
- RX_MSDU_START_INFO1_DECAP_FORMAT);
- decap_hdr = (void *)rxd->rx_hdr_status;
-
- skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
-
- /* First frame in an A-MSDU chain has more decapped data. */
- if (skb == first) {
- len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
- len += round_up(ath10k_htt_rx_crypto_param_len(ar,
- enctype), 4);
- decap_hdr += len;
- }
+ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
- switch (fmt) {
- case RX_MSDU_DECAP_RAW:
- /* remove trailing FCS */
- skb_trim(skb, skb->len - FCS_LEN);
- break;
- case RX_MSDU_DECAP_NATIVE_WIFI:
- /* pull decapped header and copy SA & DA */
- hdr = (struct ieee80211_hdr *)skb->data;
- hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
- ether_addr_copy(da, ieee80211_get_DA(hdr));
- ether_addr_copy(sa, ieee80211_get_SA(hdr));
- skb_pull(skb, hdr_len);
-
- /* push original 802.11 header */
- hdr = (struct ieee80211_hdr *)hdr_buf;
- hdr_len = ieee80211_hdrlen(hdr->frame_control);
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
-
- /* original A-MSDU header has the bit set but we're
- * not including A-MSDU subframe header */
- hdr = (struct ieee80211_hdr *)skb->data;
- qos = ieee80211_get_qos_ctl(hdr);
- qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
-
- /* original 802.11 header has a different DA and in
- * case of 4addr it may also have different SA
- */
- ether_addr_copy(ieee80211_get_DA(hdr), da);
- ether_addr_copy(ieee80211_get_SA(hdr), sa);
- break;
- case RX_MSDU_DECAP_ETHERNET2_DIX:
- /* strip ethernet header and insert decapped 802.11
- * header, amsdu subframe header and rfc1042 header */
+ /* original 802.11 header has a different DA and in
+ * case of 4addr it may also have different SA
+ */
+ hdr = (struct ieee80211_hdr *)msdu->data;
+ ether_addr_copy(ieee80211_get_DA(hdr), da);
+ ether_addr_copy(ieee80211_get_SA(hdr), sa);
+}
- len = 0;
- len += sizeof(struct rfc1042_hdr);
- len += sizeof(struct amsdu_subframe_hdr);
+static void *ath10k_htt_rx_h_find_rfc1042(struct ath10k *ar,
+ struct sk_buff *msdu,
+ enum htt_rx_mpdu_encrypt_type enctype)
+{
+ struct ieee80211_hdr *hdr;
+ struct htt_rx_desc *rxd;
+ size_t hdr_len, crypto_len;
+ void *rfc1042;
+ bool is_first, is_last, is_amsdu;
- skb_pull(skb, sizeof(struct ethhdr));
- memcpy(skb_push(skb, len), decap_hdr, len);
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
- break;
- case RX_MSDU_DECAP_8023_SNAP_LLC:
- /* insert decapped 802.11 header making a singly
- * A-MSDU */
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
- break;
- }
+ rxd = (void *)msdu->data - sizeof(*rxd);
+ hdr = (void *)rxd->rx_hdr_status;
- skb_in = skb;
- ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype, fmt,
- false);
- skb = skb->next;
- skb_in->next = NULL;
+ is_first = !!(rxd->msdu_end.info0 &
+ __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
+ is_last = !!(rxd->msdu_end.info0 &
+ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
+ is_amsdu = !(is_first && is_last);
- if (skb)
- rx_status->flag |= RX_FLAG_AMSDU_MORE;
- else
- rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
+ rfc1042 = hdr;
+
+ if (is_first) {
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
- ath10k_process_rx(htt->ar, rx_status, skb_in);
+ rfc1042 += round_up(hdr_len, 4) +
+ round_up(crypto_len, 4);
}
- /* FIXME: It might be nice to re-assemble the A-MSDU when there's a
- * monitor interface active for sniffing purposes. */
+ if (is_amsdu)
+ rfc1042 += sizeof(struct amsdu_subframe_hdr);
+
+ return rfc1042;
}
-static void ath10k_htt_rx_msdu(struct ath10k_htt *htt,
- struct ieee80211_rx_status *rx_status,
- struct sk_buff *skb)
+static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ const u8 first_hdr[64],
+ enum htt_rx_mpdu_encrypt_type enctype)
{
- struct ath10k *ar = htt->ar;
- struct htt_rx_desc *rxd;
struct ieee80211_hdr *hdr;
- enum rx_msdu_decap_format fmt;
- enum htt_rx_mpdu_encrypt_type enctype;
- int hdr_len;
+ struct ethhdr *eth;
+ size_t hdr_len;
void *rfc1042;
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
- /* This shouldn't happen. If it does than it may be a FW bug. */
- if (skb->next) {
- ath10k_warn(ar, "htt rx received chained non A-MSDU frame\n");
- ath10k_htt_rx_free_msdu_chain(skb->next);
- skb->next = NULL;
- }
+ /* Delivered decapped frame:
+ * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
+ * [payload]
+ */
- rxd = (void *)skb->data - sizeof(*rxd);
- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
- RX_MSDU_START_INFO1_DECAP_FORMAT);
- enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
- RX_MPDU_START_INFO0_ENCRYPT_TYPE);
- hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+ rfc1042 = ath10k_htt_rx_h_find_rfc1042(ar, msdu, enctype);
+ if (WARN_ON_ONCE(!rfc1042))
+ return;
+
+ /* pull decapped header and copy SA & DA */
+ eth = (struct ethhdr *)msdu->data;
+ ether_addr_copy(da, eth->h_dest);
+ ether_addr_copy(sa, eth->h_source);
+ skb_pull(msdu, sizeof(struct ethhdr));
+
+ /* push rfc1042/llc/snap */
+ memcpy(skb_push(msdu, sizeof(struct rfc1042_hdr)), rfc1042,
+ sizeof(struct rfc1042_hdr));
+
+ /* push original 802.11 header */
+ hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
- skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
+ /* original 802.11 header has a different DA and in
+ * case of 4addr it may also have different SA
+ */
+ hdr = (struct ieee80211_hdr *)msdu->data;
+ ether_addr_copy(ieee80211_get_DA(hdr), da);
+ ether_addr_copy(ieee80211_get_SA(hdr), sa);
+}
- switch (fmt) {
+static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ const u8 first_hdr[64])
+{
+ struct ieee80211_hdr *hdr;
+ size_t hdr_len;
+
+ /* Delivered decapped frame:
+ * [amsdu header] <-- replaced with 802.11 hdr
+ * [rfc1042/llc]
+ * [payload]
+ */
+
+ skb_pull(msdu, sizeof(struct amsdu_subframe_hdr));
+
+ hdr = (struct ieee80211_hdr *)first_hdr;
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
+}
+
+static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ u8 first_hdr[64],
+ enum htt_rx_mpdu_encrypt_type enctype,
+ bool is_decrypted)
+{
+ struct htt_rx_desc *rxd;
+ enum rx_msdu_decap_format decap;
+ struct ieee80211_hdr *hdr;
+
+ /* First msdu's decapped header:
+ * [802.11 header] <-- padded to 4 bytes long
+ * [crypto param] <-- padded to 4 bytes long
+ * [amsdu header] <-- only if A-MSDU
+ * [rfc1042/llc]
+ *
+ * Other (2nd, 3rd, ..) msdu's decapped header:
+ * [amsdu header] <-- only if A-MSDU
+ * [rfc1042/llc]
+ */
+
+ rxd = (void *)msdu->data - sizeof(*rxd);
+ hdr = (void *)rxd->rx_hdr_status;
+ decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
+ RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+ switch (decap) {
case RX_MSDU_DECAP_RAW:
- /* remove trailing FCS */
- skb_trim(skb, skb->len - FCS_LEN);
+ ath10k_htt_rx_h_undecap_raw(ar, msdu, status, enctype,
+ is_decrypted);
break;
case RX_MSDU_DECAP_NATIVE_WIFI:
- /* Pull decapped header */
- hdr = (struct ieee80211_hdr *)skb->data;
- hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
- skb_pull(skb, hdr_len);
-
- /* Push original header */
- hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
- hdr_len = ieee80211_hdrlen(hdr->frame_control);
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+ ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
break;
case RX_MSDU_DECAP_ETHERNET2_DIX:
- /* strip ethernet header and insert decapped 802.11 header and
- * rfc1042 header */
-
- rfc1042 = hdr;
- rfc1042 += roundup(hdr_len, 4);
- rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(ar,
- enctype), 4);
-
- skb_pull(skb, sizeof(struct ethhdr));
- memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
- rfc1042, sizeof(struct rfc1042_hdr));
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+ ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
- /* remove A-MSDU subframe header and insert
- * decapped 802.11 header. rfc1042 header is already there */
-
- skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+ ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
break;
}
-
- ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false);
-
- ath10k_process_rx(htt->ar, rx_status, skb);
}
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
@@ -1116,10 +1103,128 @@ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
return CHECKSUM_UNNECESSARY;
}
-static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
+static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
+{
+ msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
+}
+
+static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *status)
{
- struct sk_buff *next = msdu_head->next;
- struct sk_buff *to_free = next;
+ struct sk_buff *first;
+ struct sk_buff *last;
+ struct sk_buff *msdu;
+ struct htt_rx_desc *rxd;
+ struct ieee80211_hdr *hdr;
+ enum htt_rx_mpdu_encrypt_type enctype;
+ u8 first_hdr[64];
+ u8 *qos;
+ size_t hdr_len;
+ bool has_fcs_err;
+ bool has_crypto_err;
+ bool has_tkip_err;
+ bool has_peer_idx_invalid;
+ bool is_decrypted;
+ u32 attention;
+
+ if (skb_queue_empty(amsdu))
+ return;
+
+ first = skb_peek(amsdu);
+ rxd = (void *)first->data - sizeof(*rxd);
+
+ enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+ RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+ /* First MSDU's Rx descriptor in an A-MSDU contains full 802.11
+ * decapped header. It'll be used for undecapping of each MSDU.
+ */
+ hdr = (void *)rxd->rx_hdr_status;
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ memcpy(first_hdr, hdr, hdr_len);
+
+ /* Each A-MSDU subframe will use the original header as the base and be
+ * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
+ */
+ hdr = (void *)first_hdr;
+ qos = ieee80211_get_qos_ctl(hdr);
+ qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+ /* Some attention flags are valid only in the last MSDU. */
+ last = skb_peek_tail(amsdu);
+ rxd = (void *)last->data - sizeof(*rxd);
+ attention = __le32_to_cpu(rxd->attention.flags);
+
+ has_fcs_err = !!(attention & RX_ATTENTION_FLAGS_FCS_ERR);
+ has_crypto_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
+ has_tkip_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
+ has_peer_idx_invalid = !!(attention & RX_ATTENTION_FLAGS_PEER_IDX_INVALID);
+
+ /* Note: If hardware captures an encrypted frame that it can't decrypt,
+ * e.g. due to fcs error, missing peer or invalid key data it will
+ * report the frame as raw.
+ */
+ is_decrypted = (enctype != HTT_RX_MPDU_ENCRYPT_NONE &&
+ !has_fcs_err &&
+ !has_crypto_err &&
+ !has_peer_idx_invalid);
+
+ /* Clear per-MPDU flags while leaving per-PPDU flags intact. */
+ status->flag &= ~(RX_FLAG_FAILED_FCS_CRC |
+ RX_FLAG_MMIC_ERROR |
+ RX_FLAG_DECRYPTED |
+ RX_FLAG_IV_STRIPPED |
+ RX_FLAG_MMIC_STRIPPED);
+
+ if (has_fcs_err)
+ status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ if (has_tkip_err)
+ status->flag |= RX_FLAG_MMIC_ERROR;
+
+ if (is_decrypted)
+ status->flag |= RX_FLAG_DECRYPTED |
+ RX_FLAG_IV_STRIPPED |
+ RX_FLAG_MMIC_STRIPPED;
+
+ skb_queue_walk(amsdu, msdu) {
+ ath10k_htt_rx_h_csum_offload(msdu);
+ ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
+ is_decrypted);
+
+ /* Undecapping involves copying the original 802.11 header back
+ * to sk_buff. If frame is protected and hardware has decrypted
+ * it then remove the protected bit.
+ */
+ if (!is_decrypted)
+ continue;
+
+ hdr = (void *)msdu->data;
+ hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ }
+}
+
+static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *status)
+{
+ struct sk_buff *msdu;
+
+ while ((msdu = __skb_dequeue(amsdu))) {
+ /* Setup per-MSDU flags */
+ if (skb_queue_empty(amsdu))
+ status->flag &= ~RX_FLAG_AMSDU_MORE;
+ else
+ status->flag |= RX_FLAG_AMSDU_MORE;
+
+ ath10k_process_rx(ar, status, msdu);
+ }
+}
+
+static int ath10k_unchain_msdu(struct sk_buff_head *amsdu)
+{
+ struct sk_buff *skb, *first;
int space;
int total_len = 0;
@@ -1130,113 +1235,142 @@ static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
* skb?
*/
- msdu_head->next = NULL;
+ first = __skb_dequeue(amsdu);
/* Allocate total length all at once. */
- while (next) {
- total_len += next->len;
- next = next->next;
- }
+ skb_queue_walk(amsdu, skb)
+ total_len += skb->len;
- space = total_len - skb_tailroom(msdu_head);
+ space = total_len - skb_tailroom(first);
if ((space > 0) &&
- (pskb_expand_head(msdu_head, 0, space, GFP_ATOMIC) < 0)) {
+ (pskb_expand_head(first, 0, space, GFP_ATOMIC) < 0)) {
/* TODO: bump some rx-oom error stat */
/* put it back together so we can free the
* whole list at once.
*/
- msdu_head->next = to_free;
+ __skb_queue_head(amsdu, first);
return -1;
}
/* Walk list again, copying contents into
* msdu_head
*/
- next = to_free;
- while (next) {
- skb_copy_from_linear_data(next, skb_put(msdu_head, next->len),
- next->len);
- next = next->next;
+ while ((skb = __skb_dequeue(amsdu))) {
+ skb_copy_from_linear_data(skb, skb_put(first, skb->len),
+ skb->len);
+ dev_kfree_skb_any(skb);
}
- /* If here, we have consolidated skb. Free the
- * fragments and pass the main skb on up the
- * stack.
- */
- ath10k_htt_rx_free_msdu_chain(to_free);
+ __skb_queue_head(amsdu, first);
return 0;
}
-static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt,
- struct sk_buff *head,
- enum htt_rx_mpdu_status status,
- bool channel_set,
- u32 attention)
+static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ bool chained)
{
- struct ath10k *ar = htt->ar;
+ struct sk_buff *first;
+ struct htt_rx_desc *rxd;
+ enum rx_msdu_decap_format decap;
- if (head->len == 0) {
- ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt rx dropping due to zero-len\n");
- return false;
- }
+ first = skb_peek(amsdu);
+ rxd = (void *)first->data - sizeof(*rxd);
+ decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
+ RX_MSDU_START_INFO1_DECAP_FORMAT);
- if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) {
- ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt rx dropping due to decrypt-err\n");
- return false;
- }
+ if (!chained)
+ return;
- if (!channel_set) {
- ath10k_warn(ar, "no channel configured; ignoring frame!\n");
- return false;
+ /* FIXME: Current unchaining logic can only handle simple case of raw
+ * msdu chaining. If decapping is other than raw the chaining may be
+ * more complex and this isn't handled by the current code. Don't even
+ * try re-constructing such frames - it'll be pretty much garbage.
+ */
+ if (decap != RX_MSDU_DECAP_RAW ||
+ skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) {
+ __skb_queue_purge(amsdu);
+ return;
}
- /* Skip mgmt frames while we handle this in WMI */
- if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL ||
- attention & RX_ATTENTION_FLAGS_MGMT_TYPE) {
- ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
+ ath10k_unchain_msdu(amsdu);
+}
+
+static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct sk_buff *msdu;
+ struct htt_rx_desc *rxd;
+ bool is_mgmt;
+ bool has_fcs_err;
+
+ msdu = skb_peek(amsdu);
+ rxd = (void *)msdu->data - sizeof(*rxd);
+
+ /* FIXME: It might be a good idea to do some fuzzy-testing to drop
+ * invalid/dangerous frames.
+ */
+
+ if (!rx_status->freq) {
+ ath10k_warn(ar, "no channel configured; ignoring frame(s)!\n");
return false;
}
- if (status != HTT_RX_IND_MPDU_STATUS_OK &&
- status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
- status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
- !htt->ar->monitor_started) {
- ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt rx ignoring frame w/ status %d\n",
- status);
+ is_mgmt = !!(rxd->attention.flags &
+ __cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE));
+ has_fcs_err = !!(rxd->attention.flags &
+ __cpu_to_le32(RX_ATTENTION_FLAGS_FCS_ERR));
+
+ /* Management frames are handled via WMI events. The pros of such
+ * approach is that channel is explicitly provided in WMI events
+ * whereas HTT doesn't provide channel information for Rxed frames.
+ *
+ * However some firmware revisions don't report corrupted frames via
+ * WMI so don't drop them.
+ */
+ if (is_mgmt && !has_fcs_err) {
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
return false;
}
- if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
- ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt rx CAC running\n");
+ if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx cac running\n");
return false;
}
return true;
}
+static void ath10k_htt_rx_h_filter(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *rx_status)
+{
+ if (skb_queue_empty(amsdu))
+ return;
+
+ if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status))
+ return;
+
+ __skb_queue_purge(amsdu);
+}
+
static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
struct htt_rx_indication *rx)
{
struct ath10k *ar = htt->ar;
struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct htt_rx_indication_mpdu_range *mpdu_ranges;
- struct htt_rx_desc *rxd;
- enum htt_rx_mpdu_status status;
- struct ieee80211_hdr *hdr;
+ struct sk_buff_head amsdu;
int num_mpdu_ranges;
- u32 attention;
int fw_desc_len;
u8 *fw_desc;
- bool channel_set;
- int i, j;
- int ret;
+ int i, ret, mpdu_count = 0;
lockdep_assert_held(&htt->rx_ring.lock);
+ if (htt->rx_confused)
+ return;
+
fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
fw_desc = (u8 *)&rx->fw_desc;
@@ -1244,92 +1378,33 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
- /* Fill this once, while this is per-ppdu */
- if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_START_VALID) {
- memset(rx_status, 0, sizeof(*rx_status));
- rx_status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
- rx->ppdu.combined_rssi;
- }
-
- if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
- /* TSF available only in 32-bit */
- rx_status->mactime = __le32_to_cpu(rx->ppdu.tsf) & 0xffffffff;
- rx_status->flag |= RX_FLAG_MACTIME_END;
- }
-
- channel_set = ath10k_htt_rx_h_channel(htt->ar, rx_status);
-
- if (channel_set) {
- ath10k_htt_rx_h_rates(htt->ar, rx_status->band,
- rx->ppdu.info0,
- __le32_to_cpu(rx->ppdu.info1),
- __le32_to_cpu(rx->ppdu.info2),
- rx_status);
- }
-
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
rx, sizeof(*rx) +
(sizeof(struct htt_rx_indication_mpdu_range) *
num_mpdu_ranges));
- for (i = 0; i < num_mpdu_ranges; i++) {
- status = mpdu_ranges[i].mpdu_range_status;
-
- for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
- struct sk_buff *msdu_head, *msdu_tail;
-
- attention = 0;
- msdu_head = NULL;
- msdu_tail = NULL;
- ret = ath10k_htt_rx_amsdu_pop(htt,
- &fw_desc,
- &fw_desc_len,
- &msdu_head,
- &msdu_tail,
- &attention);
-
- if (ret < 0) {
- ath10k_warn(ar, "failed to pop amsdu from htt rx ring %d\n",
- ret);
- ath10k_htt_rx_free_msdu_chain(msdu_head);
- continue;
- }
-
- rxd = container_of((void *)msdu_head->data,
- struct htt_rx_desc,
- msdu_payload);
-
- if (!ath10k_htt_rx_amsdu_allowed(htt, msdu_head,
- status,
- channel_set,
- attention)) {
- ath10k_htt_rx_free_msdu_chain(msdu_head);
- continue;
- }
-
- if (ret > 0 &&
- ath10k_unchain_msdu(msdu_head) < 0) {
- ath10k_htt_rx_free_msdu_chain(msdu_head);
- continue;
- }
-
- if (attention & RX_ATTENTION_FLAGS_FCS_ERR)
- rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
- else
- rx_status->flag &= ~RX_FLAG_FAILED_FCS_CRC;
-
- if (attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
- rx_status->flag |= RX_FLAG_MMIC_ERROR;
- else
- rx_status->flag &= ~RX_FLAG_MMIC_ERROR;
-
- hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
-
- if (ath10k_htt_rx_hdr_is_amsdu(hdr))
- ath10k_htt_rx_amsdu(htt, rx_status, msdu_head);
- else
- ath10k_htt_rx_msdu(htt, rx_status, msdu_head);
+ for (i = 0; i < num_mpdu_ranges; i++)
+ mpdu_count += mpdu_ranges[i].mpdu_count;
+
+ while (mpdu_count--) {
+ __skb_queue_head_init(&amsdu);
+ ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc,
+ &fw_desc_len, &amsdu);
+ if (ret < 0) {
+ ath10k_warn(ar, "rx ring became corrupted: %d\n", ret);
+ __skb_queue_purge(&amsdu);
+ /* FIXME: It's probably a good idea to reboot the
+ * device instead of leaving it inoperable.
+ */
+ htt->rx_confused = true;
+ break;
}
+
+ ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
+ ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
}
tasklet_schedule(&htt->rx_replenish_task);
@@ -1339,108 +1414,44 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
struct htt_rx_fragment_indication *frag)
{
struct ath10k *ar = htt->ar;
- struct sk_buff *msdu_head, *msdu_tail;
- enum htt_rx_mpdu_encrypt_type enctype;
- struct htt_rx_desc *rxd;
- enum rx_msdu_decap_format fmt;
struct ieee80211_rx_status *rx_status = &htt->rx_status;
- struct ieee80211_hdr *hdr;
+ struct sk_buff_head amsdu;
int ret;
- bool tkip_mic_err;
- bool decrypt_err;
u8 *fw_desc;
- int fw_desc_len, hdrlen, paramlen;
- int trim;
- u32 attention = 0;
+ int fw_desc_len;
fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
fw_desc = (u8 *)frag->fw_msdu_rx_desc;
- msdu_head = NULL;
- msdu_tail = NULL;
+ __skb_queue_head_init(&amsdu);
spin_lock_bh(&htt->rx_ring.lock);
ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
- &msdu_head, &msdu_tail,
- &attention);
+ &amsdu);
spin_unlock_bh(&htt->rx_ring.lock);
+ tasklet_schedule(&htt->rx_replenish_task);
+
ath10k_dbg(ar, ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
if (ret) {
ath10k_warn(ar, "failed to pop amsdu from httr rx ring for fragmented rx %d\n",
ret);
- ath10k_htt_rx_free_msdu_chain(msdu_head);
+ __skb_queue_purge(&amsdu);
return;
}
- /* FIXME: implement signal strength */
- rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
-
- hdr = (struct ieee80211_hdr *)msdu_head->data;
- rxd = (void *)msdu_head->data - sizeof(*rxd);
- tkip_mic_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
- decrypt_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
- RX_MSDU_START_INFO1_DECAP_FORMAT);
-
- if (fmt != RX_MSDU_DECAP_RAW) {
- ath10k_warn(ar, "we dont support non-raw fragmented rx yet\n");
- dev_kfree_skb_any(msdu_head);
- goto end;
- }
-
- enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
- RX_MPDU_START_INFO0_ENCRYPT_TYPE);
- ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype, fmt,
- true);
- msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
-
- if (tkip_mic_err)
- ath10k_warn(ar, "tkip mic error\n");
-
- if (decrypt_err) {
- ath10k_warn(ar, "decryption err in fragmented rx\n");
- dev_kfree_skb_any(msdu_head);
- goto end;
- }
-
- if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) {
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
- paramlen = ath10k_htt_rx_crypto_param_len(ar, enctype);
-
- /* It is more efficient to move the header than the payload */
- memmove((void *)msdu_head->data + paramlen,
- (void *)msdu_head->data,
- hdrlen);
- skb_pull(msdu_head, paramlen);
- hdr = (struct ieee80211_hdr *)msdu_head->data;
- }
-
- /* remove trailing FCS */
- trim = 4;
-
- /* remove crypto trailer */
- trim += ath10k_htt_rx_crypto_tail_len(ar, enctype);
-
- /* last fragment of TKIP frags has MIC */
- if (!ieee80211_has_morefrags(hdr->frame_control) &&
- enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
- trim += 8;
-
- if (trim > msdu_head->len) {
- ath10k_warn(ar, "htt rx fragment: trailer longer than the frame itself? drop\n");
- dev_kfree_skb_any(msdu_head);
- goto end;
+ if (skb_queue_len(&amsdu) != 1) {
+ ath10k_warn(ar, "failed to pop frag amsdu: too many msdus\n");
+ __skb_queue_purge(&amsdu);
+ return;
}
- skb_trim(msdu_head, msdu_head->len - trim);
-
- ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
- msdu_head->data, msdu_head->len);
- ath10k_process_rx(htt->ar, rx_status, msdu_head);
+ ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
-end:
if (fw_desc_len > 0) {
ath10k_dbg(ar, ATH10K_DBG_HTT,
"expecting more fragmented rx in one indication %d\n",
@@ -1674,6 +1685,15 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
case HTT_T2H_MSG_TYPE_RX_DELBA:
ath10k_htt_rx_delba(ar, resp);
break;
+ case HTT_T2H_MSG_TYPE_PKTLOG: {
+ struct ath10k_pktlog_hdr *hdr =
+ (struct ath10k_pktlog_hdr *)resp->pktlog_msg.payload;
+
+ trace_ath10k_htt_pktlog(ar, resp->pktlog_msg.payload,
+ sizeof(*hdr) +
+ __le16_to_cpu(hdr->size));
+ break;
+ }
case HTT_T2H_MSG_TYPE_RX_FLUSH: {
/* Ignore this event because mac80211 takes care of Rx
* aggregation reordering.
@@ -1681,8 +1701,8 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
break;
}
default:
- ath10k_dbg(ar, ATH10K_DBG_HTT, "htt event (%d) not handled\n",
- resp->hdr.msg_type);
+ ath10k_warn(ar, "htt event (%d) not handled\n",
+ resp->hdr.msg_type);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
skb->data, skb->len);
break;
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index bd87a35201d8..4bc51d8a14a3 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -92,7 +92,6 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
struct ath10k *ar = htt->ar;
spin_lock_init(&htt->tx_lock);
- init_waitqueue_head(&htt->empty_tx_wq);
if (test_bit(ATH10K_FW_FEATURE_WMI_10X, htt->ar->fw_features))
htt->max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC;
@@ -555,14 +554,18 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
- skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID);
+ skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID);
+ skb_cb->htt.txbuf->cmd_tx.freq = __cpu_to_le16(skb_cb->htt.freq);
+ trace_ath10k_htt_tx(ar, msdu_id, msdu->len, vdev_id, tid);
ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu\n",
+ "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu freq %hu\n",
flags0, flags1, msdu->len, msdu_id, frags_paddr,
- (u32)skb_cb->paddr, vdev_id, tid);
+ (u32)skb_cb->paddr, vdev_id, tid, skb_cb->htt.freq);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
msdu->data, msdu->len);
+ trace_ath10k_tx_hdr(ar, msdu->data, msdu->len);
+ trace_ath10k_tx_payload(ar, msdu->data, msdu->len);
sg_items[0].transfer_id = 0;
sg_items[0].transfer_context = NULL;
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 3cf5702c1e7e..dfedfd0e0f34 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -20,15 +20,16 @@
#include "targaddrs.h"
+#define ATH10K_FW_DIR "ath10k"
+
/* QCA988X 1.0 definitions (unsupported) */
#define QCA988X_HW_1_0_CHIP_ID_REV 0x0
/* QCA988X 2.0 definitions */
#define QCA988X_HW_2_0_VERSION 0x4100016c
#define QCA988X_HW_2_0_CHIP_ID_REV 0x2
-#define QCA988X_HW_2_0_FW_DIR "ath10k/QCA988X/hw2.0"
+#define QCA988X_HW_2_0_FW_DIR ATH10K_FW_DIR "/QCA988X/hw2.0"
#define QCA988X_HW_2_0_FW_FILE "firmware.bin"
-#define QCA988X_HW_2_0_FW_3_FILE "firmware-3.bin"
#define QCA988X_HW_2_0_OTP_FILE "otp.bin"
#define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin"
#define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234
@@ -43,6 +44,8 @@
#define REG_DUMP_COUNT_QCA988X 60
+#define QCA988X_CAL_DATA_LEN 2116
+
struct ath10k_fw_ie {
__le32 id;
__le32 len;
@@ -78,6 +81,15 @@ enum ath10k_mcast2ucast_mode {
ATH10K_MCAST2UCAST_ENABLED = 1,
};
+struct ath10k_pktlog_hdr {
+ __le16 flags;
+ __le16 missed_cnt;
+ __le16 log_type;
+ __le16 size;
+ __le32 timestamp;
+ u8 payload[0];
+} __packed;
+
/* Target specific defines for MAIN firmware */
#define TARGET_NUM_VDEVS 8
#define TARGET_NUM_PEER_AST 2
@@ -85,11 +97,13 @@ enum ath10k_mcast2ucast_mode {
#define TARGET_DMA_BURST_SIZE 0
#define TARGET_MAC_AGGR_DELIM 0
#define TARGET_AST_SKID_LIMIT 16
-#define TARGET_NUM_PEERS 16
+#define TARGET_NUM_STATIONS 16
+#define TARGET_NUM_PEERS ((TARGET_NUM_STATIONS) + \
+ (TARGET_NUM_VDEVS))
#define TARGET_NUM_OFFLOAD_PEERS 0
#define TARGET_NUM_OFFLOAD_REORDER_BUFS 0
#define TARGET_NUM_PEER_KEYS 2
-#define TARGET_NUM_TIDS (2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS)))
+#define TARGET_NUM_TIDS ((TARGET_NUM_PEERS) * 2)
#define TARGET_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_RX_TIMEOUT_LO_PRI 100
@@ -120,12 +134,15 @@ enum ath10k_mcast2ucast_mode {
#define TARGET_10X_DMA_BURST_SIZE 0
#define TARGET_10X_MAC_AGGR_DELIM 0
#define TARGET_10X_AST_SKID_LIMIT 16
-#define TARGET_10X_NUM_PEERS (128 + (TARGET_10X_NUM_VDEVS))
-#define TARGET_10X_NUM_PEERS_MAX 128
+#define TARGET_10X_NUM_STATIONS 128
+#define TARGET_10X_NUM_PEERS ((TARGET_10X_NUM_STATIONS) + \
+ (TARGET_10X_NUM_VDEVS))
#define TARGET_10X_NUM_OFFLOAD_PEERS 0
#define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0
#define TARGET_10X_NUM_PEER_KEYS 2
-#define TARGET_10X_NUM_TIDS 256
+#define TARGET_10X_NUM_TIDS_MAX 256
+#define TARGET_10X_NUM_TIDS min((TARGET_10X_NUM_TIDS_MAX), \
+ (TARGET_10X_NUM_PEERS) * 2)
#define TARGET_10X_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_10X_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_10X_RX_TIMEOUT_LO_PRI 100
@@ -279,6 +296,7 @@ enum ath10k_mcast2ucast_mode {
#define SI_RX_DATA1_OFFSET 0x00000014
#define CORE_CTRL_CPU_INTR_MASK 0x00002000
+#define CORE_CTRL_PCIE_REG_31_MASK 0x00000800
#define CORE_CTRL_ADDRESS 0x0000
#define PCIE_INTR_ENABLE_ADDRESS 0x0008
#define PCIE_INTR_CAUSE_ADDRESS 0x000c
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 46709301a51e..c4005670cba2 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -136,7 +136,9 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
if (ret)
return ret;
+ spin_lock_bh(&ar->data_lock);
peer->keys[i] = arvif->wep_keys[i];
+ spin_unlock_bh(&ar->data_lock);
}
return 0;
@@ -173,12 +175,39 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
ath10k_warn(ar, "failed to remove peer wep key %d: %d\n",
i, ret);
+ spin_lock_bh(&ar->data_lock);
peer->keys[i] = NULL;
+ spin_unlock_bh(&ar->data_lock);
}
return first_errno;
}
+bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
+ u8 keyidx)
+{
+ struct ath10k_peer *peer;
+ int i;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ /* We don't know which vdev this peer belongs to,
+ * since WMI doesn't give us that information.
+ *
+ * FIXME: multi-bss needs to be handled.
+ */
+ peer = ath10k_peer_find(ar, 0, addr);
+ if (!peer)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+ if (peer->keys[i] && peer->keys[i]->keyidx == keyidx)
+ return true;
+ }
+
+ return false;
+}
+
static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
struct ieee80211_key_conf *key)
{
@@ -326,6 +355,9 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
lockdep_assert_held(&ar->conf_mutex);
+ if (ar->num_peers >= ar->max_num_peers)
+ return -ENOBUFS;
+
ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
if (ret) {
ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n",
@@ -339,9 +371,8 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
addr, vdev_id, ret);
return ret;
}
- spin_lock_bh(&ar->data_lock);
+
ar->num_peers++;
- spin_unlock_bh(&ar->data_lock);
return 0;
}
@@ -391,15 +422,11 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
return 0;
}
-static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
{
struct ath10k *ar = arvif->ar;
u32 vdev_param;
- if (value != 0xFFFFFFFF)
- value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
- ATH10K_RTS_MAX);
-
vdev_param = ar->wmi.vdev_param->rts_threshold;
return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
}
@@ -432,9 +459,7 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
if (ret)
return ret;
- spin_lock_bh(&ar->data_lock);
ar->num_peers--;
- spin_unlock_bh(&ar->data_lock);
return 0;
}
@@ -471,20 +496,59 @@ static void ath10k_peer_cleanup_all(struct ath10k *ar)
list_del(&peer->list);
kfree(peer);
}
- ar->num_peers = 0;
spin_unlock_bh(&ar->data_lock);
+
+ ar->num_peers = 0;
+ ar->num_stations = 0;
}
/************************/
/* Interface management */
/************************/
+void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif)
+{
+ struct ath10k *ar = arvif->ar;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ if (!arvif->beacon)
+ return;
+
+ if (!arvif->beacon_buf)
+ dma_unmap_single(ar->dev, ATH10K_SKB_CB(arvif->beacon)->paddr,
+ arvif->beacon->len, DMA_TO_DEVICE);
+
+ dev_kfree_skb_any(arvif->beacon);
+
+ arvif->beacon = NULL;
+ arvif->beacon_sent = false;
+}
+
+static void ath10k_mac_vif_beacon_cleanup(struct ath10k_vif *arvif)
+{
+ struct ath10k *ar = arvif->ar;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ ath10k_mac_vif_beacon_free(arvif);
+
+ if (arvif->beacon_buf) {
+ dma_free_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN,
+ arvif->beacon_buf, arvif->beacon_paddr);
+ arvif->beacon_buf = NULL;
+ }
+}
+
static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return -ESHUTDOWN;
+
ret = wait_for_completion_timeout(&ar->vdev_setup_done,
ATH10K_VDEV_SETUP_TIMEOUT_HZ);
if (ret == 0)
@@ -517,6 +581,8 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
arg.channel.max_reg_power = channel->max_reg_power * 2;
arg.channel.max_antenna_gain = channel->max_antenna_gain * 2;
+ reinit_completion(&ar->vdev_setup_done);
+
ret = ath10k_wmi_vdev_start(ar, &arg);
if (ret) {
ath10k_warn(ar, "failed to request monitor vdev %i start: %d\n",
@@ -564,6 +630,8 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar)
ath10k_warn(ar, "failed to put down monitor vdev %i: %d\n",
ar->monitor_vdev_id, ret);
+ reinit_completion(&ar->vdev_setup_done);
+
ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
if (ret)
ath10k_warn(ar, "failed to to request monitor vdev %i stop: %d\n",
@@ -590,9 +658,9 @@ static int ath10k_monitor_vdev_create(struct ath10k *ar)
return -ENOMEM;
}
- bit = ffs(ar->free_vdev_map);
+ bit = __ffs64(ar->free_vdev_map);
- ar->monitor_vdev_id = bit - 1;
+ ar->monitor_vdev_id = bit;
ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id,
WMI_VDEV_TYPE_MONITOR,
@@ -603,7 +671,7 @@ static int ath10k_monitor_vdev_create(struct ath10k *ar)
return ret;
}
- ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id);
+ ar->free_vdev_map &= ~(1LL << ar->monitor_vdev_id);
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
ar->monitor_vdev_id);
@@ -623,7 +691,7 @@ static int ath10k_monitor_vdev_delete(struct ath10k *ar)
return ret;
}
- ar->free_vdev_map |= 1 << ar->monitor_vdev_id;
+ ar->free_vdev_map |= 1LL << ar->monitor_vdev_id;
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
ar->monitor_vdev_id);
@@ -909,15 +977,7 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
arvif->is_up = false;
spin_lock_bh(&arvif->ar->data_lock);
- if (arvif->beacon) {
- dma_unmap_single(arvif->ar->dev,
- ATH10K_SKB_CB(arvif->beacon)->paddr,
- arvif->beacon->len, DMA_TO_DEVICE);
- dev_kfree_skb_any(arvif->beacon);
-
- arvif->beacon = NULL;
- arvif->beacon_sent = false;
- }
+ ath10k_mac_vif_beacon_free(arvif);
spin_unlock_bh(&arvif->ar->data_lock);
return;
@@ -966,14 +1026,6 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
if (is_zero_ether_addr(arvif->bssid))
return;
- ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
- arvif->bssid);
- if (ret) {
- ath10k_warn(ar, "failed to delete IBSS BSSID peer %pM for vdev %d: %d\n",
- arvif->bssid, arvif->vdev_id, ret);
- return;
- }
-
memset(arvif->bssid, 0, ETH_ALEN);
return;
@@ -1042,51 +1094,45 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
/* Station management */
/**********************/
+static u32 ath10k_peer_assoc_h_listen_intval(struct ath10k *ar,
+ struct ieee80211_vif *vif)
+{
+ /* Some firmware revisions have unstable STA powersave when listen
+ * interval is set too high (e.g. 5). The symptoms are firmware doesn't
+ * generate NullFunc frames properly even if buffered frames have been
+ * indicated in Beacon TIM. Firmware would seldom wake up to pull
+ * buffered frames. Often pinging the device from AP would simply fail.
+ *
+ * As a workaround set it to 1.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION)
+ return 1;
+
+ return ar->hw->conf.listen_interval;
+}
+
static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
- struct ieee80211_bss_conf *bss_conf,
struct wmi_peer_assoc_complete_arg *arg)
{
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
lockdep_assert_held(&ar->conf_mutex);
ether_addr_copy(arg->addr, sta->addr);
arg->vdev_id = arvif->vdev_id;
arg->peer_aid = sta->aid;
arg->peer_flags |= WMI_PEER_AUTH;
-
- if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
- /*
- * Seems FW have problems with Power Save in STA
- * mode when we setup this parameter to high (eg. 5).
- * Often we see that FW don't send NULL (with clean P flags)
- * frame even there is info about buffered frames in beacons.
- * Sometimes we have to wait more than 10 seconds before FW
- * will wakeup. Often sending one ping from AP to our device
- * just fail (more than 50%).
- *
- * Seems setting this FW parameter to 1 couse FW
- * will check every beacon and will wakup immediately
- * after detection buffered data.
- */
- arg->peer_listen_intval = 1;
- else
- arg->peer_listen_intval = ar->hw->conf.listen_interval;
-
+ arg->peer_listen_intval = ath10k_peer_assoc_h_listen_intval(ar, vif);
arg->peer_num_spatial_streams = 1;
-
- /*
- * The assoc capabilities are available only in managed mode.
- */
- if (arvif->vdev_type == WMI_VDEV_TYPE_STA && bss_conf)
- arg->peer_caps = bss_conf->assoc_capability;
+ arg->peer_caps = vif->bss_conf.assoc_capability;
}
static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct wmi_peer_assoc_complete_arg *arg)
{
- struct ieee80211_vif *vif = arvif->vif;
struct ieee80211_bss_conf *info = &vif->bss_conf;
struct cfg80211_bss *bss;
const u8 *rsnie = NULL;
@@ -1343,11 +1389,12 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
}
static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
- struct ieee80211_bss_conf *bss_conf,
struct wmi_peer_assoc_complete_arg *arg)
{
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
switch (arvif->vdev_type) {
case WMI_VDEV_TYPE_AP:
if (sta->wme)
@@ -1359,7 +1406,7 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
}
break;
case WMI_VDEV_TYPE_STA:
- if (bss_conf->qos)
+ if (vif->bss_conf.qos)
arg->peer_flags |= WMI_PEER_QOS;
break;
default:
@@ -1368,7 +1415,7 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
}
static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct wmi_peer_assoc_complete_arg *arg)
{
@@ -1419,22 +1466,21 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
}
static int ath10k_peer_assoc_prepare(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
- struct ieee80211_bss_conf *bss_conf,
struct wmi_peer_assoc_complete_arg *arg)
{
lockdep_assert_held(&ar->conf_mutex);
memset(arg, 0, sizeof(*arg));
- ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, arg);
- ath10k_peer_assoc_h_crypto(ar, arvif, arg);
+ ath10k_peer_assoc_h_basic(ar, vif, sta, arg);
+ ath10k_peer_assoc_h_crypto(ar, vif, arg);
ath10k_peer_assoc_h_rates(ar, sta, arg);
ath10k_peer_assoc_h_ht(ar, sta, arg);
ath10k_peer_assoc_h_vht(ar, sta, arg);
- ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, arg);
- ath10k_peer_assoc_h_phymode(ar, arvif, sta, arg);
+ ath10k_peer_assoc_h_qos(ar, vif, sta, arg);
+ ath10k_peer_assoc_h_phymode(ar, vif, sta, arg);
return 0;
}
@@ -1480,6 +1526,9 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
lockdep_assert_held(&ar->conf_mutex);
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i assoc bssid %pM aid %d\n",
+ arvif->vdev_id, arvif->bssid, arvif->aid);
+
rcu_read_lock();
ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
@@ -1494,8 +1543,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
* before calling ath10k_setup_peer_smps() which might sleep. */
ht_cap = ap_sta->ht_cap;
- ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
- bss_conf, &peer_arg);
+ ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg);
if (ret) {
ath10k_warn(ar, "failed to prepare peer assoc for %pM vdev %i: %d\n",
bss_conf->bssid, arvif->vdev_id, ret);
@@ -1523,6 +1571,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
"mac vdev %d up (associated) bssid %pM aid %d\n",
arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+ WARN_ON(arvif->is_up);
+
arvif->aid = bss_conf->aid;
ether_addr_copy(arvif->bssid, bss_conf->bssid);
@@ -1536,9 +1586,6 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
arvif->is_up = true;
}
-/*
- * FIXME: flush TIDs
- */
static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
@@ -1548,45 +1595,30 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
lockdep_assert_held(&ar->conf_mutex);
- /*
- * For some reason, calling VDEV-DOWN before VDEV-STOP
- * makes the FW to send frames via HTT after disassociation.
- * No idea why this happens, even though VDEV-DOWN is supposed
- * to be analogous to link down, so just stop the VDEV.
- */
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n",
- arvif->vdev_id);
-
- /* FIXME: check return value */
- ret = ath10k_vdev_stop(arvif);
-
- /*
- * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
- * report beacons from previously associated network through HTT.
- * This in turn would spam mac80211 WARN_ON if we bring down all
- * interfaces as it expects there is no rx when no interface is
- * running.
- */
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id);
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i disassoc bssid %pM\n",
+ arvif->vdev_id, arvif->bssid);
- /* FIXME: why don't we print error if wmi call fails? */
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
+ if (ret)
+ ath10k_warn(ar, "faield to down vdev %i: %d\n",
+ arvif->vdev_id, ret);
arvif->def_wep_key_idx = 0;
-
- arvif->is_started = false;
arvif->is_up = false;
}
-static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
- struct ieee80211_sta *sta, bool reassoc)
+static int ath10k_station_assoc(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ bool reassoc)
{
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct wmi_peer_assoc_complete_arg peer_arg;
int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
- ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
+ ret = ath10k_peer_assoc_prepare(ar, vif, sta, &peer_arg);
if (ret) {
ath10k_warn(ar, "failed to prepare WMI peer assoc for %pM vdev %i: %i\n",
sta->addr, arvif->vdev_id, ret);
@@ -1601,43 +1633,51 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
return ret;
}
- ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap);
- if (ret) {
- ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n",
- arvif->vdev_id, ret);
- return ret;
- }
-
- if (!sta->wme && !reassoc) {
- arvif->num_legacy_stations++;
- ret = ath10k_recalc_rtscts_prot(arvif);
+ /* Re-assoc is run only to update supported rates for given station. It
+ * doesn't make much sense to reconfigure the peer completely.
+ */
+ if (!reassoc) {
+ ret = ath10k_setup_peer_smps(ar, arvif, sta->addr,
+ &sta->ht_cap);
if (ret) {
- ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
+ ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n",
arvif->vdev_id, ret);
return ret;
}
- }
- ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
- if (ret) {
- ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
- arvif->vdev_id, ret);
- return ret;
- }
+ ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
+ if (ret) {
+ ath10k_warn(ar, "failed to set qos params for STA %pM for vdev %i: %d\n",
+ sta->addr, arvif->vdev_id, ret);
+ return ret;
+ }
- ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
- if (ret) {
- ath10k_warn(ar, "failed to set qos params for STA %pM for vdev %i: %d\n",
- sta->addr, arvif->vdev_id, ret);
- return ret;
+ if (!sta->wme) {
+ arvif->num_legacy_stations++;
+ ret = ath10k_recalc_rtscts_prot(arvif);
+ if (ret) {
+ ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+ }
+
+ ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
+ if (ret) {
+ ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
}
return ret;
}
-static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
+static int ath10k_station_disassoc(struct ath10k *ar,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
@@ -1729,6 +1769,7 @@ static int ath10k_update_channel_list(struct ath10k *ar)
ch->passive = passive;
ch->freq = channel->center_freq;
+ ch->band_center_freq1 = channel->center_freq;
ch->min_power = 0;
ch->max_power = channel->max_power * 2;
ch->max_reg_power = channel->max_reg_power * 2;
@@ -1983,6 +2024,18 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
}
}
+static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
+{
+ /* FIXME: Not really sure since when the behaviour changed. At some
+ * point new firmware stopped requiring creation of peer entries for
+ * offchannel tx (and actually creating them causes issues with wmi-htc
+ * tx credit replenishment and reliability). Assuming it's at least 3.4
+ * because that's when the `freq` was introduced to TX_FRM HTT command.
+ */
+ return !(ar->htt.target_version_major >= 3 &&
+ ar->htt.target_version_minor >= 4);
+}
+
static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
@@ -2158,10 +2211,10 @@ void __ath10k_scan_finish(struct ath10k *ar)
case ATH10K_SCAN_IDLE:
break;
case ATH10K_SCAN_RUNNING:
- case ATH10K_SCAN_ABORTING:
if (ar->scan.is_roc)
ieee80211_remain_on_channel_expired(ar->hw);
- else
+ case ATH10K_SCAN_ABORTING:
+ if (!ar->scan.is_roc)
ieee80211_scan_completed(ar->hw,
(ar->scan.state ==
ATH10K_SCAN_ABORTING));
@@ -2327,23 +2380,28 @@ static void ath10k_tx(struct ieee80211_hw *hw,
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
spin_lock_bh(&ar->data_lock);
- ATH10K_SKB_CB(skb)->htt.is_offchan = true;
+ ATH10K_SKB_CB(skb)->htt.freq = ar->scan.roc_freq;
ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
spin_unlock_bh(&ar->data_lock);
- ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
- skb);
+ if (ath10k_mac_need_offchan_tx_work(ar)) {
+ ATH10K_SKB_CB(skb)->htt.freq = 0;
+ ATH10K_SKB_CB(skb)->htt.is_offchan = true;
- skb_queue_tail(&ar->offchan_tx_queue, skb);
- ieee80211_queue_work(hw, &ar->offchan_tx_work);
- return;
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
+ skb);
+
+ skb_queue_tail(&ar->offchan_tx_queue, skb);
+ ieee80211_queue_work(hw, &ar->offchan_tx_work);
+ return;
+ }
}
ath10k_tx_htt(ar, skb);
}
/* Must not be called with conf_mutex held as workers can use that also. */
-static void ath10k_drain_tx(struct ath10k *ar)
+void ath10k_drain_tx(struct ath10k *ar)
{
/* make sure rcu-protected mac80211 tx path itself is drained */
synchronize_net();
@@ -2376,16 +2434,8 @@ void ath10k_halt(struct ath10k *ar)
ath10k_hif_power_down(ar);
spin_lock_bh(&ar->data_lock);
- list_for_each_entry(arvif, &ar->arvifs, list) {
- if (!arvif->beacon)
- continue;
-
- dma_unmap_single(arvif->ar->dev,
- ATH10K_SKB_CB(arvif->beacon)->paddr,
- arvif->beacon->len, DMA_TO_DEVICE);
- dev_kfree_skb_any(arvif->beacon);
- arvif->beacon = NULL;
- }
+ list_for_each_entry(arvif, &ar->arvifs, list)
+ ath10k_mac_vif_beacon_cleanup(arvif);
spin_unlock_bh(&ar->data_lock);
}
@@ -2408,12 +2458,28 @@ static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
return 0;
}
+static void ath10k_check_chain_mask(struct ath10k *ar, u32 cm, const char *dbg)
+{
+ /* It is not clear that allowing gaps in chainmask
+ * is helpful. Probably it will not do what user
+ * is hoping for, so warn in that case.
+ */
+ if (cm == 15 || cm == 7 || cm == 3 || cm == 1 || cm == 0)
+ return;
+
+ ath10k_warn(ar, "mac %s antenna chainmask may be invalid: 0x%x. Suggested values: 15, 7, 3, 1 or 0.\n",
+ dbg, cm);
+}
+
static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
+ ath10k_check_chain_mask(ar, tx_ant, "tx");
+ ath10k_check_chain_mask(ar, rx_ant, "rx");
+
ar->cfg_tx_chainmask = tx_ant;
ar->cfg_rx_chainmask = rx_ant;
@@ -2677,12 +2743,68 @@ static void ath10k_config_chan(struct ath10k *ar)
ath10k_monitor_recalc(ar);
}
+static int ath10k_mac_txpower_setup(struct ath10k *ar, int txpower)
+{
+ int ret;
+ u32 param;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac txpower %d\n", txpower);
+
+ param = ar->wmi.pdev_param->txpower_limit2g;
+ ret = ath10k_wmi_pdev_set_param(ar, param, txpower * 2);
+ if (ret) {
+ ath10k_warn(ar, "failed to set 2g txpower %d: %d\n",
+ txpower, ret);
+ return ret;
+ }
+
+ param = ar->wmi.pdev_param->txpower_limit5g;
+ ret = ath10k_wmi_pdev_set_param(ar, param, txpower * 2);
+ if (ret) {
+ ath10k_warn(ar, "failed to set 5g txpower %d: %d\n",
+ txpower, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath10k_mac_txpower_recalc(struct ath10k *ar)
+{
+ struct ath10k_vif *arvif;
+ int ret, txpower = -1;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ WARN_ON(arvif->txpower < 0);
+
+ if (txpower == -1)
+ txpower = arvif->txpower;
+ else
+ txpower = min(txpower, arvif->txpower);
+ }
+
+ if (WARN_ON(txpower == -1))
+ return -EINVAL;
+
+ ret = ath10k_mac_txpower_setup(ar, txpower);
+ if (ret) {
+ ath10k_warn(ar, "failed to setup tx power %d: %d\n",
+ txpower, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
{
struct ath10k *ar = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
int ret = 0;
- u32 param;
mutex_lock(&ar->conf_mutex);
@@ -2706,25 +2828,6 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
}
}
- if (changed & IEEE80211_CONF_CHANGE_POWER) {
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac config power %d\n",
- hw->conf.power_level);
-
- param = ar->wmi.pdev_param->txpower_limit2g;
- ret = ath10k_wmi_pdev_set_param(ar, param,
- hw->conf.power_level * 2);
- if (ret)
- ath10k_warn(ar, "failed to set 2g txpower %d: %d\n",
- hw->conf.power_level, ret);
-
- param = ar->wmi.pdev_param->txpower_limit5g;
- ret = ath10k_wmi_pdev_set_param(ar, param,
- hw->conf.power_level * 2);
- if (ret)
- ath10k_warn(ar, "failed to set 5g txpower %d: %d\n",
- hw->conf.power_level, ret);
- }
-
if (changed & IEEE80211_CONF_CHANGE_PS)
ath10k_config_ps(ar);
@@ -2739,6 +2842,17 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
return ret;
}
+static u32 get_nss_from_chainmask(u16 chain_mask)
+{
+ if ((chain_mask & 0x15) == 0x15)
+ return 4;
+ else if ((chain_mask & 0x7) == 0x7)
+ return 3;
+ else if ((chain_mask & 0x3) == 0x3)
+ return 2;
+ return 1;
+}
+
/*
* TODO:
* Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
@@ -2772,9 +2886,12 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
ret = -EBUSY;
goto err;
}
- bit = ffs(ar->free_vdev_map);
+ bit = __ffs64(ar->free_vdev_map);
- arvif->vdev_id = bit - 1;
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac create vdev %i map %llx\n",
+ bit, ar->free_vdev_map);
+
+ arvif->vdev_id = bit;
arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
if (ar->p2p)
@@ -2804,8 +2921,39 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
break;
}
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n",
- arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
+ /* Some firmware revisions don't wait for beacon tx completion before
+ * sending another SWBA event. This could lead to hardware using old
+ * (freed) beacon data in some cases, e.g. tx credit starvation
+ * combined with missed TBTT. This is very very rare.
+ *
+ * On non-IOMMU-enabled hosts this could be a possible security issue
+ * because hw could beacon some random data on the air. On
+ * IOMMU-enabled hosts DMAR faults would occur in most cases and target
+ * device would crash.
+ *
+ * Since there are no beacon tx completions (implicit nor explicit)
+ * propagated to host the only workaround for this is to allocate a
+ * DMA-coherent buffer for a lifetime of a vif and use it for all
+ * beacon tx commands. Worst case for this approach is some beacons may
+ * become corrupted, e.g. have garbled IEs or out-of-date TIM bitmap.
+ */
+ if (vif->type == NL80211_IFTYPE_ADHOC ||
+ vif->type == NL80211_IFTYPE_AP) {
+ arvif->beacon_buf = dma_zalloc_coherent(ar->dev,
+ IEEE80211_MAX_FRAME_LEN,
+ &arvif->beacon_paddr,
+ GFP_ATOMIC);
+ if (!arvif->beacon_buf) {
+ ret = -ENOMEM;
+ ath10k_warn(ar, "failed to allocate beacon buffer: %d\n",
+ ret);
+ goto err;
+ }
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d bcnmode %s\n",
+ arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype,
+ arvif->beacon_buf ? "single-buf" : "per-skb");
ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
arvif->vdev_subtype, vif->addr);
@@ -2815,7 +2963,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
goto err;
}
- ar->free_vdev_map &= ~(1 << arvif->vdev_id);
+ ar->free_vdev_map &= ~(1LL << arvif->vdev_id);
list_add(&arvif->list, &ar->arvifs);
vdev_param = ar->wmi.vdev_param->def_keyid;
@@ -2837,6 +2985,20 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
goto err_vdev_delete;
}
+ if (ar->cfg_tx_chainmask) {
+ u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
+
+ vdev_param = ar->wmi.vdev_param->nss;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+ nss);
+ if (ret) {
+ ath10k_warn(ar, "failed to set vdev %i chainmask 0x%x, nss %i: %d\n",
+ arvif->vdev_id, ar->cfg_tx_chainmask, nss,
+ ret);
+ goto err_vdev_delete;
+ }
+ }
+
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
if (ret) {
@@ -2899,6 +3061,13 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
goto err_peer_delete;
}
+ arvif->txpower = vif->bss_conf.txpower;
+ ret = ath10k_mac_txpower_recalc(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
+ goto err_peer_delete;
+ }
+
mutex_unlock(&ar->conf_mutex);
return 0;
@@ -2908,10 +3077,16 @@ err_peer_delete:
err_vdev_delete:
ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
- ar->free_vdev_map |= 1 << arvif->vdev_id;
+ ar->free_vdev_map |= 1LL << arvif->vdev_id;
list_del(&arvif->list);
err:
+ if (arvif->beacon_buf) {
+ dma_free_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN,
+ arvif->beacon_buf, arvif->beacon_paddr);
+ arvif->beacon_buf = NULL;
+ }
+
mutex_unlock(&ar->conf_mutex);
return ret;
@@ -2924,19 +3099,12 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
int ret;
- mutex_lock(&ar->conf_mutex);
-
cancel_work_sync(&arvif->wep_key_work);
- spin_lock_bh(&ar->data_lock);
- if (arvif->beacon) {
- dma_unmap_single(arvif->ar->dev,
- ATH10K_SKB_CB(arvif->beacon)->paddr,
- arvif->beacon->len, DMA_TO_DEVICE);
- dev_kfree_skb_any(arvif->beacon);
- arvif->beacon = NULL;
- }
+ mutex_lock(&ar->conf_mutex);
+ spin_lock_bh(&ar->data_lock);
+ ath10k_mac_vif_beacon_cleanup(arvif);
spin_unlock_bh(&ar->data_lock);
ret = ath10k_spectral_vif_stop(arvif);
@@ -2944,7 +3112,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
ath10k_warn(ar, "failed to stop spectral for vdev %i: %d\n",
arvif->vdev_id, ret);
- ar->free_vdev_map |= 1 << arvif->vdev_id;
+ ar->free_vdev_map |= 1LL << arvif->vdev_id;
list_del(&arvif->list);
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
@@ -3068,54 +3236,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
arvif->u.ap.hidden_ssid = info->hidden_ssid;
}
- /*
- * Firmware manages AP self-peer internally so make sure to not create
- * it in driver. Otherwise AP self-peer deletion may timeout later.
- */
- if (changed & BSS_CHANGED_BSSID &&
- vif->type != NL80211_IFTYPE_AP) {
- if (!is_zero_ether_addr(info->bssid)) {
- ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac vdev %d create peer %pM\n",
- arvif->vdev_id, info->bssid);
-
- ret = ath10k_peer_create(ar, arvif->vdev_id,
- info->bssid);
- if (ret)
- ath10k_warn(ar, "failed to add peer %pM for vdev %d when changing bssid: %i\n",
- info->bssid, arvif->vdev_id, ret);
-
- if (vif->type == NL80211_IFTYPE_STATION) {
- /*
- * this is never erased as we it for crypto key
- * clearing; this is FW requirement
- */
- ether_addr_copy(arvif->bssid, info->bssid);
-
- ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac vdev %d start %pM\n",
- arvif->vdev_id, info->bssid);
-
- ret = ath10k_vdev_start(arvif);
- if (ret) {
- ath10k_warn(ar, "failed to start vdev %i: %d\n",
- arvif->vdev_id, ret);
- goto exit;
- }
-
- arvif->is_started = true;
- }
-
- /*
- * Mac80211 does not keep IBSS bssid when leaving IBSS,
- * so driver need to store it. It is needed when leaving
- * IBSS in order to remove BSSID peer.
- */
- if (vif->type == NL80211_IFTYPE_ADHOC)
- memcpy(arvif->bssid, info->bssid,
- ETH_ALEN);
- }
- }
+ if (changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid))
+ ether_addr_copy(arvif->bssid, info->bssid);
if (changed & BSS_CHANGED_BEACON_ENABLED)
ath10k_control_beaconing(arvif, info);
@@ -3177,10 +3299,21 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ath10k_monitor_stop(ar);
ath10k_bss_assoc(hw, vif, info);
ath10k_monitor_recalc(ar);
+ } else {
+ ath10k_bss_disassoc(hw, vif);
}
}
-exit:
+ if (changed & BSS_CHANGED_TXPOWER) {
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev_id %i txpower %d\n",
+ arvif->vdev_id, info->txpower);
+
+ arvif->txpower = info->txpower;
+ ret = ath10k_mac_txpower_recalc(ar);
+ if (ret)
+ ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
+ }
+
mutex_unlock(&ar->conf_mutex);
}
@@ -3266,9 +3399,10 @@ static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
mutex_lock(&ar->conf_mutex);
- cancel_delayed_work_sync(&ar->scan.timeout);
ath10k_scan_abort(ar);
mutex_unlock(&ar->conf_mutex);
+
+ cancel_delayed_work_sync(&ar->scan.timeout);
}
static void ath10k_set_key_h_def_keyidx(struct ath10k *ar,
@@ -3453,7 +3587,7 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk)
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
sta->addr);
- err = ath10k_station_assoc(ar, arvif, sta, true);
+ err = ath10k_station_assoc(ar, arvif->vif, sta, true);
if (err)
ath10k_warn(ar, "failed to reassociate station: %pM\n",
sta->addr);
@@ -3462,6 +3596,37 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk)
mutex_unlock(&ar->conf_mutex);
}
+static int ath10k_mac_inc_num_stations(struct ath10k_vif *arvif)
+{
+ struct ath10k *ar = arvif->ar;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+ arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+ return 0;
+
+ if (ar->num_stations >= ar->max_num_stations)
+ return -ENOBUFS;
+
+ ar->num_stations++;
+
+ return 0;
+}
+
+static void ath10k_mac_dec_num_stations(struct ath10k_vif *arvif)
+{
+ struct ath10k *ar = arvif->ar;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+ arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+ return;
+
+ ar->num_stations--;
+}
+
static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -3471,7 +3636,6 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
- int max_num_peers;
int ret = 0;
if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -3489,31 +3653,46 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
mutex_lock(&ar->conf_mutex);
if (old_state == IEEE80211_STA_NOTEXIST &&
- new_state == IEEE80211_STA_NONE &&
- vif->type != NL80211_IFTYPE_STATION) {
+ new_state == IEEE80211_STA_NONE) {
/*
* New station addition.
*/
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
- max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1;
- else
- max_num_peers = TARGET_NUM_PEERS;
+ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac vdev %d peer create %pM (new sta) sta %d / %d peer %d / %d\n",
+ arvif->vdev_id, sta->addr,
+ ar->num_stations + 1, ar->max_num_stations,
+ ar->num_peers + 1, ar->max_num_peers);
- if (ar->num_peers >= max_num_peers) {
- ath10k_warn(ar, "number of peers exceeded: peers number %d (max peers %d)\n",
- ar->num_peers, max_num_peers);
- ret = -ENOBUFS;
+ ret = ath10k_mac_inc_num_stations(arvif);
+ if (ret) {
+ ath10k_warn(ar, "refusing to associate station: too many connected already (%d)\n",
+ ar->max_num_stations);
goto exit;
}
- ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac vdev %d peer create %pM (new sta) num_peers %d\n",
- arvif->vdev_id, sta->addr, ar->num_peers);
-
ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
- if (ret)
+ if (ret) {
ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
sta->addr, arvif->vdev_id, ret);
+ ath10k_mac_dec_num_stations(arvif);
+ goto exit;
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ WARN_ON(arvif->is_started);
+
+ ret = ath10k_vdev_start(arvif);
+ if (ret) {
+ ath10k_warn(ar, "failed to start vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ WARN_ON(ath10k_peer_delete(ar, arvif->vdev_id,
+ sta->addr));
+ ath10k_mac_dec_num_stations(arvif);
+ goto exit;
+ }
+
+ arvif->is_started = true;
+ }
} else if ((old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST)) {
/*
@@ -3522,13 +3701,24 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
ath10k_dbg(ar, ATH10K_DBG_MAC,
"mac vdev %d peer delete %pM (sta gone)\n",
arvif->vdev_id, sta->addr);
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ WARN_ON(!arvif->is_started);
+
+ ret = ath10k_vdev_stop(arvif);
+ if (ret)
+ ath10k_warn(ar, "failed to stop vdev %i: %d\n",
+ arvif->vdev_id, ret);
+
+ arvif->is_started = false;
+ }
+
ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
if (ret)
ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
sta->addr, arvif->vdev_id, ret);
- if (vif->type == NL80211_IFTYPE_STATION)
- ath10k_bss_disassoc(hw, vif);
+ ath10k_mac_dec_num_stations(arvif);
} else if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC &&
(vif->type == NL80211_IFTYPE_AP ||
@@ -3539,7 +3729,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta %pM associated\n",
sta->addr);
- ret = ath10k_station_assoc(ar, arvif, sta, false);
+ ret = ath10k_station_assoc(ar, vif, sta, false);
if (ret)
ath10k_warn(ar, "failed to associate station %pM for vdev %i: %i\n",
sta->addr, arvif->vdev_id, ret);
@@ -3553,7 +3743,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta %pM disassociated\n",
sta->addr);
- ret = ath10k_station_disassoc(ar, arvif, sta);
+ ret = ath10k_station_disassoc(ar, vif, sta);
if (ret)
ath10k_warn(ar, "failed to disassociate station: %pM vdev %i: %i\n",
sta->addr, arvif->vdev_id, ret);
@@ -3717,6 +3907,8 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
if (ret)
goto exit;
+ duration = max(duration, WMI_SCAN_CHAN_MIN_TIME_MSEC);
+
memset(&arg, 0, sizeof(arg));
ath10k_wmi_start_scan_init(ar, &arg);
arg.vdev_id = arvif->vdev_id;
@@ -3761,10 +3953,11 @@ static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw)
struct ath10k *ar = hw->priv;
mutex_lock(&ar->conf_mutex);
- cancel_delayed_work_sync(&ar->scan.timeout);
ath10k_scan_abort(ar);
mutex_unlock(&ar->conf_mutex);
+ cancel_delayed_work_sync(&ar->scan.timeout);
+
return 0;
}
@@ -3807,7 +4000,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n",
arvif->vdev_id, value);
- ret = ath10k_mac_set_rts(arvif, value);
+ ret = ath10k_mac_set_frag(arvif, value);
if (ret) {
ath10k_warn(ar, "failed to set fragmentation threshold for vdev %d: %d\n",
arvif->vdev_id, ret);
@@ -3843,7 +4036,9 @@ static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
empty = (ar->htt.num_pending_tx == 0);
spin_unlock_bh(&ar->htt.tx_lock);
- skip = (ar->state == ATH10K_STATE_WEDGED);
+ skip = (ar->state == ATH10K_STATE_WEDGED) ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH,
+ &ar->dev_flags);
(empty || skip);
}), ATH10K_FLUSH_TIMEOUT_HZ);
@@ -3929,10 +4124,14 @@ exit:
}
#endif
-static void ath10k_restart_complete(struct ieee80211_hw *hw)
+static void ath10k_reconfig_complete(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type)
{
struct ath10k *ar = hw->priv;
+ if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART)
+ return;
+
mutex_lock(&ar->conf_mutex);
/* If device failed to restart it will be in a different state, e.g.
@@ -3940,6 +4139,7 @@ static void ath10k_restart_complete(struct ieee80211_hw *hw)
if (ar->state == ATH10K_STATE_RESTARTED) {
ath10k_info(ar, "device successfully recovered\n");
ar->state = ATH10K_STATE_ON;
+ ieee80211_wake_queues(ar->hw);
}
mutex_unlock(&ar->conf_mutex);
@@ -3975,6 +4175,9 @@ static int ath10k_get_survey(struct ieee80211_hw *hw, int idx,
survey->channel = &sband->channels[idx];
+ if (ar->rx_channel == survey->channel)
+ survey->filled |= SURVEY_INFO_IN_USE;
+
exit:
mutex_unlock(&ar->conf_mutex);
return ret;
@@ -4022,6 +4225,10 @@ ath10k_default_bitrate_mask(struct ath10k *ar,
u32 legacy = 0x00ff;
u8 ht = 0xff, i;
u16 vht = 0x3ff;
+ u16 nrf = ar->num_rf_chains;
+
+ if (ar->cfg_tx_chainmask)
+ nrf = get_nss_from_chainmask(ar->cfg_tx_chainmask);
switch (band) {
case IEEE80211_BAND_2GHZ:
@@ -4037,11 +4244,11 @@ ath10k_default_bitrate_mask(struct ath10k *ar,
if (mask->control[band].legacy != legacy)
return false;
- for (i = 0; i < ar->num_rf_chains; i++)
+ for (i = 0; i < nrf; i++)
if (mask->control[band].ht_mcs[i] != ht)
return false;
- for (i = 0; i < ar->num_rf_chains; i++)
+ for (i = 0; i < nrf; i++)
if (mask->control[band].vht_mcs[i] != vht)
return false;
@@ -4292,6 +4499,9 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
u8 fixed_nss = ar->num_rf_chains;
u8 force_sgi;
+ if (ar->cfg_tx_chainmask)
+ fixed_nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
+
force_sgi = mask->control[band].gi;
if (force_sgi == NL80211_TXRATE_FORCE_LGI)
return -EINVAL;
@@ -4450,12 +4660,15 @@ static const struct ieee80211_ops ath10k_ops = {
.tx_last_beacon = ath10k_tx_last_beacon,
.set_antenna = ath10k_set_antenna,
.get_antenna = ath10k_get_antenna,
- .restart_complete = ath10k_restart_complete,
+ .reconfig_complete = ath10k_reconfig_complete,
.get_survey = ath10k_get_survey,
.set_bitrate_mask = ath10k_set_bitrate_mask,
.sta_rc_update = ath10k_sta_rc_update,
.get_tsf = ath10k_get_tsf,
.ampdu_action = ath10k_ampdu_action,
+ .get_et_sset_count = ath10k_debug_get_et_sset_count,
+ .get_et_stats = ath10k_debug_get_et_stats,
+ .get_et_strings = ath10k_debug_get_et_strings,
CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
@@ -4800,15 +5013,6 @@ int ath10k_mac_register(struct ath10k *ar)
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP);
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
- /* TODO: Have to deal with 2x2 chips if/when the come out. */
- ar->supp_tx_chainmask = TARGET_10X_TX_CHAIN_MASK;
- ar->supp_rx_chainmask = TARGET_10X_RX_CHAIN_MASK;
- } else {
- ar->supp_tx_chainmask = TARGET_TX_CHAIN_MASK;
- ar->supp_rx_chainmask = TARGET_RX_CHAIN_MASK;
- }
-
ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask;
ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask;
@@ -4827,10 +5031,6 @@ int ath10k_mac_register(struct ath10k *ar)
IEEE80211_HW_AP_LINK_PS |
IEEE80211_HW_SPECTRUM_MGMT;
- /* MSDU can have HTT TX fragment pushed in front. The additional 4
- * bytes is used for padding/alignment if necessary. */
- ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
-
ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
@@ -4854,6 +5054,8 @@ int ath10k_mac_register(struct ath10k *ar)
ar->hw->wiphy->max_remain_on_channel_duration = 5000;
ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+ ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
+
/*
* on LL hardware queues are managed entirely by the FW
* so we only advertise to mac we can do the queues thing
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 6c80eeada3e2..68296117d203 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -21,6 +21,8 @@
#include <net/mac80211.h>
#include "core.h"
+#define WEP_KEYID_SHIFT 6
+
struct ath10k_generic_iter {
struct ath10k *ar;
int ret;
@@ -39,6 +41,10 @@ void ath10k_offchan_tx_work(struct work_struct *work);
void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar);
void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
void ath10k_halt(struct ath10k *ar);
+void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif);
+void ath10k_drain_tx(struct ath10k *ar);
+bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
+ u8 keyidx);
static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 59e0ea83be50..7abb8367119a 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -485,6 +485,8 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
void *data_buf = NULL;
int i;
+ spin_lock_bh(&ar_pci->ce_lock);
+
ce_diag = ar_pci->ce_diag;
/*
@@ -511,7 +513,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
nbytes = min_t(unsigned int, remaining_bytes,
DIAG_TRANSFER_LIMIT);
- ret = ath10k_ce_rx_post_buf(ce_diag, NULL, ce_data);
+ ret = __ath10k_ce_rx_post_buf(ce_diag, NULL, ce_data);
if (ret != 0)
goto done;
@@ -527,15 +529,15 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem,
address);
- ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0,
- 0);
+ ret = ath10k_ce_send_nolock(ce_diag, NULL, (u32)address, nbytes, 0,
+ 0);
if (ret)
goto done;
i = 0;
- while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
- &completed_nbytes,
- &id) != 0) {
+ while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf,
+ &completed_nbytes,
+ &id) != 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
ret = -EBUSY;
@@ -554,9 +556,9 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
}
i = 0;
- while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
- &completed_nbytes,
- &id, &flags) != 0) {
+ while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
+ &completed_nbytes,
+ &id, &flags) != 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
@@ -591,6 +593,8 @@ done:
dma_free_coherent(ar->dev, orig_nbytes, data_buf,
ce_data_base);
+ spin_unlock_bh(&ar_pci->ce_lock);
+
return ret;
}
@@ -648,6 +652,8 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
dma_addr_t ce_data_base = 0;
int i;
+ spin_lock_bh(&ar_pci->ce_lock);
+
ce_diag = ar_pci->ce_diag;
/*
@@ -688,7 +694,7 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT);
/* Set up to receive directly into Target(!) address */
- ret = ath10k_ce_rx_post_buf(ce_diag, NULL, address);
+ ret = __ath10k_ce_rx_post_buf(ce_diag, NULL, address);
if (ret != 0)
goto done;
@@ -696,15 +702,15 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
* Request CE to send caller-supplied data that
* was copied to bounce buffer to Target(!) address.
*/
- ret = ath10k_ce_send(ce_diag, NULL, (u32)ce_data,
- nbytes, 0, 0);
+ ret = ath10k_ce_send_nolock(ce_diag, NULL, (u32)ce_data,
+ nbytes, 0, 0);
if (ret != 0)
goto done;
i = 0;
- while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
- &completed_nbytes,
- &id) != 0) {
+ while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf,
+ &completed_nbytes,
+ &id) != 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
@@ -724,9 +730,9 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
}
i = 0;
- while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
- &completed_nbytes,
- &id, &flags) != 0) {
+ while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
+ &completed_nbytes,
+ &id, &flags) != 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
@@ -760,6 +766,8 @@ done:
ath10k_warn(ar, "failed to write diag value at 0x%x: %d\n",
address, ret);
+ spin_unlock_bh(&ar_pci->ce_lock);
+
return ret;
}
@@ -815,20 +823,24 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
- void *transfer_context;
+ struct sk_buff_head list;
+ struct sk_buff *skb;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
- while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
- &ce_data, &nbytes,
- &transfer_id) == 0) {
+ __skb_queue_head_init(&list);
+ while (ath10k_ce_completed_send_next(ce_state, (void **)&skb, &ce_data,
+ &nbytes, &transfer_id) == 0) {
/* no need to call tx completion for NULL pointers */
- if (transfer_context == NULL)
+ if (skb == NULL)
continue;
- cb->tx_completion(ar, transfer_context, transfer_id);
+ __skb_queue_tail(&list, skb);
}
+
+ while ((skb = __skb_dequeue(&list)))
+ cb->tx_completion(ar, skb);
}
/* Called by lower (CE) layer when data is received from the Target. */
@@ -839,12 +851,14 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
struct sk_buff *skb;
+ struct sk_buff_head list;
void *transfer_context;
u32 ce_data;
unsigned int nbytes, max_nbytes;
unsigned int transfer_id;
unsigned int flags;
+ __skb_queue_head_init(&list);
while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
&ce_data, &nbytes, &transfer_id,
&flags) == 0) {
@@ -861,7 +875,16 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
}
skb_put(skb, nbytes);
- cb->rx_completion(ar, skb, pipe_info->pipe_num);
+ __skb_queue_tail(&list, skb);
+ }
+
+ while ((skb = __skb_dequeue(&list))) {
+ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci rx ce pipe %d len %d\n",
+ ce_state->id, skb->len);
+ ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci rx: ",
+ skb->data, skb->len);
+
+ cb->rx_completion(ar, skb);
}
ath10k_pci_rx_post_pipe(pipe_info);
@@ -936,6 +959,12 @@ err:
return err;
}
+static int ath10k_pci_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len)
+{
+ return ath10k_pci_diag_read_mem(ar, address, buf, buf_len);
+}
+
static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -986,6 +1015,8 @@ static void ath10k_pci_fw_crashed_dump(struct ath10k *ar)
spin_lock_bh(&ar->data_lock);
+ ar->stats.fw_crash_counter++;
+
crash_data = ath10k_debug_get_new_fw_crash_data(ar);
if (crash_data)
@@ -1121,14 +1152,37 @@ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
&dl_is_polled);
}
-static void ath10k_pci_irq_disable(struct ath10k *ar)
+static void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar)
{
- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- int i;
+ u32 val;
+
+ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS);
+ val &= ~CORE_CTRL_PCIE_REG_31_MASK;
+
+ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS, val);
+}
+static void ath10k_pci_irq_msi_fw_unmask(struct ath10k *ar)
+{
+ u32 val;
+
+ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS);
+ val |= CORE_CTRL_PCIE_REG_31_MASK;
+
+ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS, val);
+}
+
+static void ath10k_pci_irq_disable(struct ath10k *ar)
+{
ath10k_ce_disable_interrupts(ar);
ath10k_pci_disable_and_clear_legacy_irq(ar);
- /* FIXME: How to mask all MSI interrupts? */
+ ath10k_pci_irq_msi_fw_mask(ar);
+}
+
+static void ath10k_pci_irq_sync(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ int i;
for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
synchronize_irq(ar_pci->pdev->irq + i);
@@ -1138,7 +1192,7 @@ static void ath10k_pci_irq_enable(struct ath10k *ar)
{
ath10k_ce_enable_interrupts(ar);
ath10k_pci_enable_legacy_irq(ar);
- /* FIXME: How to unmask all MSI interrupts? */
+ ath10k_pci_irq_msi_fw_unmask(ar);
}
static int ath10k_pci_hif_start(struct ath10k *ar)
@@ -1151,64 +1205,74 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
return 0;
}
-static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
+static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
{
struct ath10k *ar;
- struct ath10k_pci *ar_pci;
- struct ath10k_ce_pipe *ce_hdl;
- u32 buf_sz;
- struct sk_buff *netbuf;
- u32 ce_data;
+ struct ath10k_ce_pipe *ce_pipe;
+ struct ath10k_ce_ring *ce_ring;
+ struct sk_buff *skb;
+ int i;
- buf_sz = pipe_info->buf_sz;
+ ar = pci_pipe->hif_ce_state;
+ ce_pipe = pci_pipe->ce_hdl;
+ ce_ring = ce_pipe->dest_ring;
- /* Unused Copy Engine */
- if (buf_sz == 0)
+ if (!ce_ring)
return;
- ar = pipe_info->hif_ce_state;
- ar_pci = ath10k_pci_priv(ar);
- ce_hdl = pipe_info->ce_hdl;
+ if (!pci_pipe->buf_sz)
+ return;
+
+ for (i = 0; i < ce_ring->nentries; i++) {
+ skb = ce_ring->per_transfer_context[i];
+ if (!skb)
+ continue;
+
+ ce_ring->per_transfer_context[i] = NULL;
- while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf,
- &ce_data) == 0) {
- dma_unmap_single(ar->dev, ATH10K_SKB_CB(netbuf)->paddr,
- netbuf->len + skb_tailroom(netbuf),
+ dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+ skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
- dev_kfree_skb_any(netbuf);
+ dev_kfree_skb_any(skb);
}
}
-static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
+static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
{
struct ath10k *ar;
struct ath10k_pci *ar_pci;
- struct ath10k_ce_pipe *ce_hdl;
- struct sk_buff *netbuf;
- u32 ce_data;
- unsigned int nbytes;
+ struct ath10k_ce_pipe *ce_pipe;
+ struct ath10k_ce_ring *ce_ring;
+ struct ce_desc *ce_desc;
+ struct sk_buff *skb;
unsigned int id;
- u32 buf_sz;
+ int i;
- buf_sz = pipe_info->buf_sz;
+ ar = pci_pipe->hif_ce_state;
+ ar_pci = ath10k_pci_priv(ar);
+ ce_pipe = pci_pipe->ce_hdl;
+ ce_ring = ce_pipe->src_ring;
- /* Unused Copy Engine */
- if (buf_sz == 0)
+ if (!ce_ring)
return;
- ar = pipe_info->hif_ce_state;
- ar_pci = ath10k_pci_priv(ar);
- ce_hdl = pipe_info->ce_hdl;
+ if (!pci_pipe->buf_sz)
+ return;
- while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
- &ce_data, &nbytes, &id) == 0) {
- /* no need to call tx completion for NULL pointers */
- if (!netbuf)
+ ce_desc = ce_ring->shadow_base;
+ if (WARN_ON(!ce_desc))
+ return;
+
+ for (i = 0; i < ce_ring->nentries; i++) {
+ skb = ce_ring->per_transfer_context[i];
+ if (!skb)
continue;
- ar_pci->msg_callbacks_current.tx_completion(ar,
- netbuf,
- id);
+ ce_ring->per_transfer_context[i] = NULL;
+ id = MS(__le16_to_cpu(ce_desc[i].flags),
+ CE_DESC_FLAGS_META_DATA);
+
+ ar_pci->msg_callbacks_current.tx_completion(ar, skb);
}
}
@@ -1266,6 +1330,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
ath10k_pci_warm_reset(ar);
ath10k_pci_irq_disable(ar);
+ ath10k_pci_irq_sync(ar);
ath10k_pci_flush(ar);
}
@@ -1386,6 +1451,9 @@ static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
&nbytes, &transfer_id, &flags))
return;
+ if (WARN_ON_ONCE(!xfer))
+ return;
+
if (!xfer->wait_for_resp) {
ath10k_warn(ar, "unexpected: BMI data received; ignoring\n");
return;
@@ -1569,23 +1637,40 @@ static int ath10k_pci_init_config(struct ath10k *ar)
return 0;
}
-static int ath10k_pci_alloc_ce(struct ath10k *ar)
+static int ath10k_pci_alloc_pipes(struct ath10k *ar)
{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ struct ath10k_pci_pipe *pipe;
int i, ret;
for (i = 0; i < CE_COUNT; i++) {
- ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]);
+ pipe = &ar_pci->pipe_info[i];
+ pipe->ce_hdl = &ar_pci->ce_states[i];
+ pipe->pipe_num = i;
+ pipe->hif_ce_state = ar;
+
+ ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i],
+ ath10k_pci_ce_send_done,
+ ath10k_pci_ce_recv_data);
if (ret) {
ath10k_err(ar, "failed to allocate copy engine pipe %d: %d\n",
i, ret);
return ret;
}
+
+ /* Last CE is Diagnostic Window */
+ if (i == CE_COUNT - 1) {
+ ar_pci->ce_diag = pipe->ce_hdl;
+ continue;
+ }
+
+ pipe->buf_sz = (size_t)(host_ce_config_wlan[i].src_sz_max);
}
return 0;
}
-static void ath10k_pci_free_ce(struct ath10k *ar)
+static void ath10k_pci_free_pipes(struct ath10k *ar)
{
int i;
@@ -1593,39 +1678,17 @@ static void ath10k_pci_free_ce(struct ath10k *ar)
ath10k_ce_free_pipe(ar, i);
}
-static int ath10k_pci_ce_init(struct ath10k *ar)
+static int ath10k_pci_init_pipes(struct ath10k *ar)
{
- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- struct ath10k_pci_pipe *pipe_info;
- const struct ce_attr *attr;
- int pipe_num, ret;
+ int i, ret;
- for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
- pipe_info = &ar_pci->pipe_info[pipe_num];
- pipe_info->ce_hdl = &ar_pci->ce_states[pipe_num];
- pipe_info->pipe_num = pipe_num;
- pipe_info->hif_ce_state = ar;
- attr = &host_ce_config_wlan[pipe_num];
-
- ret = ath10k_ce_init_pipe(ar, pipe_num, attr,
- ath10k_pci_ce_send_done,
- ath10k_pci_ce_recv_data);
+ for (i = 0; i < CE_COUNT; i++) {
+ ret = ath10k_ce_init_pipe(ar, i, &host_ce_config_wlan[i]);
if (ret) {
ath10k_err(ar, "failed to initialize copy engine pipe %d: %d\n",
- pipe_num, ret);
+ i, ret);
return ret;
}
-
- if (pipe_num == CE_COUNT - 1) {
- /*
- * Reserve the ultimate CE for
- * diagnostic Window support
- */
- ar_pci->ce_diag = pipe_info->ce_hdl;
- continue;
- }
-
- pipe_info->buf_sz = (size_t)(attr->src_sz_max);
}
return 0;
@@ -1666,93 +1729,167 @@ static void ath10k_pci_warm_reset_si0(struct ath10k *ar)
msleep(10);
}
-static int ath10k_pci_warm_reset(struct ath10k *ar)
+static void ath10k_pci_warm_reset_cpu(struct ath10k *ar)
{
u32 val;
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset\n");
-
- /* debug */
- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_CAUSE_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n",
- val);
+ ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, 0);
- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
- CPU_INTR_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
- val);
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
+}
- /* disable pending irqs */
- ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_ENABLE_ADDRESS, 0);
+static void ath10k_pci_warm_reset_ce(struct ath10k *ar)
+{
+ u32 val;
- ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_CLR_ADDRESS, ~0);
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
- msleep(100);
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val | SOC_RESET_CONTROL_CE_RST_MASK);
+ msleep(10);
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val & ~SOC_RESET_CONTROL_CE_RST_MASK);
+}
- /* clear fw indicator */
- ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, 0);
+static void ath10k_pci_warm_reset_clear_lf(struct ath10k *ar)
+{
+ u32 val;
- /* clear target LF timer interrupts */
val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
SOC_LF_TIMER_CONTROL0_ADDRESS);
ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
SOC_LF_TIMER_CONTROL0_ADDRESS,
val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
+}
- /* reset CE */
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
- val | SOC_RESET_CONTROL_CE_RST_MASK);
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- msleep(10);
+static int ath10k_pci_warm_reset(struct ath10k *ar)
+{
+ int ret;
- /* unreset CE */
- ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
- val & ~SOC_RESET_CONTROL_CE_RST_MASK);
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- msleep(10);
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset\n");
+
+ spin_lock_bh(&ar->data_lock);
+ ar->stats.fw_warm_reset_counter++;
+ spin_unlock_bh(&ar->data_lock);
+ ath10k_pci_irq_disable(ar);
+
+ /* Make sure the target CPU is not doing anything dangerous, e.g. if it
+ * were to access copy engine while host performs copy engine reset
+ * then it is possible for the device to confuse pci-e controller to
+ * the point of bringing host system to a complete stop (i.e. hang).
+ */
ath10k_pci_warm_reset_si0(ar);
+ ath10k_pci_warm_reset_cpu(ar);
+ ath10k_pci_init_pipes(ar);
+ ath10k_pci_wait_for_target_init(ar);
- /* debug */
- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_CAUSE_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n",
- val);
+ ath10k_pci_warm_reset_clear_lf(ar);
+ ath10k_pci_warm_reset_ce(ar);
+ ath10k_pci_warm_reset_cpu(ar);
+ ath10k_pci_init_pipes(ar);
- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
- CPU_INTR_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
- val);
+ ret = ath10k_pci_wait_for_target_init(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to wait for target init: %d\n", ret);
+ return ret;
+ }
- /* CPU warm reset */
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
- val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset complete\n");
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n",
- val);
+ return 0;
+}
- msleep(100);
+static int ath10k_pci_chip_reset(struct ath10k *ar)
+{
+ int i, ret;
+ u32 val;
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset complete\n");
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset\n");
+
+ /* Some hardware revisions (e.g. CUS223v2) has issues with cold reset.
+ * It is thus preferred to use warm reset which is safer but may not be
+ * able to recover the device from all possible fail scenarios.
+ *
+ * Warm reset doesn't always work on first try so attempt it a few
+ * times before giving up.
+ */
+ for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) {
+ ret = ath10k_pci_warm_reset(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to warm reset attempt %d of %d: %d\n",
+ i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS,
+ ret);
+ continue;
+ }
+
+ /* FIXME: Sometimes copy engine doesn't recover after warm
+ * reset. In most cases this needs cold reset. In some of these
+ * cases the device is in such a state that a cold reset may
+ * lock up the host.
+ *
+ * Reading any host interest register via copy engine is
+ * sufficient to verify if device is capable of booting
+ * firmware blob.
+ */
+ ret = ath10k_pci_init_pipes(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to init copy engine: %d\n",
+ ret);
+ continue;
+ }
+
+ ret = ath10k_pci_diag_read32(ar, QCA988X_HOST_INTEREST_ADDRESS,
+ &val);
+ if (ret) {
+ ath10k_warn(ar, "failed to poke copy engine: %d\n",
+ ret);
+ continue;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset complete (warm)\n");
+ return 0;
+ }
+
+ if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY) {
+ ath10k_warn(ar, "refusing cold reset as requested\n");
+ return -EPERM;
+ }
+
+ ret = ath10k_pci_cold_reset(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to cold reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = ath10k_pci_wait_for_target_init(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to wait for target after cold reset: %d\n",
+ ret);
+ return ret;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset complete (cold)\n");
return 0;
}
-static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
{
int ret;
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power up\n");
+
+ ret = ath10k_pci_wake(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to wake up target: %d\n", ret);
+ return ret;
+ }
+
/*
* Bring the target up cleanly.
*
@@ -1763,26 +1900,16 @@ static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
* is in an unexpected state. We try to catch that here in order to
* reset the Target and retry the probe.
*/
- if (cold_reset)
- ret = ath10k_pci_cold_reset(ar);
- else
- ret = ath10k_pci_warm_reset(ar);
-
+ ret = ath10k_pci_chip_reset(ar);
if (ret) {
- ath10k_err(ar, "failed to reset target: %d\n", ret);
- goto err;
+ ath10k_err(ar, "failed to reset chip: %d\n", ret);
+ goto err_sleep;
}
- ret = ath10k_pci_ce_init(ar);
+ ret = ath10k_pci_init_pipes(ar);
if (ret) {
ath10k_err(ar, "failed to initialize CE: %d\n", ret);
- goto err;
- }
-
- ret = ath10k_pci_wait_for_target_init(ar);
- if (ret) {
- ath10k_err(ar, "failed to wait for target to init: %d\n", ret);
- goto err_ce;
+ goto err_sleep;
}
ret = ath10k_pci_init_config(ar);
@@ -1801,73 +1928,21 @@ static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
err_ce:
ath10k_pci_ce_deinit(ar);
- ath10k_pci_warm_reset(ar);
-err:
- return ret;
-}
-
-static int ath10k_pci_hif_power_up_warm(struct ath10k *ar)
-{
- int i, ret;
-
- /*
- * Sometime warm reset succeeds after retries.
- *
- * FIXME: It might be possible to tune ath10k_pci_warm_reset() to work
- * at first try.
- */
- for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) {
- ret = __ath10k_pci_hif_power_up(ar, false);
- if (ret == 0)
- break;
-
- ath10k_warn(ar, "failed to warm reset (attempt %d out of %d): %d\n",
- i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS, ret);
- }
+err_sleep:
+ ath10k_pci_sleep(ar);
return ret;
}
-static int ath10k_pci_hif_power_up(struct ath10k *ar)
-{
- int ret;
-
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power up\n");
-
- /*
- * Hardware CUS232 version 2 has some issues with cold reset and the
- * preferred (and safer) way to perform a device reset is through a
- * warm reset.
- *
- * Warm reset doesn't always work though so fall back to cold reset may
- * be necessary.
- */
- ret = ath10k_pci_hif_power_up_warm(ar);
- if (ret) {
- ath10k_warn(ar, "failed to power up target using warm reset: %d\n",
- ret);
-
- if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY)
- return ret;
-
- ath10k_warn(ar, "trying cold reset\n");
-
- ret = __ath10k_pci_hif_power_up(ar, true);
- if (ret) {
- ath10k_err(ar, "failed to power up target using cold reset too (%d)\n",
- ret);
- return ret;
- }
- }
-
- return 0;
-}
-
static void ath10k_pci_hif_power_down(struct ath10k *ar)
{
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n");
- ath10k_pci_warm_reset(ar);
+ /* Currently hif_power_up performs effectively a reset and hif_stop
+ * resets the chip as well so there's no point in resetting here.
+ */
+
+ ath10k_pci_sleep(ar);
}
#ifdef CONFIG_PM
@@ -1921,6 +1996,8 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
.tx_sg = ath10k_pci_hif_tx_sg,
+ .diag_read = ath10k_pci_hif_diag_read,
+ .diag_write = ath10k_pci_diag_write_mem,
.exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg,
.start = ath10k_pci_hif_start,
.stop = ath10k_pci_hif_stop,
@@ -1931,6 +2008,8 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
.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,
+ .read32 = ath10k_pci_read32,
+ .write32 = ath10k_pci_write32,
#ifdef CONFIG_PM
.suspend = ath10k_pci_hif_suspend,
.resume = ath10k_pci_hif_resume,
@@ -2250,14 +2329,14 @@ static int ath10k_pci_wait_for_target_init(struct ath10k *ar)
if (ar_pci->num_msi_intrs == 0)
/* Fix potential race by repeating CORE_BASE writes */
- ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_ENABLE_ADDRESS,
- PCIE_INTR_FIRMWARE_MASK |
- PCIE_INTR_CE_MASK_ALL);
+ ath10k_pci_enable_legacy_irq(ar);
mdelay(10);
} while (time_before(jiffies, timeout));
+ ath10k_pci_disable_and_clear_legacy_irq(ar);
+ ath10k_pci_irq_msi_fw_mask(ar);
+
if (val == 0xffffffff) {
ath10k_err(ar, "failed to read device register, device is gone\n");
return -EIO;
@@ -2287,6 +2366,12 @@ static int ath10k_pci_cold_reset(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cold reset\n");
+ spin_lock_bh(&ar->data_lock);
+
+ ar->stats.fw_cold_reset_counter++;
+
+ spin_unlock_bh(&ar->data_lock);
+
/* Put Target, including PCIe, into RESET. */
val = ath10k_pci_reg_read32(ar, SOC_GLOBAL_RESET_ADDRESS);
val |= 1;
@@ -2400,6 +2485,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
u32 chip_id;
ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev,
+ ATH10K_BUS_PCI,
&ath10k_pci_hif_ops);
if (!ar) {
dev_err(&pdev->dev, "failed to allocate core\n");
@@ -2435,7 +2521,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
goto err_sleep;
}
- ret = ath10k_pci_alloc_ce(ar);
+ ret = ath10k_pci_alloc_pipes(ar);
if (ret) {
ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
ret);
@@ -2443,25 +2529,12 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
}
ath10k_pci_ce_deinit(ar);
-
- ret = ath10k_ce_disable_interrupts(ar);
- if (ret) {
- ath10k_err(ar, "failed to disable copy engine interrupts: %d\n",
- ret);
- goto err_free_ce;
- }
-
- /* Workaround: There's no known way to mask all possible interrupts via
- * device CSR. The only way to make sure device doesn't assert
- * interrupts is to reset it. Interrupts are then disabled on host
- * after handlers are registered.
- */
- ath10k_pci_warm_reset(ar);
+ ath10k_pci_irq_disable(ar);
ret = ath10k_pci_init_irq(ar);
if (ret) {
ath10k_err(ar, "failed to init irqs: %d\n", ret);
- goto err_free_ce;
+ goto err_free_pipes;
}
ath10k_info(ar, "pci irq %s interrupts %d irq_mode %d reset_mode %d\n",
@@ -2474,8 +2547,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
goto err_deinit_irq;
}
- /* This shouldn't race as the device has been reset above. */
- ath10k_pci_irq_disable(ar);
+ ath10k_pci_sleep(ar);
ret = ath10k_core_register(ar, chip_id);
if (ret) {
@@ -2492,8 +2564,8 @@ err_free_irq:
err_deinit_irq:
ath10k_pci_deinit_irq(ar);
-err_free_ce:
- ath10k_pci_free_ce(ar);
+err_free_pipes:
+ ath10k_pci_free_pipes(ar);
err_sleep:
ath10k_pci_sleep(ar);
@@ -2527,8 +2599,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
ath10k_pci_kill_tasklet(ar);
ath10k_pci_deinit_irq(ar);
ath10k_pci_ce_deinit(ar);
- ath10k_pci_free_ce(ar);
- ath10k_pci_sleep(ar);
+ ath10k_pci_free_pipes(ar);
ath10k_pci_release(ar);
ath10k_core_destroy(ar);
}
@@ -2565,5 +2636,7 @@ module_exit(ath10k_pci_exit);
MODULE_AUTHOR("Qualcomm Atheros");
MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices");
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_3_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API2_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API3_FILE);
MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c
index 3e1454b74e00..63ce61fcdac8 100644
--- a/drivers/net/wireless/ath/ath10k/spectral.c
+++ b/drivers/net/wireless/ath/ath10k/spectral.c
@@ -56,14 +56,14 @@ static uint8_t get_max_exp(s8 max_index, u16 max_magnitude, size_t bin_len,
}
int ath10k_spectral_process_fft(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_fft_report *fftr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_fft_report *fftr,
size_t bin_len, u64 tsf)
{
struct fft_sample_ath10k *fft_sample;
u8 buf[sizeof(*fft_sample) + SPECTRAL_ATH10K_MAX_NUM_BINS];
u16 freq1, freq2, total_gain_db, base_pwr_db, length, peak_mag;
- u32 reg0, reg1, nf_list1, nf_list2;
+ u32 reg0, reg1;
u8 chain_idx, *bins;
int dc_pos;
@@ -82,7 +82,7 @@ int ath10k_spectral_process_fft(struct ath10k *ar,
/* TODO: there might be a reason why the hardware reports 20/40/80 MHz,
* but the results/plots suggest that its actually 22/44/88 MHz.
*/
- switch (event->hdr.chan_width_mhz) {
+ switch (phyerr->chan_width_mhz) {
case 20:
fft_sample->chan_width_mhz = 22;
break;
@@ -101,7 +101,7 @@ int ath10k_spectral_process_fft(struct ath10k *ar,
fft_sample->chan_width_mhz = 88;
break;
default:
- fft_sample->chan_width_mhz = event->hdr.chan_width_mhz;
+ fft_sample->chan_width_mhz = phyerr->chan_width_mhz;
}
fft_sample->relpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB);
@@ -110,36 +110,22 @@ int ath10k_spectral_process_fft(struct ath10k *ar,
peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG);
fft_sample->max_magnitude = __cpu_to_be16(peak_mag);
fft_sample->max_index = MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX);
- fft_sample->rssi = event->hdr.rssi_combined;
+ fft_sample->rssi = phyerr->rssi_combined;
total_gain_db = MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB);
base_pwr_db = MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB);
fft_sample->total_gain_db = __cpu_to_be16(total_gain_db);
fft_sample->base_pwr_db = __cpu_to_be16(base_pwr_db);
- freq1 = __le16_to_cpu(event->hdr.freq1);
- freq2 = __le16_to_cpu(event->hdr.freq2);
+ freq1 = __le16_to_cpu(phyerr->freq1);
+ freq2 = __le16_to_cpu(phyerr->freq2);
fft_sample->freq1 = __cpu_to_be16(freq1);
fft_sample->freq2 = __cpu_to_be16(freq2);
- nf_list1 = __le32_to_cpu(event->hdr.nf_list_1);
- nf_list2 = __le32_to_cpu(event->hdr.nf_list_2);
chain_idx = MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX);
- switch (chain_idx) {
- case 0:
- fft_sample->noise = __cpu_to_be16(nf_list1 & 0xffffu);
- break;
- case 1:
- fft_sample->noise = __cpu_to_be16((nf_list1 >> 16) & 0xffffu);
- break;
- case 2:
- fft_sample->noise = __cpu_to_be16(nf_list2 & 0xffffu);
- break;
- case 3:
- fft_sample->noise = __cpu_to_be16((nf_list2 >> 16) & 0xffffu);
- break;
- }
+ fft_sample->noise = __cpu_to_be16(
+ __le16_to_cpu(phyerr->nf_chains[chain_idx]));
bins = (u8 *)fftr;
bins += sizeof(*fftr);
diff --git a/drivers/net/wireless/ath/ath10k/spectral.h b/drivers/net/wireless/ath/ath10k/spectral.h
index ddc57c557272..042f5b302c75 100644
--- a/drivers/net/wireless/ath/ath10k/spectral.h
+++ b/drivers/net/wireless/ath/ath10k/spectral.h
@@ -47,8 +47,8 @@ enum ath10k_spectral_mode {
#ifdef CONFIG_ATH10K_DEBUGFS
int ath10k_spectral_process_fft(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_fft_report *fftr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_fft_report *fftr,
size_t bin_len, u64 tsf);
int ath10k_spectral_start(struct ath10k *ar);
int ath10k_spectral_vif_stop(struct ath10k_vif *arvif);
@@ -59,8 +59,8 @@ void ath10k_spectral_destroy(struct ath10k *ar);
static inline int
ath10k_spectral_process_fft(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_fft_report *fftr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_fft_report *fftr,
size_t bin_len, u64 tsf)
{
return 0;
diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
index 574b75ab2609..b289378b6e3e 100644
--- a/drivers/net/wireless/ath/ath10k/trace.h
+++ b/drivers/net/wireless/ath/ath10k/trace.h
@@ -20,6 +20,15 @@
#include <linux/tracepoint.h>
#include "core.h"
+#if !defined(_TRACE_H_)
+static inline u32 ath10k_frm_hdr_len(const void *buf)
+{
+ const struct ieee80211_hdr *hdr = buf;
+
+ return ieee80211_hdrlen(hdr->frame_control);
+}
+#endif
+
#define _TRACE_H_
/* create empty functions when tracing is disabled */
@@ -138,7 +147,8 @@ TRACE_EVENT(ath10k_log_dbg_dump,
);
TRACE_EVENT(ath10k_wmi_cmd,
- TP_PROTO(struct ath10k *ar, int id, void *buf, size_t buf_len, int ret),
+ TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len,
+ int ret),
TP_ARGS(ar, id, buf, buf_len, ret),
@@ -171,7 +181,7 @@ TRACE_EVENT(ath10k_wmi_cmd,
);
TRACE_EVENT(ath10k_wmi_event,
- TP_PROTO(struct ath10k *ar, int id, void *buf, size_t buf_len),
+ TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len),
TP_ARGS(ar, id, buf, buf_len),
@@ -201,7 +211,7 @@ TRACE_EVENT(ath10k_wmi_event,
);
TRACE_EVENT(ath10k_htt_stats,
- TP_PROTO(struct ath10k *ar, void *buf, size_t buf_len),
+ TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
TP_ARGS(ar, buf, buf_len),
@@ -228,7 +238,7 @@ TRACE_EVENT(ath10k_htt_stats,
);
TRACE_EVENT(ath10k_wmi_dbglog,
- TP_PROTO(struct ath10k *ar, void *buf, size_t buf_len),
+ TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
TP_ARGS(ar, buf, buf_len),
@@ -254,6 +264,195 @@ TRACE_EVENT(ath10k_wmi_dbglog,
)
);
+TRACE_EVENT(ath10k_htt_pktlog,
+ TP_PROTO(struct ath10k *ar, const void *buf, u16 buf_len),
+
+ TP_ARGS(ar, buf, buf_len),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(u16, buf_len)
+ __dynamic_array(u8, pktlog, buf_len)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(pktlog), buf, buf_len);
+ ),
+
+ TP_printk(
+ "%s %s size %hu",
+ __get_str(driver),
+ __get_str(device),
+ __entry->buf_len
+ )
+);
+
+TRACE_EVENT(ath10k_htt_tx,
+ TP_PROTO(struct ath10k *ar, u16 msdu_id, u16 msdu_len,
+ u8 vdev_id, u8 tid),
+
+ TP_ARGS(ar, msdu_id, msdu_len, vdev_id, tid),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(u16, msdu_id)
+ __field(u16, msdu_len)
+ __field(u8, vdev_id)
+ __field(u8, tid)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->msdu_id = msdu_id;
+ __entry->msdu_len = msdu_len;
+ __entry->vdev_id = vdev_id;
+ __entry->tid = tid;
+ ),
+
+ TP_printk(
+ "%s %s msdu_id %d msdu_len %d vdev_id %d tid %d",
+ __get_str(driver),
+ __get_str(device),
+ __entry->msdu_id,
+ __entry->msdu_len,
+ __entry->vdev_id,
+ __entry->tid
+ )
+);
+
+TRACE_EVENT(ath10k_txrx_tx_unref,
+ TP_PROTO(struct ath10k *ar, u16 msdu_id),
+
+ TP_ARGS(ar, msdu_id),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(u16, msdu_id)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->msdu_id = msdu_id;
+ ),
+
+ TP_printk(
+ "%s %s msdu_id %d",
+ __get_str(driver),
+ __get_str(device),
+ __entry->msdu_id
+ )
+);
+
+DECLARE_EVENT_CLASS(ath10k_hdr_event,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+
+ TP_ARGS(ar, data, len),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(size_t, len)
+ __dynamic_array(u8, data, ath10k_frm_hdr_len(data))
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->len = ath10k_frm_hdr_len(data);
+ memcpy(__get_dynamic_array(data), data, __entry->len);
+ ),
+
+ TP_printk(
+ "%s %s len %zu\n",
+ __get_str(driver),
+ __get_str(device),
+ __entry->len
+ )
+);
+
+DECLARE_EVENT_CLASS(ath10k_payload_event,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+
+ TP_ARGS(ar, data, len),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(size_t, len)
+ __dynamic_array(u8, payload, (len - ath10k_frm_hdr_len(data)))
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->len = len - ath10k_frm_hdr_len(data);
+ memcpy(__get_dynamic_array(payload),
+ data + ath10k_frm_hdr_len(data), __entry->len);
+ ),
+
+ TP_printk(
+ "%s %s len %zu\n",
+ __get_str(driver),
+ __get_str(device),
+ __entry->len
+ )
+);
+
+DEFINE_EVENT(ath10k_hdr_event, ath10k_tx_hdr,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+ TP_ARGS(ar, data, len)
+);
+
+DEFINE_EVENT(ath10k_payload_event, ath10k_tx_payload,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+ TP_ARGS(ar, data, len)
+);
+
+DEFINE_EVENT(ath10k_hdr_event, ath10k_rx_hdr,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+ TP_ARGS(ar, data, len)
+);
+
+DEFINE_EVENT(ath10k_payload_event, ath10k_rx_payload,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+ TP_ARGS(ar, data, len)
+);
+
+TRACE_EVENT(ath10k_htt_rx_desc,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+
+ TP_ARGS(ar, data, len),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(u16, len)
+ __dynamic_array(u8, rxdesc, len)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->len = len;
+ memcpy(__get_dynamic_array(rxdesc), data, len);
+ ),
+
+ TP_printk(
+ "%s %s rxdesc len %d",
+ __get_str(driver),
+ __get_str(device),
+ __entry->len
+ )
+);
+
#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
/* we don't want to use include/trace/events */
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index a0cbc21d0d4b..7579de8e7a8c 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -78,6 +78,7 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
info = IEEE80211_SKB_CB(msdu);
memset(&info->status, 0, sizeof(info->status));
+ trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id);
if (tx_done->discard) {
ieee80211_free_txskb(htt->ar->hw, msdu);
@@ -145,7 +146,8 @@ static int ath10k_wait_for_peer_common(struct ath10k *ar, int vdev_id,
mapped = !!ath10k_peer_find(ar, vdev_id, addr);
spin_unlock_bh(&ar->data_lock);
- mapped == expect_mapped;
+ (mapped == expect_mapped ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags));
}), 3*HZ);
if (ret <= 0)
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 2c42bd504b79..c0f3e4d09263 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -609,6 +609,40 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = {
.gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
};
+static void
+ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
+ const struct wmi_channel_arg *arg)
+{
+ u32 flags = 0;
+
+ memset(ch, 0, sizeof(*ch));
+
+ if (arg->passive)
+ flags |= WMI_CHAN_FLAG_PASSIVE;
+ if (arg->allow_ibss)
+ flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED;
+ if (arg->allow_ht)
+ flags |= WMI_CHAN_FLAG_ALLOW_HT;
+ if (arg->allow_vht)
+ flags |= WMI_CHAN_FLAG_ALLOW_VHT;
+ if (arg->ht40plus)
+ flags |= WMI_CHAN_FLAG_HT40_PLUS;
+ if (arg->chan_radar)
+ flags |= WMI_CHAN_FLAG_DFS;
+
+ ch->mhz = __cpu_to_le32(arg->freq);
+ ch->band_center_freq1 = __cpu_to_le32(arg->band_center_freq1);
+ ch->band_center_freq2 = 0;
+ ch->min_power = arg->min_power;
+ ch->max_power = arg->max_power;
+ ch->reg_power = arg->max_reg_power;
+ ch->antenna_max = arg->max_antenna_gain;
+
+ /* mode & flags share storage */
+ ch->mode = arg->mode;
+ ch->flags |= __cpu_to_le32(flags);
+}
+
int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
{
int ret;
@@ -745,6 +779,10 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
ath10k_wmi_tx_beacons_nowait(ar);
ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
+
+ if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ ret = -ESHUTDOWN;
+
(ret != -EAGAIN);
}), 3*HZ);
@@ -800,6 +838,8 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE,
fc & IEEE80211_FCTL_STYPE);
+ trace_ath10k_tx_hdr(ar, skb->data, skb->len);
+ trace_ath10k_tx_payload(ar, skb->data, skb->len);
/* Send the management frame buffer to the target */
ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid);
@@ -1073,13 +1113,46 @@ static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band)
return rate_idx;
}
+/* If keys are configured, HW decrypts all frames
+ * with protected bit set. Mark such frames as decrypted.
+ */
+static void ath10k_wmi_handle_wep_reauth(struct ath10k *ar,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *status)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ unsigned int hdrlen;
+ bool peer_key;
+ u8 *addr, keyidx;
+
+ if (!ieee80211_is_auth(hdr->frame_control) ||
+ !ieee80211_has_protected(hdr->frame_control))
+ return;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (skb->len < (hdrlen + IEEE80211_WEP_IV_LEN))
+ return;
+
+ keyidx = skb->data[hdrlen + (IEEE80211_WEP_IV_LEN - 1)] >> WEP_KEYID_SHIFT;
+ addr = ieee80211_get_SA(hdr);
+
+ spin_lock_bh(&ar->data_lock);
+ peer_key = ath10k_mac_is_peer_wep_key_set(ar, addr, keyidx);
+ spin_unlock_bh(&ar->data_lock);
+
+ if (peer_key) {
+ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac wep key present for peer %pM\n", addr);
+ status->flag |= RX_FLAG_DECRYPTED;
+ }
+}
+
static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_mgmt_rx_event_v1 *ev_v1;
struct wmi_mgmt_rx_event_v2 *ev_v2;
struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_channel *ch;
struct ieee80211_hdr *hdr;
u32 rx_status;
u32 channel;
@@ -1127,30 +1200,34 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
return 0;
}
- if (rx_status & WMI_RX_STATUS_ERR_CRC)
- status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ if (rx_status & WMI_RX_STATUS_ERR_CRC) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
if (rx_status & WMI_RX_STATUS_ERR_MIC)
status->flag |= RX_FLAG_MMIC_ERROR;
- /* HW can Rx CCK rates on 5GHz. In that case phy_mode is set to
+ /* Hardware can Rx CCK rates on 5GHz. In that case phy_mode is set to
* MODE_11B. This means phy_mode is not a reliable source for the band
- * of mgmt rx. */
-
- ch = ar->scan_channel;
- if (!ch)
- ch = ar->rx_channel;
-
- if (ch) {
- status->band = ch->band;
-
- if (phy_mode == MODE_11B &&
- status->band == IEEE80211_BAND_5GHZ)
- ath10k_dbg(ar, ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n");
+ * of mgmt rx.
+ */
+ if (channel >= 1 && channel <= 14) {
+ status->band = IEEE80211_BAND_2GHZ;
+ } else if (channel >= 36 && channel <= 165) {
+ status->band = IEEE80211_BAND_5GHZ;
} else {
- ath10k_warn(ar, "using (unreliable) phy_mode to extract band for mgmt rx\n");
- status->band = phy_mode_to_band(phy_mode);
+ /* Shouldn't happen unless list of advertised channels to
+ * mac80211 has been changed.
+ */
+ WARN_ON_ONCE(1);
+ dev_kfree_skb(skb);
+ return 0;
}
+ if (phy_mode == MODE_11B && status->band == IEEE80211_BAND_5GHZ)
+ ath10k_dbg(ar, ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n");
+
status->freq = ieee80211_channel_to_frequency(channel, status->band);
status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
status->rate_idx = get_rate_idx(rate, status->band);
@@ -1160,6 +1237,8 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
hdr = (struct ieee80211_hdr *)skb->data;
fc = le16_to_cpu(hdr->frame_control);
+ ath10k_wmi_handle_wep_reauth(ar, skb, status);
+
/* FW delivers WEP Shared Auth frame with Protected Bit set and
* encrypted payload. However in case of PMF it delivers decrypted
* frames with Protected Bit set. */
@@ -1295,14 +1374,196 @@ static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
return 0;
}
+static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
+ struct ath10k_fw_stats_pdev *dst)
+{
+ const struct wal_dbg_tx_stats *tx = &src->wal.tx;
+ const struct wal_dbg_rx_stats *rx = &src->wal.rx;
+
+ dst->ch_noise_floor = __le32_to_cpu(src->chan_nf);
+ dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count);
+ dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count);
+ dst->rx_clear_count = __le32_to_cpu(src->rx_clear_count);
+ dst->cycle_count = __le32_to_cpu(src->cycle_count);
+ dst->phy_err_count = __le32_to_cpu(src->phy_err_count);
+ dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr);
+
+ dst->comp_queued = __le32_to_cpu(tx->comp_queued);
+ dst->comp_delivered = __le32_to_cpu(tx->comp_delivered);
+ dst->msdu_enqued = __le32_to_cpu(tx->msdu_enqued);
+ dst->mpdu_enqued = __le32_to_cpu(tx->mpdu_enqued);
+ dst->wmm_drop = __le32_to_cpu(tx->wmm_drop);
+ dst->local_enqued = __le32_to_cpu(tx->local_enqued);
+ dst->local_freed = __le32_to_cpu(tx->local_freed);
+ dst->hw_queued = __le32_to_cpu(tx->hw_queued);
+ dst->hw_reaped = __le32_to_cpu(tx->hw_reaped);
+ dst->underrun = __le32_to_cpu(tx->underrun);
+ dst->tx_abort = __le32_to_cpu(tx->tx_abort);
+ dst->mpdus_requed = __le32_to_cpu(tx->mpdus_requed);
+ dst->tx_ko = __le32_to_cpu(tx->tx_ko);
+ dst->data_rc = __le32_to_cpu(tx->data_rc);
+ dst->self_triggers = __le32_to_cpu(tx->self_triggers);
+ dst->sw_retry_failure = __le32_to_cpu(tx->sw_retry_failure);
+ dst->illgl_rate_phy_err = __le32_to_cpu(tx->illgl_rate_phy_err);
+ dst->pdev_cont_xretry = __le32_to_cpu(tx->pdev_cont_xretry);
+ dst->pdev_tx_timeout = __le32_to_cpu(tx->pdev_tx_timeout);
+ dst->pdev_resets = __le32_to_cpu(tx->pdev_resets);
+ dst->phy_underrun = __le32_to_cpu(tx->phy_underrun);
+ dst->txop_ovf = __le32_to_cpu(tx->txop_ovf);
+
+ dst->mid_ppdu_route_change = __le32_to_cpu(rx->mid_ppdu_route_change);
+ dst->status_rcvd = __le32_to_cpu(rx->status_rcvd);
+ dst->r0_frags = __le32_to_cpu(rx->r0_frags);
+ dst->r1_frags = __le32_to_cpu(rx->r1_frags);
+ dst->r2_frags = __le32_to_cpu(rx->r2_frags);
+ dst->r3_frags = __le32_to_cpu(rx->r3_frags);
+ dst->htt_msdus = __le32_to_cpu(rx->htt_msdus);
+ dst->htt_mpdus = __le32_to_cpu(rx->htt_mpdus);
+ dst->loc_msdus = __le32_to_cpu(rx->loc_msdus);
+ dst->loc_mpdus = __le32_to_cpu(rx->loc_mpdus);
+ dst->oversize_amsdu = __le32_to_cpu(rx->oversize_amsdu);
+ dst->phy_errs = __le32_to_cpu(rx->phy_errs);
+ dst->phy_err_drop = __le32_to_cpu(rx->phy_err_drop);
+ dst->mpdu_errs = __le32_to_cpu(rx->mpdu_errs);
+}
+
+static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
+ struct ath10k_fw_stats_peer *dst)
+{
+ ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
+ dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
+ dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
+}
+
+static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar,
+ struct sk_buff *skb,
+ struct ath10k_fw_stats *stats)
+{
+ const struct wmi_stats_event *ev = (void *)skb->data;
+ u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
+ int i;
+
+ if (!skb_pull(skb, sizeof(*ev)))
+ return -EPROTO;
+
+ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+
+ for (i = 0; i < num_pdev_stats; i++) {
+ const struct wmi_pdev_stats *src;
+ struct ath10k_fw_stats_pdev *dst;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_pdev_stats(src, dst);
+ list_add_tail(&dst->list, &stats->pdevs);
+ }
+
+ /* fw doesn't implement vdev stats */
+
+ for (i = 0; i < num_peer_stats; i++) {
+ const struct wmi_peer_stats *src;
+ struct ath10k_fw_stats_peer *dst;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_peer_stats(src, dst);
+ list_add_tail(&dst->list, &stats->peers);
+ }
+
+ return 0;
+}
+
+static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar,
+ struct sk_buff *skb,
+ struct ath10k_fw_stats *stats)
+{
+ const struct wmi_stats_event *ev = (void *)skb->data;
+ u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
+ int i;
+
+ if (!skb_pull(skb, sizeof(*ev)))
+ return -EPROTO;
+
+ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+
+ for (i = 0; i < num_pdev_stats; i++) {
+ const struct wmi_10x_pdev_stats *src;
+ struct ath10k_fw_stats_pdev *dst;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_pdev_stats(&src->old, dst);
+
+ dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad);
+ dst->rts_bad = __le32_to_cpu(src->rts_bad);
+ dst->rts_good = __le32_to_cpu(src->rts_good);
+ dst->fcs_bad = __le32_to_cpu(src->fcs_bad);
+ dst->no_beacons = __le32_to_cpu(src->no_beacons);
+ dst->mib_int_count = __le32_to_cpu(src->mib_int_count);
+
+ list_add_tail(&dst->list, &stats->pdevs);
+ }
+
+ /* fw doesn't implement vdev stats */
+
+ for (i = 0; i < num_peer_stats; i++) {
+ const struct wmi_10x_peer_stats *src;
+ struct ath10k_fw_stats_peer *dst;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_peer_stats(&src->old, dst);
+
+ dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
+
+ list_add_tail(&dst->list, &stats->peers);
+ }
+
+ return 0;
+}
+
+int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
+ struct ath10k_fw_stats *stats)
+{
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+ return ath10k_wmi_10x_pull_fw_stats(ar, skb, stats);
+ else
+ return ath10k_wmi_main_pull_fw_stats(ar, skb, stats);
+}
+
static void ath10k_wmi_event_update_stats(struct ath10k *ar,
struct sk_buff *skb)
{
- struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data;
-
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
-
- ath10k_debug_read_target_stats(ar, ev);
+ ath10k_debug_fw_stats_process(ar, skb);
}
static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
@@ -1579,6 +1840,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
struct wmi_bcn_info *bcn_info;
struct ath10k_vif *arvif;
struct sk_buff *bcn;
+ dma_addr_t paddr;
int ret, vdev_id = 0;
ev = (struct wmi_host_swba_event *)skb->data;
@@ -1647,27 +1909,37 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
ath10k_warn(ar, "SWBA overrun on vdev %d\n",
arvif->vdev_id);
- dma_unmap_single(arvif->ar->dev,
- ATH10K_SKB_CB(arvif->beacon)->paddr,
- arvif->beacon->len, DMA_TO_DEVICE);
- dev_kfree_skb_any(arvif->beacon);
- arvif->beacon = NULL;
+ ath10k_mac_vif_beacon_free(arvif);
}
- ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev,
- bcn->data, bcn->len,
- DMA_TO_DEVICE);
- ret = dma_mapping_error(arvif->ar->dev,
- ATH10K_SKB_CB(bcn)->paddr);
- if (ret) {
- ath10k_warn(ar, "failed to map beacon: %d\n", ret);
- dev_kfree_skb_any(bcn);
- goto skip;
+ if (!arvif->beacon_buf) {
+ paddr = dma_map_single(arvif->ar->dev, bcn->data,
+ bcn->len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(arvif->ar->dev, paddr);
+ if (ret) {
+ ath10k_warn(ar, "failed to map beacon: %d\n",
+ ret);
+ dev_kfree_skb_any(bcn);
+ goto skip;
+ }
+
+ ATH10K_SKB_CB(bcn)->paddr = paddr;
+ } else {
+ if (bcn->len > IEEE80211_MAX_FRAME_LEN) {
+ ath10k_warn(ar, "trimming beacon %d -> %d bytes!\n",
+ bcn->len, IEEE80211_MAX_FRAME_LEN);
+ skb_trim(bcn, IEEE80211_MAX_FRAME_LEN);
+ }
+ memcpy(arvif->beacon_buf, bcn->data, bcn->len);
+ ATH10K_SKB_CB(bcn)->paddr = arvif->beacon_paddr;
}
arvif->beacon = bcn;
arvif->beacon_sent = false;
+ trace_ath10k_tx_hdr(ar, bcn->data, bcn->len);
+ trace_ath10k_tx_payload(ar, bcn->data, bcn->len);
+
ath10k_wmi_tx_beacon_nowait(arvif);
skip:
spin_unlock_bh(&ar->data_lock);
@@ -1681,8 +1953,8 @@ static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar,
}
static void ath10k_dfs_radar_report(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_radar_report *rr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_radar_report *rr,
u64 tsf)
{
u32 reg0, reg1, tsf32l;
@@ -1715,12 +1987,12 @@ static void ath10k_dfs_radar_report(struct ath10k *ar,
return;
/* report event to DFS pattern detector */
- tsf32l = __le32_to_cpu(event->hdr.tsf_timestamp);
+ tsf32l = __le32_to_cpu(phyerr->tsf_timestamp);
tsf64 = tsf & (~0xFFFFFFFFULL);
tsf64 |= tsf32l;
width = MS(reg1, RADAR_REPORT_REG1_PULSE_DUR);
- rssi = event->hdr.rssi_combined;
+ rssi = phyerr->rssi_combined;
/* hardware store this as 8 bit signed value,
* set to zero if negative number
@@ -1759,8 +2031,8 @@ static void ath10k_dfs_radar_report(struct ath10k *ar,
}
static int ath10k_dfs_fft_report(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_fft_report *fftr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_fft_report *fftr,
u64 tsf)
{
u32 reg0, reg1;
@@ -1768,7 +2040,7 @@ static int ath10k_dfs_fft_report(struct ath10k *ar,
reg0 = __le32_to_cpu(fftr->reg0);
reg1 = __le32_to_cpu(fftr->reg1);
- rssi = event->hdr.rssi_combined;
+ rssi = phyerr->rssi_combined;
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"wmi phyerr fft report total_gain_db %d base_pwr_db %d fft_chn_idx %d peak_sidx %d\n",
@@ -1797,20 +2069,20 @@ static int ath10k_dfs_fft_report(struct ath10k *ar,
}
static void ath10k_wmi_event_dfs(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
+ const struct wmi_phyerr *phyerr,
u64 tsf)
{
int buf_len, tlv_len, res, i = 0;
- struct phyerr_tlv *tlv;
- struct phyerr_radar_report *rr;
- struct phyerr_fft_report *fftr;
- u8 *tlv_buf;
+ const struct phyerr_tlv *tlv;
+ const struct phyerr_radar_report *rr;
+ const struct phyerr_fft_report *fftr;
+ const u8 *tlv_buf;
- buf_len = __le32_to_cpu(event->hdr.buf_len);
+ buf_len = __le32_to_cpu(phyerr->buf_len);
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"wmi event dfs err_code %d rssi %d tsfl 0x%X tsf64 0x%llX len %d\n",
- event->hdr.phy_err_code, event->hdr.rssi_combined,
- __le32_to_cpu(event->hdr.tsf_timestamp), tsf, buf_len);
+ phyerr->phy_err_code, phyerr->rssi_combined,
+ __le32_to_cpu(phyerr->tsf_timestamp), tsf, buf_len);
/* Skip event if DFS disabled */
if (!config_enabled(CONFIG_ATH10K_DFS_CERTIFIED))
@@ -1825,9 +2097,9 @@ static void ath10k_wmi_event_dfs(struct ath10k *ar,
return;
}
- tlv = (struct phyerr_tlv *)&event->bufp[i];
+ tlv = (struct phyerr_tlv *)&phyerr->buf[i];
tlv_len = __le16_to_cpu(tlv->len);
- tlv_buf = &event->bufp[i + sizeof(*tlv)];
+ tlv_buf = &phyerr->buf[i + sizeof(*tlv)];
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"wmi event dfs tlv_len %d tlv_tag 0x%02X tlv_sig 0x%02X\n",
tlv_len, tlv->tag, tlv->sig);
@@ -1841,7 +2113,7 @@ static void ath10k_wmi_event_dfs(struct ath10k *ar,
}
rr = (struct phyerr_radar_report *)tlv_buf;
- ath10k_dfs_radar_report(ar, event, rr, tsf);
+ ath10k_dfs_radar_report(ar, phyerr, rr, tsf);
break;
case PHYERR_TLV_TAG_SEARCH_FFT_REPORT:
if (i + sizeof(*tlv) + sizeof(*fftr) > buf_len) {
@@ -1851,7 +2123,7 @@ static void ath10k_wmi_event_dfs(struct ath10k *ar,
}
fftr = (struct phyerr_fft_report *)tlv_buf;
- res = ath10k_dfs_fft_report(ar, event, fftr, tsf);
+ res = ath10k_dfs_fft_report(ar, phyerr, fftr, tsf);
if (res)
return;
break;
@@ -1863,16 +2135,16 @@ static void ath10k_wmi_event_dfs(struct ath10k *ar,
static void
ath10k_wmi_event_spectral_scan(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
+ const struct wmi_phyerr *phyerr,
u64 tsf)
{
int buf_len, tlv_len, res, i = 0;
struct phyerr_tlv *tlv;
- u8 *tlv_buf;
- struct phyerr_fft_report *fftr;
+ const void *tlv_buf;
+ const struct phyerr_fft_report *fftr;
size_t fftr_len;
- buf_len = __le32_to_cpu(event->hdr.buf_len);
+ buf_len = __le32_to_cpu(phyerr->buf_len);
while (i < buf_len) {
if (i + sizeof(*tlv) > buf_len) {
@@ -1881,9 +2153,9 @@ ath10k_wmi_event_spectral_scan(struct ath10k *ar,
return;
}
- tlv = (struct phyerr_tlv *)&event->bufp[i];
+ tlv = (struct phyerr_tlv *)&phyerr->buf[i];
tlv_len = __le16_to_cpu(tlv->len);
- tlv_buf = &event->bufp[i + sizeof(*tlv)];
+ tlv_buf = &phyerr->buf[i + sizeof(*tlv)];
if (i + sizeof(*tlv) + tlv_len > buf_len) {
ath10k_warn(ar, "failed to parse phyerr tlv payload at byte %d\n",
@@ -1900,8 +2172,8 @@ ath10k_wmi_event_spectral_scan(struct ath10k *ar,
}
fftr_len = tlv_len - sizeof(*fftr);
- fftr = (struct phyerr_fft_report *)tlv_buf;
- res = ath10k_spectral_process_fft(ar, event,
+ fftr = tlv_buf;
+ res = ath10k_spectral_process_fft(ar, phyerr,
fftr, fftr_len,
tsf);
if (res < 0) {
@@ -1918,8 +2190,8 @@ ath10k_wmi_event_spectral_scan(struct ath10k *ar,
static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
{
- struct wmi_comb_phyerr_rx_event *comb_event;
- struct wmi_single_phyerr_rx_event *event;
+ const struct wmi_phyerr_event *ev;
+ const struct wmi_phyerr *phyerr;
u32 count, i, buf_len, phy_err_code;
u64 tsf;
int left_len = skb->len;
@@ -1927,38 +2199,38 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
ATH10K_DFS_STAT_INC(ar, phy_errors);
/* Check if combined event available */
- if (left_len < sizeof(*comb_event)) {
+ if (left_len < sizeof(*ev)) {
ath10k_warn(ar, "wmi phyerr combined event wrong len\n");
return;
}
- left_len -= sizeof(*comb_event);
+ left_len -= sizeof(*ev);
/* Check number of included events */
- comb_event = (struct wmi_comb_phyerr_rx_event *)skb->data;
- count = __le32_to_cpu(comb_event->hdr.num_phyerr_events);
+ ev = (const struct wmi_phyerr_event *)skb->data;
+ count = __le32_to_cpu(ev->num_phyerrs);
- tsf = __le32_to_cpu(comb_event->hdr.tsf_u32);
+ tsf = __le32_to_cpu(ev->tsf_u32);
tsf <<= 32;
- tsf |= __le32_to_cpu(comb_event->hdr.tsf_l32);
+ tsf |= __le32_to_cpu(ev->tsf_l32);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi event phyerr count %d tsf64 0x%llX\n",
count, tsf);
- event = (struct wmi_single_phyerr_rx_event *)comb_event->bufp;
+ phyerr = ev->phyerrs;
for (i = 0; i < count; i++) {
/* Check if we can read event header */
- if (left_len < sizeof(*event)) {
+ if (left_len < sizeof(*phyerr)) {
ath10k_warn(ar, "single event (%d) wrong head len\n",
i);
return;
}
- left_len -= sizeof(*event);
+ left_len -= sizeof(*phyerr);
- buf_len = __le32_to_cpu(event->hdr.buf_len);
- phy_err_code = event->hdr.phy_err_code;
+ buf_len = __le32_to_cpu(phyerr->buf_len);
+ phy_err_code = phyerr->phy_err_code;
if (left_len < buf_len) {
ath10k_warn(ar, "single event (%d) wrong buf len\n", i);
@@ -1969,20 +2241,20 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
switch (phy_err_code) {
case PHY_ERROR_RADAR:
- ath10k_wmi_event_dfs(ar, event, tsf);
+ ath10k_wmi_event_dfs(ar, phyerr, tsf);
break;
case PHY_ERROR_SPECTRAL_SCAN:
- ath10k_wmi_event_spectral_scan(ar, event, tsf);
+ ath10k_wmi_event_spectral_scan(ar, phyerr, tsf);
break;
case PHY_ERROR_FALSE_RADAR_EXT:
- ath10k_wmi_event_dfs(ar, event, tsf);
- ath10k_wmi_event_spectral_scan(ar, event, tsf);
+ ath10k_wmi_event_dfs(ar, phyerr, tsf);
+ ath10k_wmi_event_spectral_scan(ar, phyerr, tsf);
break;
default:
break;
}
- event += sizeof(*event) + buf_len;
+ phyerr = (void *)phyerr + sizeof(*phyerr) + buf_len;
}
}
@@ -2028,7 +2300,7 @@ static void ath10k_wmi_event_debug_print(struct ath10k *ar,
/* the last byte is always reserved for the null character */
buf[i] = '\0';
- ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf);
+ ath10k_dbg(ar, ATH10K_DBG_WMI_PRINT, "wmi print '%s'\n", buf);
}
static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
@@ -2163,30 +2435,117 @@ static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
return 0;
}
-static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
- struct sk_buff *skb)
+static int ath10k_wmi_main_pull_svc_rdy_ev(struct sk_buff *skb,
+ struct wmi_svc_rdy_ev_arg *arg)
+{
+ struct wmi_service_ready_event *ev;
+ size_t i, n;
+
+ if (skb->len < sizeof(*ev))
+ return -EPROTO;
+
+ ev = (void *)skb->data;
+ skb_pull(skb, sizeof(*ev));
+ arg->min_tx_power = ev->hw_min_tx_power;
+ arg->max_tx_power = ev->hw_max_tx_power;
+ arg->ht_cap = ev->ht_cap_info;
+ arg->vht_cap = ev->vht_cap_info;
+ arg->sw_ver0 = ev->sw_version;
+ arg->sw_ver1 = ev->sw_version_1;
+ arg->phy_capab = ev->phy_capability;
+ arg->num_rf_chains = ev->num_rf_chains;
+ arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
+ arg->num_mem_reqs = ev->num_mem_reqs;
+ arg->service_map = ev->wmi_service_bitmap;
+ arg->service_map_len = sizeof(ev->wmi_service_bitmap);
+
+ n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
+ ARRAY_SIZE(arg->mem_reqs));
+ for (i = 0; i < n; i++)
+ arg->mem_reqs[i] = &ev->mem_reqs[i];
+
+ if (skb->len <
+ __le32_to_cpu(arg->num_mem_reqs) * sizeof(arg->mem_reqs[0]))
+ return -EPROTO;
+
+ return 0;
+}
+
+static int ath10k_wmi_10x_pull_svc_rdy_ev(struct sk_buff *skb,
+ struct wmi_svc_rdy_ev_arg *arg)
+{
+ struct wmi_10x_service_ready_event *ev;
+ int i, n;
+
+ if (skb->len < sizeof(*ev))
+ return -EPROTO;
+
+ ev = (void *)skb->data;
+ skb_pull(skb, sizeof(*ev));
+ arg->min_tx_power = ev->hw_min_tx_power;
+ arg->max_tx_power = ev->hw_max_tx_power;
+ arg->ht_cap = ev->ht_cap_info;
+ arg->vht_cap = ev->vht_cap_info;
+ arg->sw_ver0 = ev->sw_version;
+ arg->phy_capab = ev->phy_capability;
+ arg->num_rf_chains = ev->num_rf_chains;
+ arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
+ arg->num_mem_reqs = ev->num_mem_reqs;
+ arg->service_map = ev->wmi_service_bitmap;
+ arg->service_map_len = sizeof(ev->wmi_service_bitmap);
+
+ n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
+ ARRAY_SIZE(arg->mem_reqs));
+ for (i = 0; i < n; i++)
+ arg->mem_reqs[i] = &ev->mem_reqs[i];
+
+ if (skb->len <
+ __le32_to_cpu(arg->num_mem_reqs) * sizeof(arg->mem_reqs[0]))
+ return -EPROTO;
+
+ return 0;
+}
+
+static void ath10k_wmi_event_service_ready(struct ath10k *ar,
+ struct sk_buff *skb)
{
- struct wmi_service_ready_event *ev = (void *)skb->data;
- DECLARE_BITMAP(svc_bmap, WMI_SERVICE_MAX) = {};
+ struct wmi_svc_rdy_ev_arg arg = {};
+ u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
+ int ret;
- if (skb->len < sizeof(*ev)) {
- ath10k_warn(ar, "Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
- skb->len, sizeof(*ev));
+ memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
+
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+ ret = ath10k_wmi_10x_pull_svc_rdy_ev(skb, &arg);
+ wmi_10x_svc_map(arg.service_map, ar->wmi.svc_map,
+ arg.service_map_len);
+ } else {
+ ret = ath10k_wmi_main_pull_svc_rdy_ev(skb, &arg);
+ wmi_main_svc_map(arg.service_map, ar->wmi.svc_map,
+ arg.service_map_len);
+ }
+
+ if (ret) {
+ ath10k_warn(ar, "failed to parse service ready: %d\n", ret);
return;
}
- ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
- ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
- ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
- ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
+ ar->hw_min_tx_power = __le32_to_cpu(arg.min_tx_power);
+ ar->hw_max_tx_power = __le32_to_cpu(arg.max_tx_power);
+ ar->ht_cap_info = __le32_to_cpu(arg.ht_cap);
+ ar->vht_cap_info = __le32_to_cpu(arg.vht_cap);
ar->fw_version_major =
- (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
- ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
+ (__le32_to_cpu(arg.sw_ver0) & 0xff000000) >> 24;
+ ar->fw_version_minor = (__le32_to_cpu(arg.sw_ver0) & 0x00ffffff);
ar->fw_version_release =
- (__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16;
- ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff);
- ar->phy_capability = __le32_to_cpu(ev->phy_capability);
- ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
+ (__le32_to_cpu(arg.sw_ver1) & 0xffff0000) >> 16;
+ ar->fw_version_build = (__le32_to_cpu(arg.sw_ver1) & 0x0000ffff);
+ ar->phy_capability = __le32_to_cpu(arg.phy_capab);
+ ar->num_rf_chains = __le32_to_cpu(arg.num_rf_chains);
+ ar->ath_common.regulatory.current_rd = __le32_to_cpu(arg.eeprom_rd);
+
+ ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
+ arg.service_map, arg.service_map_len);
/* only manually set fw features when not using FW IE format */
if (ar->fw_api == 1 && ar->fw_version_build > 636)
@@ -2198,13 +2557,8 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
}
- ar->ath_common.regulatory.current_rd =
- __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
-
- wmi_main_svc_map(ev->wmi_service_bitmap, svc_bmap);
- ath10k_debug_read_service_map(ar, svc_bmap, sizeof(svc_bmap));
- ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
- ev->wmi_service_bitmap, sizeof(ev->wmi_service_bitmap));
+ ar->supp_tx_chainmask = (1 << ar->num_rf_chains) - 1;
+ ar->supp_rx_chainmask = (1 << ar->num_rf_chains) - 1;
if (strlen(ar->hw->wiphy->fw_version) == 0) {
snprintf(ar->hw->wiphy->fw_version,
@@ -2216,93 +2570,18 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
ar->fw_version_build);
}
- /* FIXME: it probably should be better to support this */
- if (__le32_to_cpu(ev->num_mem_reqs) > 0) {
- ath10k_warn(ar, "target requested %d memory chunks; ignoring\n",
- __le32_to_cpu(ev->num_mem_reqs));
- }
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
- __le32_to_cpu(ev->sw_version),
- __le32_to_cpu(ev->sw_version_1),
- __le32_to_cpu(ev->abi_version),
- __le32_to_cpu(ev->phy_capability),
- __le32_to_cpu(ev->ht_cap_info),
- __le32_to_cpu(ev->vht_cap_info),
- __le32_to_cpu(ev->vht_supp_mcs),
- __le32_to_cpu(ev->sys_cap_info),
- __le32_to_cpu(ev->num_mem_reqs),
- __le32_to_cpu(ev->num_rf_chains));
-
- complete(&ar->wmi.service_ready);
-}
-
-static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar,
- struct sk_buff *skb)
-{
- u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
- int ret;
- struct wmi_service_ready_event_10x *ev = (void *)skb->data;
- DECLARE_BITMAP(svc_bmap, WMI_SERVICE_MAX) = {};
-
- if (skb->len < sizeof(*ev)) {
- ath10k_warn(ar, "Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
- skb->len, sizeof(*ev));
- return;
- }
-
- ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
- ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
- ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
- ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
- ar->fw_version_major =
- (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
- ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
- ar->phy_capability = __le32_to_cpu(ev->phy_capability);
- ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
-
- if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
- ath10k_warn(ar, "hardware advertises support for more spatial streams than it should (%d > %d)\n",
- ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
- ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
- }
-
- ar->ath_common.regulatory.current_rd =
- __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
-
- wmi_10x_svc_map(ev->wmi_service_bitmap, svc_bmap);
- ath10k_debug_read_service_map(ar, svc_bmap, sizeof(svc_bmap));
- ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
- ev->wmi_service_bitmap, sizeof(ev->wmi_service_bitmap));
-
- if (strlen(ar->hw->wiphy->fw_version) == 0) {
- snprintf(ar->hw->wiphy->fw_version,
- sizeof(ar->hw->wiphy->fw_version),
- "%u.%u",
- ar->fw_version_major,
- ar->fw_version_minor);
- }
-
- num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs);
-
- if (num_mem_reqs > ATH10K_MAX_MEM_REQS) {
+ num_mem_reqs = __le32_to_cpu(arg.num_mem_reqs);
+ if (num_mem_reqs > WMI_MAX_MEM_REQS) {
ath10k_warn(ar, "requested memory chunks number (%d) exceeds the limit\n",
num_mem_reqs);
return;
}
- if (!num_mem_reqs)
- goto exit;
-
- ath10k_dbg(ar, ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n",
- num_mem_reqs);
-
for (i = 0; i < num_mem_reqs; ++i) {
- req_id = __le32_to_cpu(ev->mem_reqs[i].req_id);
- num_units = __le32_to_cpu(ev->mem_reqs[i].num_units);
- unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size);
- num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info);
+ req_id = __le32_to_cpu(arg.mem_reqs[i]->req_id);
+ num_units = __le32_to_cpu(arg.mem_reqs[i]->num_units);
+ unit_size = __le32_to_cpu(arg.mem_reqs[i]->unit_size);
+ num_unit_info = __le32_to_cpu(arg.mem_reqs[i]->num_unit_info);
if (num_unit_info & NUM_UNITS_IS_NUM_PEERS)
/* number of units to allocate is number of
@@ -2316,7 +2595,7 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar,
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n",
req_id,
- __le32_to_cpu(ev->mem_reqs[i].num_units),
+ __le32_to_cpu(arg.mem_reqs[i]->num_units),
num_unit_info,
unit_size,
num_units);
@@ -2327,23 +2606,23 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar,
return;
}
-exit:
ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
- __le32_to_cpu(ev->sw_version),
- __le32_to_cpu(ev->abi_version),
- __le32_to_cpu(ev->phy_capability),
- __le32_to_cpu(ev->ht_cap_info),
- __le32_to_cpu(ev->vht_cap_info),
- __le32_to_cpu(ev->vht_supp_mcs),
- __le32_to_cpu(ev->sys_cap_info),
- __le32_to_cpu(ev->num_mem_reqs),
- __le32_to_cpu(ev->num_rf_chains));
+ "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n",
+ __le32_to_cpu(arg.min_tx_power),
+ __le32_to_cpu(arg.max_tx_power),
+ __le32_to_cpu(arg.ht_cap),
+ __le32_to_cpu(arg.vht_cap),
+ __le32_to_cpu(arg.sw_ver0),
+ __le32_to_cpu(arg.sw_ver1),
+ __le32_to_cpu(arg.phy_capab),
+ __le32_to_cpu(arg.num_rf_chains),
+ __le32_to_cpu(arg.eeprom_rd),
+ __le32_to_cpu(arg.num_mem_reqs));
complete(&ar->wmi.service_ready);
}
-static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
@@ -2466,10 +2745,10 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
ath10k_wmi_event_vdev_install_key_complete(ar, skb);
break;
case WMI_SERVICE_READY_EVENTID:
- ath10k_wmi_service_ready_event_rx(ar, skb);
+ ath10k_wmi_event_service_ready(ar, skb);
break;
case WMI_READY_EVENTID:
- ath10k_wmi_ready_event_rx(ar, skb);
+ ath10k_wmi_event_ready(ar, skb);
break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
@@ -2586,10 +2865,10 @@ static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
ath10k_wmi_event_vdev_resume_req(ar, skb);
break;
case WMI_10X_SERVICE_READY_EVENTID:
- ath10k_wmi_10x_service_ready_event_rx(ar, skb);
+ ath10k_wmi_event_service_ready(ar, skb);
break;
case WMI_10X_READY_EVENTID:
- ath10k_wmi_ready_event_rx(ar, skb);
+ ath10k_wmi_event_ready(ar, skb);
break;
case WMI_10X_PDEV_UTF_EVENTID:
/* ignore utf events */
@@ -2697,10 +2976,10 @@ static void ath10k_wmi_10_2_process_rx(struct ath10k *ar, struct sk_buff *skb)
ath10k_wmi_event_vdev_resume_req(ar, skb);
break;
case WMI_10_2_SERVICE_READY_EVENTID:
- ath10k_wmi_10x_service_ready_event_rx(ar, skb);
+ ath10k_wmi_event_service_ready(ar, skb);
break;
case WMI_10_2_READY_EVENTID:
- ath10k_wmi_ready_event_rx(ar, skb);
+ ath10k_wmi_event_ready(ar, skb);
break;
case WMI_10_2_RTT_KEEPALIVE_EVENTID:
case WMI_10_2_GPIO_INPUT_EVENTID:
@@ -2732,45 +3011,6 @@ static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
}
}
-/* WMI Initialization functions */
-int ath10k_wmi_attach(struct ath10k *ar)
-{
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
- if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
- ar->wmi.cmd = &wmi_10_2_cmd_map;
- else
- ar->wmi.cmd = &wmi_10x_cmd_map;
-
- ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
- ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
- } else {
- ar->wmi.cmd = &wmi_cmd_map;
- ar->wmi.vdev_param = &wmi_vdev_param_map;
- ar->wmi.pdev_param = &wmi_pdev_param_map;
- }
-
- init_completion(&ar->wmi.service_ready);
- init_completion(&ar->wmi.unified_ready);
- init_waitqueue_head(&ar->wmi.tx_credits_wq);
-
- return 0;
-}
-
-void ath10k_wmi_detach(struct ath10k *ar)
-{
- int i;
-
- /* free the host memory chunks requested by firmware */
- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- dma_free_coherent(ar->dev,
- ar->wmi.mem_chunks[i].len,
- ar->wmi.mem_chunks[i].vaddr,
- ar->wmi.mem_chunks[i].paddr);
- }
-
- ar->wmi.num_mem_chunks = 0;
-}
-
int ath10k_wmi_connect(struct ath10k *ar)
{
int status;
@@ -2865,42 +3105,6 @@ int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
ctl2g, ctl5g);
}
-int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
- const struct wmi_channel_arg *arg)
-{
- struct wmi_set_channel_cmd *cmd;
- struct sk_buff *skb;
- u32 ch_flags = 0;
-
- if (arg->passive)
- return -EINVAL;
-
- skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
-
- if (arg->chan_radar)
- ch_flags |= WMI_CHAN_FLAG_DFS;
-
- cmd = (struct wmi_set_channel_cmd *)skb->data;
- cmd->chan.mhz = __cpu_to_le32(arg->freq);
- cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
- cmd->chan.mode = arg->mode;
- cmd->chan.flags |= __cpu_to_le32(ch_flags);
- cmd->chan.min_power = arg->min_power;
- cmd->chan.max_power = arg->max_power;
- cmd->chan.reg_power = arg->max_reg_power;
- cmd->chan.reg_classid = arg->reg_class_id;
- cmd->chan.antenna_max = arg->max_antenna_gain;
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi set channel mode %d freq %d\n",
- arg->mode, arg->freq);
-
- return ath10k_wmi_cmd_send(ar, skb,
- ar->wmi.cmd->pdev_set_channel_cmdid);
-}
-
int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt)
{
struct wmi_pdev_suspend_cmd *cmd;
@@ -2951,16 +3155,37 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
}
+static void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
+ struct wmi_host_mem_chunks *chunks)
+{
+ struct host_memory_chunk *chunk;
+ int i;
+
+ chunks->count = __cpu_to_le32(ar->wmi.num_mem_chunks);
+
+ for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+ chunk = &chunks->items[i];
+ chunk->ptr = __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+ chunk->size = __cpu_to_le32(ar->wmi.mem_chunks[i].len);
+ chunk->req_id = __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
+
+ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi chunk %d len %d requested, addr 0x%llx\n",
+ i,
+ ar->wmi.mem_chunks[i].len,
+ (unsigned long long)ar->wmi.mem_chunks[i].paddr);
+ }
+}
+
static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
{
struct wmi_init_cmd *cmd;
struct sk_buff *buf;
struct wmi_resource_config config = {};
u32 len, val;
- int i;
config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
- config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
+ config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS);
config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS);
config.num_offload_reorder_bufs =
@@ -3019,32 +3244,8 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
cmd = (struct wmi_init_cmd *)buf->data;
- if (ar->wmi.num_mem_chunks == 0) {
- cmd->num_host_mem_chunks = 0;
- goto out;
- }
-
- ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
- ar->wmi.num_mem_chunks);
-
- cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
-
- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- cmd->host_mem_chunks[i].ptr =
- __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
- cmd->host_mem_chunks[i].size =
- __cpu_to_le32(ar->wmi.mem_chunks[i].len);
- cmd->host_mem_chunks[i].req_id =
- __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi chunk %d len %d requested, addr 0x%llx\n",
- i,
- ar->wmi.mem_chunks[i].len,
- (unsigned long long)ar->wmi.mem_chunks[i].paddr);
- }
-out:
memcpy(&cmd->resource_config, &config, sizeof(config));
+ ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init\n");
return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
@@ -3056,7 +3257,6 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar)
struct sk_buff *buf;
struct wmi_resource_config_10x config = {};
u32 len, val;
- int i;
config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
@@ -3110,32 +3310,8 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar)
cmd = (struct wmi_init_cmd_10x *)buf->data;
- if (ar->wmi.num_mem_chunks == 0) {
- cmd->num_host_mem_chunks = 0;
- goto out;
- }
-
- ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
- ar->wmi.num_mem_chunks);
-
- cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
-
- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- cmd->host_mem_chunks[i].ptr =
- __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
- cmd->host_mem_chunks[i].size =
- __cpu_to_le32(ar->wmi.mem_chunks[i].len);
- cmd->host_mem_chunks[i].req_id =
- __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi chunk %d len %d requested, addr 0x%llx\n",
- i,
- ar->wmi.mem_chunks[i].len,
- (unsigned long long)ar->wmi.mem_chunks[i].paddr);
- }
-out:
memcpy(&cmd->resource_config, &config, sizeof(config));
+ ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10x\n");
return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
@@ -3147,7 +3323,6 @@ static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar)
struct sk_buff *buf;
struct wmi_resource_config_10x config = {};
u32 len, val;
- int i;
config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
@@ -3201,32 +3376,8 @@ static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar)
cmd = (struct wmi_init_cmd_10_2 *)buf->data;
- if (ar->wmi.num_mem_chunks == 0) {
- cmd->num_host_mem_chunks = 0;
- goto out;
- }
-
- ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
- ar->wmi.num_mem_chunks);
-
- cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
-
- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- cmd->host_mem_chunks[i].ptr =
- __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
- cmd->host_mem_chunks[i].size =
- __cpu_to_le32(ar->wmi.mem_chunks[i].len);
- cmd->host_mem_chunks[i].req_id =
- __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi chunk %d len %d requested, addr 0x%llx\n",
- i,
- ar->wmi.mem_chunks[i].len,
- (unsigned long long)ar->wmi.mem_chunks[i].paddr);
- }
-out:
memcpy(&cmd->resource_config.common, &config, sizeof(config));
+ ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10.2\n");
return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
@@ -3248,52 +3399,50 @@ int ath10k_wmi_cmd_init(struct ath10k *ar)
return ret;
}
-static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar,
- const struct wmi_start_scan_arg *arg)
+static int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg)
{
- int len;
+ if (arg->ie_len && !arg->ie)
+ return -EINVAL;
+ if (arg->n_channels && !arg->channels)
+ return -EINVAL;
+ if (arg->n_ssids && !arg->ssids)
+ return -EINVAL;
+ if (arg->n_bssids && !arg->bssids)
+ return -EINVAL;
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
- len = sizeof(struct wmi_start_scan_cmd_10x);
- else
- len = sizeof(struct wmi_start_scan_cmd);
+ if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN)
+ return -EINVAL;
+ if (arg->n_channels > ARRAY_SIZE(arg->channels))
+ return -EINVAL;
+ if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID)
+ return -EINVAL;
+ if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID)
+ return -EINVAL;
- if (arg->ie_len) {
- if (!arg->ie)
- return -EINVAL;
- if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN)
- return -EINVAL;
+ return 0;
+}
+static size_t
+ath10k_wmi_start_scan_tlvs_len(const struct wmi_start_scan_arg *arg)
+{
+ int len = 0;
+
+ if (arg->ie_len) {
len += sizeof(struct wmi_ie_data);
len += roundup(arg->ie_len, 4);
}
if (arg->n_channels) {
- if (!arg->channels)
- return -EINVAL;
- if (arg->n_channels > ARRAY_SIZE(arg->channels))
- return -EINVAL;
-
len += sizeof(struct wmi_chan_list);
len += sizeof(__le32) * arg->n_channels;
}
if (arg->n_ssids) {
- if (!arg->ssids)
- return -EINVAL;
- if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID)
- return -EINVAL;
-
len += sizeof(struct wmi_ssid_list);
len += sizeof(struct wmi_ssid) * arg->n_ssids;
}
if (arg->n_bssids) {
- if (!arg->bssids)
- return -EINVAL;
- if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID)
- return -EINVAL;
-
len += sizeof(struct wmi_bssid_list);
len += sizeof(struct wmi_mac_addr) * arg->n_bssids;
}
@@ -3301,28 +3450,12 @@ static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar,
return len;
}
-int ath10k_wmi_start_scan(struct ath10k *ar,
- const struct wmi_start_scan_arg *arg)
+static void
+ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
+ const struct wmi_start_scan_arg *arg)
{
- struct wmi_start_scan_cmd *cmd;
- struct sk_buff *skb;
- struct wmi_ie_data *ie;
- struct wmi_chan_list *channels;
- struct wmi_ssid_list *ssids;
- struct wmi_bssid_list *bssids;
u32 scan_id;
u32 scan_req_id;
- int off;
- int len = 0;
- int i;
-
- len = ath10k_wmi_start_scan_calc_len(ar, arg);
- if (len < 0)
- return len; /* len contains error code here */
-
- skb = ath10k_wmi_alloc_skb(ar, len);
- if (!skb)
- return -ENOMEM;
scan_id = WMI_HOST_SCAN_REQ_ID_PREFIX;
scan_id |= arg->scan_id;
@@ -3330,35 +3463,36 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
scan_req_id = WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
scan_req_id |= arg->scan_req_id;
- cmd = (struct wmi_start_scan_cmd *)skb->data;
- cmd->scan_id = __cpu_to_le32(scan_id);
- cmd->scan_req_id = __cpu_to_le32(scan_req_id);
- cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
- cmd->scan_priority = __cpu_to_le32(arg->scan_priority);
- cmd->notify_scan_events = __cpu_to_le32(arg->notify_scan_events);
- cmd->dwell_time_active = __cpu_to_le32(arg->dwell_time_active);
- cmd->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive);
- cmd->min_rest_time = __cpu_to_le32(arg->min_rest_time);
- cmd->max_rest_time = __cpu_to_le32(arg->max_rest_time);
- cmd->repeat_probe_time = __cpu_to_le32(arg->repeat_probe_time);
- cmd->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time);
- cmd->idle_time = __cpu_to_le32(arg->idle_time);
- cmd->max_scan_time = __cpu_to_le32(arg->max_scan_time);
- cmd->probe_delay = __cpu_to_le32(arg->probe_delay);
- cmd->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags);
-
- /* TLV list starts after fields included in the struct */
- /* There's just one filed that differes the two start_scan
- * structures - burst_duration, which we are not using btw,
- no point to make the split here, just shift the buffer to fit with
- given FW */
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
- off = sizeof(struct wmi_start_scan_cmd_10x);
- else
- off = sizeof(struct wmi_start_scan_cmd);
+ cmn->scan_id = __cpu_to_le32(scan_id);
+ cmn->scan_req_id = __cpu_to_le32(scan_req_id);
+ cmn->vdev_id = __cpu_to_le32(arg->vdev_id);
+ cmn->scan_priority = __cpu_to_le32(arg->scan_priority);
+ cmn->notify_scan_events = __cpu_to_le32(arg->notify_scan_events);
+ cmn->dwell_time_active = __cpu_to_le32(arg->dwell_time_active);
+ cmn->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive);
+ cmn->min_rest_time = __cpu_to_le32(arg->min_rest_time);
+ cmn->max_rest_time = __cpu_to_le32(arg->max_rest_time);
+ cmn->repeat_probe_time = __cpu_to_le32(arg->repeat_probe_time);
+ cmn->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time);
+ cmn->idle_time = __cpu_to_le32(arg->idle_time);
+ cmn->max_scan_time = __cpu_to_le32(arg->max_scan_time);
+ cmn->probe_delay = __cpu_to_le32(arg->probe_delay);
+ cmn->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags);
+}
+
+static void
+ath10k_wmi_put_start_scan_tlvs(struct wmi_start_scan_tlvs *tlvs,
+ const struct wmi_start_scan_arg *arg)
+{
+ struct wmi_ie_data *ie;
+ struct wmi_chan_list *channels;
+ struct wmi_ssid_list *ssids;
+ struct wmi_bssid_list *bssids;
+ void *ptr = tlvs->tlvs;
+ int i;
if (arg->n_channels) {
- channels = (void *)skb->data + off;
+ channels = ptr;
channels->tag = __cpu_to_le32(WMI_CHAN_LIST_TAG);
channels->num_chan = __cpu_to_le32(arg->n_channels);
@@ -3366,12 +3500,12 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
channels->channel_list[i].freq =
__cpu_to_le16(arg->channels[i]);
- off += sizeof(*channels);
- off += sizeof(__le32) * arg->n_channels;
+ ptr += sizeof(*channels);
+ ptr += sizeof(__le32) * arg->n_channels;
}
if (arg->n_ssids) {
- ssids = (void *)skb->data + off;
+ ssids = ptr;
ssids->tag = __cpu_to_le32(WMI_SSID_LIST_TAG);
ssids->num_ssids = __cpu_to_le32(arg->n_ssids);
@@ -3383,12 +3517,12 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
arg->ssids[i].len);
}
- off += sizeof(*ssids);
- off += sizeof(struct wmi_ssid) * arg->n_ssids;
+ ptr += sizeof(*ssids);
+ ptr += sizeof(struct wmi_ssid) * arg->n_ssids;
}
if (arg->n_bssids) {
- bssids = (void *)skb->data + off;
+ bssids = ptr;
bssids->tag = __cpu_to_le32(WMI_BSSID_LIST_TAG);
bssids->num_bssid = __cpu_to_le32(arg->n_bssids);
@@ -3397,23 +3531,57 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
arg->bssids[i].bssid,
ETH_ALEN);
- off += sizeof(*bssids);
- off += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+ ptr += sizeof(*bssids);
+ ptr += sizeof(struct wmi_mac_addr) * arg->n_bssids;
}
if (arg->ie_len) {
- ie = (void *)skb->data + off;
+ ie = ptr;
ie->tag = __cpu_to_le32(WMI_IE_TAG);
ie->ie_len = __cpu_to_le32(arg->ie_len);
memcpy(ie->ie_data, arg->ie, arg->ie_len);
- off += sizeof(*ie);
- off += roundup(arg->ie_len, 4);
+ ptr += sizeof(*ie);
+ ptr += roundup(arg->ie_len, 4);
}
+}
- if (off != skb->len) {
- dev_kfree_skb(skb);
- return -EINVAL;
+int ath10k_wmi_start_scan(struct ath10k *ar,
+ const struct wmi_start_scan_arg *arg)
+{
+ struct sk_buff *skb;
+ size_t len;
+ int ret;
+
+ ret = ath10k_wmi_start_scan_verify(arg);
+ if (ret)
+ return ret;
+
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+ len = sizeof(struct wmi_10x_start_scan_cmd) +
+ ath10k_wmi_start_scan_tlvs_len(arg);
+ else
+ len = sizeof(struct wmi_start_scan_cmd) +
+ ath10k_wmi_start_scan_tlvs_len(arg);
+
+ skb = ath10k_wmi_alloc_skb(ar, len);
+ if (!skb)
+ return -ENOMEM;
+
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+ struct wmi_10x_start_scan_cmd *cmd;
+
+ cmd = (struct wmi_10x_start_scan_cmd *)skb->data;
+ ath10k_wmi_put_start_scan_common(&cmd->common, arg);
+ ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
+ } else {
+ struct wmi_start_scan_cmd *cmd;
+
+ cmd = (struct wmi_start_scan_cmd *)skb->data;
+ cmd->burst_duration_ms = __cpu_to_le32(0);
+
+ ath10k_wmi_put_start_scan_common(&cmd->common, arg);
+ ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
}
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi start scan\n");
@@ -3532,7 +3700,6 @@ ath10k_wmi_vdev_start_restart(struct ath10k *ar,
struct sk_buff *skb;
const char *cmdname;
u32 flags = 0;
- u32 ch_flags = 0;
if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
@@ -3559,8 +3726,6 @@ ath10k_wmi_vdev_start_restart(struct ath10k *ar,
flags |= WMI_VDEV_START_HIDDEN_SSID;
if (arg->pmf_enabled)
flags |= WMI_VDEV_START_PMF_ENABLED;
- if (arg->channel.chan_radar)
- ch_flags |= WMI_CHAN_FLAG_DFS;
cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
@@ -3576,18 +3741,7 @@ ath10k_wmi_vdev_start_restart(struct ath10k *ar,
memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len);
}
- cmd->chan.mhz = __cpu_to_le32(arg->channel.freq);
-
- cmd->chan.band_center_freq1 =
- __cpu_to_le32(arg->channel.band_center_freq1);
-
- cmd->chan.mode = arg->channel.mode;
- cmd->chan.flags |= __cpu_to_le32(ch_flags);
- cmd->chan.min_power = arg->channel.min_power;
- cmd->chan.max_power = arg->channel.max_power;
- cmd->chan.reg_power = arg->channel.max_reg_power;
- cmd->chan.reg_classid = arg->channel.reg_class_id;
- cmd->chan.antenna_max = arg->channel.max_antenna_gain;
+ ath10k_wmi_put_wmi_channel(&cmd->chan, &arg->channel);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, ch_flags: 0x%0X, max_power: %d\n",
@@ -3968,35 +4122,10 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar,
cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
for (i = 0; i < arg->n_channels; i++) {
- u32 flags = 0;
-
ch = &arg->channels[i];
ci = &cmd->chan_info[i];
- if (ch->passive)
- flags |= WMI_CHAN_FLAG_PASSIVE;
- if (ch->allow_ibss)
- flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED;
- if (ch->allow_ht)
- flags |= WMI_CHAN_FLAG_ALLOW_HT;
- if (ch->allow_vht)
- flags |= WMI_CHAN_FLAG_ALLOW_VHT;
- if (ch->ht40plus)
- flags |= WMI_CHAN_FLAG_HT40_PLUS;
- if (ch->chan_radar)
- flags |= WMI_CHAN_FLAG_DFS;
-
- ci->mhz = __cpu_to_le32(ch->freq);
- ci->band_center_freq1 = __cpu_to_le32(ch->freq);
- ci->band_center_freq2 = 0;
- ci->min_power = ch->min_power;
- ci->max_power = ch->max_power;
- ci->reg_power = ch->max_reg_power;
- ci->antenna_max = ch->max_antenna_gain;
-
- /* mode & flags share storage */
- ci->mode = ch->mode;
- ci->flags |= __cpu_to_le32(flags);
+ ath10k_wmi_put_wmi_channel(ci, ch);
}
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
@@ -4108,9 +4237,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
- ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg);
- else
ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg);
+ else
+ ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg);
} else {
ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg);
}
@@ -4267,3 +4396,73 @@ int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable)
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid);
}
+
+int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_bitmap)
+{
+ struct wmi_pdev_pktlog_enable_cmd *cmd;
+ struct sk_buff *skb;
+
+ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ ev_bitmap &= ATH10K_PKTLOG_ANY;
+ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi enable pktlog filter:%x\n", ev_bitmap);
+
+ cmd = (struct wmi_pdev_pktlog_enable_cmd *)skb->data;
+ cmd->ev_bitmap = __cpu_to_le32(ev_bitmap);
+ return ath10k_wmi_cmd_send(ar, skb,
+ ar->wmi.cmd->pdev_pktlog_enable_cmdid);
+}
+
+int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar)
+{
+ struct sk_buff *skb;
+
+ skb = ath10k_wmi_alloc_skb(ar, 0);
+ if (!skb)
+ return -ENOMEM;
+
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi disable pktlog\n");
+
+ return ath10k_wmi_cmd_send(ar, skb,
+ ar->wmi.cmd->pdev_pktlog_disable_cmdid);
+}
+
+int ath10k_wmi_attach(struct ath10k *ar)
+{
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
+ ar->wmi.cmd = &wmi_10_2_cmd_map;
+ else
+ ar->wmi.cmd = &wmi_10x_cmd_map;
+
+ ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
+ ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+ } else {
+ ar->wmi.cmd = &wmi_cmd_map;
+ ar->wmi.vdev_param = &wmi_vdev_param_map;
+ ar->wmi.pdev_param = &wmi_pdev_param_map;
+ }
+
+ init_completion(&ar->wmi.service_ready);
+ init_completion(&ar->wmi.unified_ready);
+
+ return 0;
+}
+
+void ath10k_wmi_detach(struct ath10k *ar)
+{
+ int i;
+
+ /* free the host memory chunks requested by firmware */
+ for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+ dma_free_coherent(ar->dev,
+ ar->wmi.mem_chunks[i].len,
+ ar->wmi.mem_chunks[i].vaddr,
+ ar->wmi.mem_chunks[i].paddr);
+ }
+
+ ar->wmi.num_mem_chunks = 0;
+}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 86f5ebccfe79..21391929d318 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -222,128 +222,131 @@ static inline char *wmi_service_name(int service_id)
#undef SVCSTR
}
-#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \
- (__le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
+#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
+ ((svc_id) < (len) && \
+ __le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
BIT((svc_id)%(sizeof(u32))))
-#define SVCMAP(x, y) \
+#define SVCMAP(x, y, len) \
do { \
- if (WMI_SERVICE_IS_ENABLED((in), (x))) \
+ if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \
__set_bit(y, out); \
} while (0)
-static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out)
+static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out,
+ size_t len)
{
SVCMAP(WMI_10X_SERVICE_BEACON_OFFLOAD,
- WMI_SERVICE_BEACON_OFFLOAD);
+ WMI_SERVICE_BEACON_OFFLOAD, len);
SVCMAP(WMI_10X_SERVICE_SCAN_OFFLOAD,
- WMI_SERVICE_SCAN_OFFLOAD);
+ WMI_SERVICE_SCAN_OFFLOAD, len);
SVCMAP(WMI_10X_SERVICE_ROAM_OFFLOAD,
- WMI_SERVICE_ROAM_OFFLOAD);
+ WMI_SERVICE_ROAM_OFFLOAD, len);
SVCMAP(WMI_10X_SERVICE_BCN_MISS_OFFLOAD,
- WMI_SERVICE_BCN_MISS_OFFLOAD);
+ WMI_SERVICE_BCN_MISS_OFFLOAD, len);
SVCMAP(WMI_10X_SERVICE_STA_PWRSAVE,
- WMI_SERVICE_STA_PWRSAVE);
+ WMI_SERVICE_STA_PWRSAVE, len);
SVCMAP(WMI_10X_SERVICE_STA_ADVANCED_PWRSAVE,
- WMI_SERVICE_STA_ADVANCED_PWRSAVE);
+ WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
SVCMAP(WMI_10X_SERVICE_AP_UAPSD,
- WMI_SERVICE_AP_UAPSD);
+ WMI_SERVICE_AP_UAPSD, len);
SVCMAP(WMI_10X_SERVICE_AP_DFS,
- WMI_SERVICE_AP_DFS);
+ WMI_SERVICE_AP_DFS, len);
SVCMAP(WMI_10X_SERVICE_11AC,
- WMI_SERVICE_11AC);
+ WMI_SERVICE_11AC, len);
SVCMAP(WMI_10X_SERVICE_BLOCKACK,
- WMI_SERVICE_BLOCKACK);
+ WMI_SERVICE_BLOCKACK, len);
SVCMAP(WMI_10X_SERVICE_PHYERR,
- WMI_SERVICE_PHYERR);
+ WMI_SERVICE_PHYERR, len);
SVCMAP(WMI_10X_SERVICE_BCN_FILTER,
- WMI_SERVICE_BCN_FILTER);
+ WMI_SERVICE_BCN_FILTER, len);
SVCMAP(WMI_10X_SERVICE_RTT,
- WMI_SERVICE_RTT);
+ WMI_SERVICE_RTT, len);
SVCMAP(WMI_10X_SERVICE_RATECTRL,
- WMI_SERVICE_RATECTRL);
+ WMI_SERVICE_RATECTRL, len);
SVCMAP(WMI_10X_SERVICE_WOW,
- WMI_SERVICE_WOW);
+ WMI_SERVICE_WOW, len);
SVCMAP(WMI_10X_SERVICE_RATECTRL_CACHE,
- WMI_SERVICE_RATECTRL_CACHE);
+ WMI_SERVICE_RATECTRL_CACHE, len);
SVCMAP(WMI_10X_SERVICE_IRAM_TIDS,
- WMI_SERVICE_IRAM_TIDS);
+ WMI_SERVICE_IRAM_TIDS, len);
SVCMAP(WMI_10X_SERVICE_BURST,
- WMI_SERVICE_BURST);
+ WMI_SERVICE_BURST, len);
SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_SW_SUPPORT,
- WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT);
+ WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT, len);
SVCMAP(WMI_10X_SERVICE_FORCE_FW_HANG,
- WMI_SERVICE_FORCE_FW_HANG);
+ WMI_SERVICE_FORCE_FW_HANG, len);
SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT,
- WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT);
+ WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT, len);
}
-static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out)
+static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
+ size_t len)
{
SVCMAP(WMI_MAIN_SERVICE_BEACON_OFFLOAD,
- WMI_SERVICE_BEACON_OFFLOAD);
+ WMI_SERVICE_BEACON_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_SCAN_OFFLOAD,
- WMI_SERVICE_SCAN_OFFLOAD);
+ WMI_SERVICE_SCAN_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_ROAM_OFFLOAD,
- WMI_SERVICE_ROAM_OFFLOAD);
+ WMI_SERVICE_ROAM_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_BCN_MISS_OFFLOAD,
- WMI_SERVICE_BCN_MISS_OFFLOAD);
+ WMI_SERVICE_BCN_MISS_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_STA_PWRSAVE,
- WMI_SERVICE_STA_PWRSAVE);
+ WMI_SERVICE_STA_PWRSAVE, len);
SVCMAP(WMI_MAIN_SERVICE_STA_ADVANCED_PWRSAVE,
- WMI_SERVICE_STA_ADVANCED_PWRSAVE);
+ WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
SVCMAP(WMI_MAIN_SERVICE_AP_UAPSD,
- WMI_SERVICE_AP_UAPSD);
+ WMI_SERVICE_AP_UAPSD, len);
SVCMAP(WMI_MAIN_SERVICE_AP_DFS,
- WMI_SERVICE_AP_DFS);
+ WMI_SERVICE_AP_DFS, len);
SVCMAP(WMI_MAIN_SERVICE_11AC,
- WMI_SERVICE_11AC);
+ WMI_SERVICE_11AC, len);
SVCMAP(WMI_MAIN_SERVICE_BLOCKACK,
- WMI_SERVICE_BLOCKACK);
+ WMI_SERVICE_BLOCKACK, len);
SVCMAP(WMI_MAIN_SERVICE_PHYERR,
- WMI_SERVICE_PHYERR);
+ WMI_SERVICE_PHYERR, len);
SVCMAP(WMI_MAIN_SERVICE_BCN_FILTER,
- WMI_SERVICE_BCN_FILTER);
+ WMI_SERVICE_BCN_FILTER, len);
SVCMAP(WMI_MAIN_SERVICE_RTT,
- WMI_SERVICE_RTT);
+ WMI_SERVICE_RTT, len);
SVCMAP(WMI_MAIN_SERVICE_RATECTRL,
- WMI_SERVICE_RATECTRL);
+ WMI_SERVICE_RATECTRL, len);
SVCMAP(WMI_MAIN_SERVICE_WOW,
- WMI_SERVICE_WOW);
+ WMI_SERVICE_WOW, len);
SVCMAP(WMI_MAIN_SERVICE_RATECTRL_CACHE,
- WMI_SERVICE_RATECTRL_CACHE);
+ WMI_SERVICE_RATECTRL_CACHE, len);
SVCMAP(WMI_MAIN_SERVICE_IRAM_TIDS,
- WMI_SERVICE_IRAM_TIDS);
+ WMI_SERVICE_IRAM_TIDS, len);
SVCMAP(WMI_MAIN_SERVICE_ARPNS_OFFLOAD,
- WMI_SERVICE_ARPNS_OFFLOAD);
+ WMI_SERVICE_ARPNS_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_NLO,
- WMI_SERVICE_NLO);
+ WMI_SERVICE_NLO, len);
SVCMAP(WMI_MAIN_SERVICE_GTK_OFFLOAD,
- WMI_SERVICE_GTK_OFFLOAD);
+ WMI_SERVICE_GTK_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_SCAN_SCH,
- WMI_SERVICE_SCAN_SCH);
+ WMI_SERVICE_SCAN_SCH, len);
SVCMAP(WMI_MAIN_SERVICE_CSA_OFFLOAD,
- WMI_SERVICE_CSA_OFFLOAD);
+ WMI_SERVICE_CSA_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_CHATTER,
- WMI_SERVICE_CHATTER);
+ WMI_SERVICE_CHATTER, len);
SVCMAP(WMI_MAIN_SERVICE_COEX_FREQAVOID,
- WMI_SERVICE_COEX_FREQAVOID);
+ WMI_SERVICE_COEX_FREQAVOID, len);
SVCMAP(WMI_MAIN_SERVICE_PACKET_POWER_SAVE,
- WMI_SERVICE_PACKET_POWER_SAVE);
+ WMI_SERVICE_PACKET_POWER_SAVE, len);
SVCMAP(WMI_MAIN_SERVICE_FORCE_FW_HANG,
- WMI_SERVICE_FORCE_FW_HANG);
+ WMI_SERVICE_FORCE_FW_HANG, len);
SVCMAP(WMI_MAIN_SERVICE_GPIO,
- WMI_SERVICE_GPIO);
+ WMI_SERVICE_GPIO, len);
SVCMAP(WMI_MAIN_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
- WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM);
+ WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, len);
SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
- WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG);
+ WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, len);
SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
- WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG);
+ WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, len);
SVCMAP(WMI_MAIN_SERVICE_STA_KEEP_ALIVE,
- WMI_SERVICE_STA_KEEP_ALIVE);
+ WMI_SERVICE_STA_KEEP_ALIVE, len);
SVCMAP(WMI_MAIN_SERVICE_TX_ENCAP,
- WMI_SERVICE_TX_ENCAP);
+ WMI_SERVICE_TX_ENCAP, len);
}
#undef SVCMAP
@@ -1428,11 +1431,11 @@ struct wmi_service_ready_event {
* where FW can access this memory directly (or) by DMA.
*/
__le32 num_mem_reqs;
- struct wlan_host_mem_req mem_reqs[1];
+ struct wlan_host_mem_req mem_reqs[0];
} __packed;
/* This is the definition from 10.X firmware branch */
-struct wmi_service_ready_event_10x {
+struct wmi_10x_service_ready_event {
__le32 sw_version;
__le32 abi_version;
@@ -1467,7 +1470,7 @@ struct wmi_service_ready_event_10x {
*/
__le32 num_mem_reqs;
- struct wlan_host_mem_req mem_reqs[1];
+ struct wlan_host_mem_req mem_reqs[0];
} __packed;
#define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
@@ -1883,38 +1886,26 @@ struct host_memory_chunk {
__le32 size;
} __packed;
+struct wmi_host_mem_chunks {
+ __le32 count;
+ /* some fw revisions require at least 1 chunk regardless of count */
+ struct host_memory_chunk items[1];
+} __packed;
+
struct wmi_init_cmd {
struct wmi_resource_config resource_config;
- __le32 num_host_mem_chunks;
-
- /*
- * variable number of host memory chunks.
- * This should be the last element in the structure
- */
- struct host_memory_chunk host_mem_chunks[1];
+ struct wmi_host_mem_chunks mem_chunks;
} __packed;
/* _10x stucture is from 10.X FW API */
struct wmi_init_cmd_10x {
struct wmi_resource_config_10x resource_config;
- __le32 num_host_mem_chunks;
-
- /*
- * variable number of host memory chunks.
- * This should be the last element in the structure
- */
- struct host_memory_chunk host_mem_chunks[1];
+ struct wmi_host_mem_chunks mem_chunks;
} __packed;
struct wmi_init_cmd_10_2 {
struct wmi_resource_config_10_2 resource_config;
- __le32 num_host_mem_chunks;
-
- /*
- * variable number of host memory chunks.
- * This should be the last element in the structure
- */
- struct host_memory_chunk host_mem_chunks[1];
+ struct wmi_host_mem_chunks mem_chunks;
} __packed;
struct wmi_chan_list_entry {
@@ -1964,6 +1955,11 @@ struct wmi_ssid_list {
#define WLAN_SCAN_PARAMS_MAX_BSSID 4
#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256
+/* Values lower than this may be refused by some firmware revisions with a scan
+ * completion with a timedout reason.
+ */
+#define WMI_SCAN_CHAN_MIN_TIME_MSEC 40
+
/* Scan priority numbers must be sequential, starting with 0 */
enum wmi_scan_priority {
WMI_SCAN_PRIORITY_VERY_LOW = 0,
@@ -1974,7 +1970,7 @@ enum wmi_scan_priority {
WMI_SCAN_PRIORITY_COUNT /* number of priorities supported */
};
-struct wmi_start_scan_cmd {
+struct wmi_start_scan_common {
/* Scan ID */
__le32 scan_id;
/* Scan requestor ID */
@@ -2032,95 +2028,25 @@ struct wmi_start_scan_cmd {
__le32 probe_delay;
/* Scan control flags */
__le32 scan_ctrl_flags;
-
- /* Burst duration time in msecs */
- __le32 burst_duration;
- /*
- * TLV (tag length value ) paramerters follow the scan_cmd structure.
- * TLV can contain channel list, bssid list, ssid list and
- * ie. the TLV tags are defined above;
- */
} __packed;
-/* This is the definition from 10.X firmware branch */
-struct wmi_start_scan_cmd_10x {
- /* Scan ID */
- __le32 scan_id;
-
- /* Scan requestor ID */
- __le32 scan_req_id;
-
- /* VDEV id(interface) that is requesting scan */
- __le32 vdev_id;
-
- /* Scan Priority, input to scan scheduler */
- __le32 scan_priority;
-
- /* Scan events subscription */
- __le32 notify_scan_events;
-
- /* dwell time in msec on active channels */
- __le32 dwell_time_active;
-
- /* dwell time in msec on passive channels */
- __le32 dwell_time_passive;
-
- /*
- * min time in msec on the BSS channel,only valid if atleast one
- * VDEV is active
- */
- __le32 min_rest_time;
-
- /*
- * max rest time in msec on the BSS channel,only valid if at least
- * one VDEV is active
- */
- /*
- * the scanner will rest on the bss channel at least min_rest_time
- * after min_rest_time the scanner will start checking for tx/rx
- * activity on all VDEVs. if there is no activity the scanner will
- * switch to off channel. if there is activity the scanner will let
- * the radio on the bss channel until max_rest_time expires.at
- * max_rest_time scanner will switch to off channel irrespective of
- * activity. activity is determined by the idle_time parameter.
- */
- __le32 max_rest_time;
-
- /*
- * time before sending next set of probe requests.
- * The scanner keeps repeating probe requests transmission with
- * period specified by repeat_probe_time.
- * The number of probe requests specified depends on the ssid_list
- * and bssid_list
- */
- __le32 repeat_probe_time;
-
- /* time in msec between 2 consequetive probe requests with in a set. */
- __le32 probe_spacing_time;
-
- /*
- * data inactivity time in msec on bss channel that will be used by
- * scanner for measuring the inactivity.
- */
- __le32 idle_time;
-
- /* maximum time in msec allowed for scan */
- __le32 max_scan_time;
-
- /*
- * delay in msec before sending first probe request after switching
- * to a channel
+struct wmi_start_scan_tlvs {
+ /* TLV parameters. These includes channel list, ssid list, bssid list,
+ * extra ies.
*/
- __le32 probe_delay;
+ u8 tlvs[0];
+} __packed;
- /* Scan control flags */
- __le32 scan_ctrl_flags;
+struct wmi_start_scan_cmd {
+ struct wmi_start_scan_common common;
+ __le32 burst_duration_ms;
+ struct wmi_start_scan_tlvs tlvs;
+} __packed;
- /*
- * TLV (tag length value ) paramerters follow the scan_cmd structure.
- * TLV can contain channel list, bssid list, ssid list and
- * ie. the TLV tags are defined above;
- */
+/* This is the definition from 10.X firmware branch */
+struct wmi_10x_start_scan_cmd {
+ struct wmi_start_scan_common common;
+ struct wmi_start_scan_tlvs tlvs;
} __packed;
struct wmi_ssid_arg {
@@ -2306,94 +2232,25 @@ struct wmi_mgmt_rx_event_v2 {
#define PHY_ERROR_FALSE_RADAR_EXT 0x24
#define PHY_ERROR_RADAR 0x05
-struct wmi_single_phyerr_rx_hdr {
- /* TSF timestamp */
+struct wmi_phyerr {
__le32 tsf_timestamp;
-
- /*
- * Current freq1, freq2
- *
- * [7:0]: freq1[lo]
- * [15:8] : freq1[hi]
- * [23:16]: freq2[lo]
- * [31:24]: freq2[hi]
- */
__le16 freq1;
__le16 freq2;
-
- /*
- * Combined RSSI over all chains and channel width for this PHY error
- *
- * [7:0]: RSSI combined
- * [15:8]: Channel width (MHz)
- * [23:16]: PHY error code
- * [24:16]: reserved (future use)
- */
u8 rssi_combined;
u8 chan_width_mhz;
u8 phy_err_code;
u8 rsvd0;
-
- /*
- * RSSI on chain 0 through 3
- *
- * This is formatted the same as the PPDU_START RX descriptor
- * field:
- *
- * [7:0]: pri20
- * [15:8]: sec20
- * [23:16]: sec40
- * [31:24]: sec80
- */
-
- __le32 rssi_chain0;
- __le32 rssi_chain1;
- __le32 rssi_chain2;
- __le32 rssi_chain3;
-
- /*
- * Last calibrated NF value for chain 0 through 3
- *
- * nf_list_1:
- *
- * + [15:0] - chain 0
- * + [31:16] - chain 1
- *
- * nf_list_2:
- *
- * + [15:0] - chain 2
- * + [31:16] - chain 3
- */
- __le32 nf_list_1;
- __le32 nf_list_2;
-
- /* Length of the frame */
+ __le32 rssi_chains[4];
+ __le16 nf_chains[4];
__le32 buf_len;
+ u8 buf[0];
} __packed;
-struct wmi_single_phyerr_rx_event {
- /* Phy error event header */
- struct wmi_single_phyerr_rx_hdr hdr;
- /* frame buffer */
- u8 bufp[0];
-} __packed;
-
-struct wmi_comb_phyerr_rx_hdr {
- /* Phy error phy error count */
- __le32 num_phyerr_events;
+struct wmi_phyerr_event {
+ __le32 num_phyerrs;
__le32 tsf_l32;
__le32 tsf_u32;
-} __packed;
-
-struct wmi_comb_phyerr_rx_event {
- /* Phy error phy error count */
- struct wmi_comb_phyerr_rx_hdr hdr;
- /*
- * frame buffer - contains multiple payloads in the order:
- * header - payload, header - payload...
- * (The header is of type: wmi_single_phyerr_rx_hdr)
- */
- u8 bufp[0];
+ struct wmi_phyerr phyerrs[0];
} __packed;
#define PHYERR_TLV_SIG 0xBB
@@ -2908,11 +2765,6 @@ enum wmi_tp_scale {
WMI_TP_SCALE_SIZE = 5, /* max num of enum */
};
-struct wmi_set_channel_cmd {
- /* channel (only frequency and mode info are used) */
- struct wmi_channel chan;
-} __packed;
-
struct wmi_pdev_chanlist_update_event {
/* number of channels */
__le32 num_chan;
@@ -2943,6 +2795,10 @@ struct wmi_pdev_set_channel_cmd {
struct wmi_channel chan;
} __packed;
+struct wmi_pdev_pktlog_enable_cmd {
+ __le32 ev_bitmap;
+} __packed;
+
/* Customize the DSCP (bit) to TID (0-7) mapping for QOS */
#define WMI_DSCP_MAP_MAX (64)
struct wmi_pdev_set_dscp_tid_map_cmd {
@@ -3177,7 +3033,7 @@ struct wmi_stats_event {
* PDEV statistics
* TODO: add all PDEV stats here
*/
-struct wmi_pdev_stats_old {
+struct wmi_pdev_stats {
__le32 chan_nf; /* Channel noise floor */
__le32 tx_frame_count; /* TX frame count */
__le32 rx_frame_count; /* RX frame count */
@@ -3188,15 +3044,8 @@ struct wmi_pdev_stats_old {
struct wal_dbg_stats wal; /* WAL dbg stats */
} __packed;
-struct wmi_pdev_stats_10x {
- __le32 chan_nf; /* Channel noise floor */
- __le32 tx_frame_count; /* TX frame count */
- __le32 rx_frame_count; /* RX frame count */
- __le32 rx_clear_count; /* rx clear count */
- __le32 cycle_count; /* cycle count */
- __le32 phy_err_count; /* Phy error count */
- __le32 chan_tx_pwr; /* channel tx power */
- struct wal_dbg_stats wal; /* WAL dbg stats */
+struct wmi_10x_pdev_stats {
+ struct wmi_pdev_stats old;
__le32 ack_rx_bad;
__le32 rts_bad;
__le32 rts_good;
@@ -3217,16 +3066,14 @@ struct wmi_vdev_stats {
* peer statistics.
* TODO: add more stats
*/
-struct wmi_peer_stats_old {
+struct wmi_peer_stats {
struct wmi_mac_addr peer_macaddr;
__le32 peer_rssi;
__le32 peer_tx_rate;
} __packed;
-struct wmi_peer_stats_10x {
- struct wmi_mac_addr peer_macaddr;
- __le32 peer_rssi;
- __le32 peer_tx_rate;
+struct wmi_10x_peer_stats {
+ struct wmi_peer_stats old;
__le32 peer_rx_rate;
} __packed;
@@ -4708,7 +4555,6 @@ struct wmi_dbglog_cfg_cmd {
__le32 config_valid;
} __packed;
-#define ATH10K_RTS_MAX 2347
#define ATH10K_FRAGMT_THRESHOLD_MIN 540
#define ATH10K_FRAGMT_THRESHOLD_MAX 2346
@@ -4719,8 +4565,27 @@ struct wmi_dbglog_cfg_cmd {
/* By default disable power save for IBSS */
#define ATH10K_DEFAULT_ATIM 0
+#define WMI_MAX_MEM_REQS 16
+
+struct wmi_svc_rdy_ev_arg {
+ __le32 min_tx_power;
+ __le32 max_tx_power;
+ __le32 ht_cap;
+ __le32 vht_cap;
+ __le32 sw_ver0;
+ __le32 sw_ver1;
+ __le32 phy_capab;
+ __le32 num_rf_chains;
+ __le32 eeprom_rd;
+ __le32 num_mem_reqs;
+ const __le32 *service_map;
+ size_t service_map_len;
+ const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS];
+};
+
struct ath10k;
struct ath10k_vif;
+struct ath10k_fw_stats;
int ath10k_wmi_attach(struct ath10k *ar);
void ath10k_wmi_detach(struct ath10k *ar);
@@ -4732,8 +4597,6 @@ int ath10k_wmi_connect(struct ath10k *ar);
struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len);
int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
-int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
- const struct wmi_channel_arg *);
int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt);
int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
@@ -4794,5 +4657,9 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar,
enum wmi_force_fw_hang_type type, u32 delay_ms);
int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable);
+int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
+ struct ath10k_fw_stats *stats);
+int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_list);
+int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar);
#endif /* _WMI_H_ */
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index ab2709a43768..19eab2a69ad5 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -547,7 +547,9 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
static void
-ath5k_sw_scan_start(struct ieee80211_hw *hw)
+ath5k_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct ath5k_hw *ah = hw->priv;
if (!ah->assoc)
@@ -556,7 +558,7 @@ ath5k_sw_scan_start(struct ieee80211_hw *hw)
static void
-ath5k_sw_scan_complete(struct ieee80211_hw *hw)
+ath5k_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct ath5k_hw *ah = hw->priv;
ath5k_hw_set_ledstate(ah, ah->assoc ?
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
index 0583c69d26db..ddaad712c59a 100644
--- a/drivers/net/wireless/ath/ath5k/qcu.c
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -225,13 +225,7 @@ ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
} else {
switch (queue_type) {
case AR5K_TX_QUEUE_DATA:
- for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
- ah->ah_txq[queue].tqi_type !=
- AR5K_TX_QUEUE_INACTIVE; queue++) {
-
- if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
- return -EINVAL;
- }
+ queue = queue_info->tqi_subtype;
break;
case AR5K_TX_QUEUE_UAPSD:
queue = AR5K_TX_QUEUE_ID_UAPSD;
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index ba60e37213eb..7a5337877a0c 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -2976,11 +2976,11 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac)
+ struct station_del_parameters *params)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
- const u8 *addr = mac ? mac : bcast_addr;
+ const u8 *addr = params->mac ? params->mac : bcast_addr;
return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h
index 05debf700a84..4f82e8632d37 100644
--- a/drivers/net/wireless/ath/ath6kl/common.h
+++ b/drivers/net/wireless/ath/ath6kl/common.h
@@ -22,7 +22,7 @@
#define ATH6KL_MAX_IE 256
-__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...);
+__printf(2, 3) void ath6kl_printk(const char *level, const char *fmt, ...);
/*
* Reflects the version of binary interface exposed by ATH6KL target
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
index 55c4064dd506..81ba48d2938b 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.c
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -37,76 +37,64 @@ struct ath6kl_fwlog_slot {
#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
-int ath6kl_printk(const char *level, const char *fmt, ...)
+void ath6kl_printk(const char *level, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
- int rtn;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- rtn = printk("%sath6kl: %pV", level, &vaf);
+ printk("%sath6kl: %pV", level, &vaf);
va_end(args);
-
- return rtn;
}
EXPORT_SYMBOL(ath6kl_printk);
-int ath6kl_info(const char *fmt, ...)
+void ath6kl_info(const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = ath6kl_printk(KERN_INFO, "%pV", &vaf);
+ ath6kl_printk(KERN_INFO, "%pV", &vaf);
trace_ath6kl_log_info(&vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath6kl_info);
-int ath6kl_err(const char *fmt, ...)
+void ath6kl_err(const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = ath6kl_printk(KERN_ERR, "%pV", &vaf);
+ ath6kl_printk(KERN_ERR, "%pV", &vaf);
trace_ath6kl_log_err(&vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath6kl_err);
-int ath6kl_warn(const char *fmt, ...)
+void ath6kl_warn(const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = ath6kl_printk(KERN_WARNING, "%pV", &vaf);
+ ath6kl_printk(KERN_WARNING, "%pV", &vaf);
trace_ath6kl_log_warn(&vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath6kl_warn);
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h
index e194c10d9f00..19106ed28961 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.h
+++ b/drivers/net/wireless/ath/ath6kl/debug.h
@@ -50,10 +50,10 @@ enum ATH6K_DEBUG_MASK {
};
extern unsigned int debug_mask;
-__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...);
-__printf(1, 2) int ath6kl_info(const char *fmt, ...);
-__printf(1, 2) int ath6kl_err(const char *fmt, ...);
-__printf(1, 2) int ath6kl_warn(const char *fmt, ...);
+__printf(2, 3) void ath6kl_printk(const char *level, const char *fmt, ...);
+__printf(1, 2) void ath6kl_info(const char *fmt, ...);
+__printf(1, 2) void ath6kl_err(const char *fmt, ...);
+__printf(1, 2) void ath6kl_warn(const char *fmt, ...);
enum ath6kl_war {
ATH6KL_WAR_INVALID_RATE,
@@ -81,10 +81,9 @@ int ath6kl_debug_init_fs(struct ath6kl *ar);
void ath6kl_debug_cleanup(struct ath6kl *ar);
#else
-static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
- const char *fmt, ...)
+static inline void ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
+ const char *fmt, ...)
{
- return 0;
}
static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c
index a6a5e40b3e98..9da3594fd010 100644
--- a/drivers/net/wireless/ath/ath6kl/usb.c
+++ b/drivers/net/wireless/ath/ath6kl/usb.c
@@ -1193,18 +1193,10 @@ static int ath6kl_usb_pm_resume(struct usb_interface *interface)
return 0;
}
-static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf)
-{
- if (usb_get_intfdata(intf))
- ath6kl_usb_remove(intf);
- return 0;
-}
-
#else
#define ath6kl_usb_pm_suspend NULL
#define ath6kl_usb_pm_resume NULL
-#define ath6kl_usb_pm_reset_resume NULL
#endif
@@ -1222,7 +1214,6 @@ static struct usb_driver ath6kl_usb_driver = {
.probe = ath6kl_usb_probe,
.suspend = ath6kl_usb_pm_suspend,
.resume = ath6kl_usb_pm_resume,
- .reset_resume = ath6kl_usb_pm_reset_resume,
.disconnect = ath6kl_usb_remove,
.id_table = ath6kl_usb_ids,
.supports_autosuspend = true,
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index 896e63281b3b..fee0cadb0f5e 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -3,6 +3,8 @@ config ATH9K_HW
config ATH9K_COMMON
tristate
select ATH_COMMON
+ select DEBUG_FS
+ select RELAY
config ATH9K_DFS_DEBUGFS
def_bool y
depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED
@@ -148,6 +150,11 @@ config ATH9K_CHANNEL_CONTEXT
for multi-channel concurrency. Enable this if P2P PowerSave support
is required.
+config ATH9K_PCOEM
+ bool "Atheros ath9k support for PC OEM cards" if EXPERT
+ depends on ATH9K
+ default y
+
config ATH9K_HTC
tristate "Atheros HTC based wireless cards support"
depends on USB && MAC80211
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 73704c1be736..473972288a84 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -16,8 +16,7 @@ ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o
ath9k-$(CONFIG_ATH9K_TX99) += tx99.o
ath9k-$(CONFIG_ATH9K_WOW) += wow.o
-ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o \
- spectral.o
+ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
ath9k-$(CONFIG_ATH9K_STATION_STATISTICS) += debug_sta.o
@@ -32,7 +31,6 @@ ath9k_hw-y:= \
ar5008_phy.o \
ar9002_calib.o \
ar9003_calib.o \
- ar9003_rtt.o \
calib.o \
eeprom.o \
eeprom_def.o \
@@ -50,6 +48,8 @@ ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o
ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \
ar9003_mci.o
+ath9k_hw-$(CONFIG_ATH9K_PCOEM) += ar9003_rtt.o
+
ath9k_hw-$(CONFIG_ATH9K_DYNACK) += dynack.o
obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o
@@ -58,7 +58,8 @@ obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
ath9k_common-y:= common.o \
common-init.o \
common-beacon.o \
- common-debug.o
+ common-debug.o \
+ common-spectral.o
ath9k_htc-y += htc_hst.o \
hif_usb.o \
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index b72d0be716db..5829074208fa 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -1190,7 +1190,7 @@ static void ar5008_hw_set_nf_limits(struct ath_hw *ah)
static void ar5008_hw_set_radar_params(struct ath_hw *ah,
struct ath_hw_radar_conf *conf)
{
- u32 radar_0 = 0, radar_1 = 0;
+ u32 radar_0 = 0, radar_1;
if (!conf) {
REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
@@ -1204,6 +1204,9 @@ static void ar5008_hw_set_radar_params(struct ath_hw *ah,
radar_0 |= SM(conf->pulse_rssi, AR_PHY_RADAR_0_PRSSI);
radar_0 |= SM(conf->pulse_inband, AR_PHY_RADAR_0_INBAND);
+ radar_1 = REG_READ(ah, AR_PHY_RADAR_1);
+ radar_1 &= ~(AR_PHY_RADAR_1_MAXLEN | AR_PHY_RADAR_1_RELSTEP_THRESH |
+ AR_PHY_RADAR_1_RELPWR_THRESH);
radar_1 |= AR_PHY_RADAR_1_MAX_RRSSI;
radar_1 |= AR_PHY_RADAR_1_BLOCK_CHECK;
radar_1 |= SM(conf->pulse_maxlen, AR_PHY_RADAR_1_MAXLEN);
@@ -1225,7 +1228,7 @@ static void ar5008_hw_set_radar_conf(struct ath_hw *ah)
conf->fir_power = -33;
conf->radar_rssi = 20;
conf->pulse_height = 10;
- conf->pulse_rssi = 24;
+ conf->pulse_rssi = 15;
conf->pulse_inband = 15;
conf->pulse_maxlen = 255;
conf->pulse_inband_step = 12;
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
index cdc74005650c..42190b67c671 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -657,31 +657,29 @@ static void ar9002_hw_olc_temp_compensation(struct ath_hw *ah)
ar9280_hw_olc_temp_compensation(ah);
}
-static bool ar9002_hw_calibrate(struct ath_hw *ah,
- struct ath9k_channel *chan,
- u8 rxchainmask,
- bool longcal)
+static int ar9002_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal)
{
- bool iscaldone = true;
struct ath9k_cal_list *currCal = ah->cal_list_curr;
- bool nfcal, nfcal_pending = false;
+ bool nfcal, nfcal_pending = false, percal_pending;
+ int ret;
nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF);
if (ah->caldata)
nfcal_pending = test_bit(NFCAL_PENDING, &ah->caldata->cal_flags);
- if (currCal && !nfcal &&
- (currCal->calState == CAL_RUNNING ||
- currCal->calState == CAL_WAITING)) {
- iscaldone = ar9002_hw_per_calibration(ah, chan,
- rxchainmask, currCal);
- if (iscaldone) {
- ah->cal_list_curr = currCal = currCal->calNext;
-
- if (currCal->calState == CAL_WAITING) {
- iscaldone = false;
- ath9k_hw_reset_calibration(ah, currCal);
- }
+ percal_pending = (currCal &&
+ (currCal->calState == CAL_RUNNING ||
+ currCal->calState == CAL_WAITING));
+
+ if (percal_pending && !nfcal) {
+ if (!ar9002_hw_per_calibration(ah, chan, rxchainmask, currCal))
+ return 0;
+
+ ah->cal_list_curr = currCal = currCal->calNext;
+ if (currCal->calState == CAL_WAITING) {
+ ath9k_hw_reset_calibration(ah, currCal);
+ return 0;
}
}
@@ -698,7 +696,9 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
* NF is slow time-variant, so it is OK to use a
* historical value.
*/
- ath9k_hw_loadnf(ah, ah->curchan);
+ ret = ath9k_hw_loadnf(ah, ah->curchan);
+ if (ret < 0)
+ return ret;
}
if (longcal) {
@@ -709,7 +709,7 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
}
}
- return iscaldone;
+ return !percal_pending;
}
/* Carrier leakage Calibration fix */
@@ -856,6 +856,8 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
/* Do PA Calibration */
ar9002_hw_pa_cal(ah, true);
+ ath9k_hw_loadnf(ah, chan);
+ ath9k_hw_start_nfcal(ah, true);
if (ah->caldata)
set_bit(NFCAL_PENDING, &ah->caldata->cal_flags);
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
index 2a93519f4bdf..f816909d9474 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
@@ -281,7 +281,7 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen)
| (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
- | SM(i->txpower, AR_XmitPower0)
+ | SM(i->txpower[0], AR_XmitPower0)
| (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
| (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
| (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
@@ -307,9 +307,9 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
| set11nRateFlags(i->rates, 3)
| SM(i->rtscts_rate, AR_RTSCTSRate);
- ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower, AR_XmitPower1);
- ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower, AR_XmitPower2);
- ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower, AR_XmitPower3);
+ ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower[1], AR_XmitPower1);
+ ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower[2], AR_XmitPower2);
+ ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower[3], AR_XmitPower3);
}
static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
index 9a2afa2c690b..fc08162b5820 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
@@ -643,9 +643,12 @@ static void ar9002_hw_spectral_scan_config(struct ath_hw *ah,
* and fix otherwise.
*/
count = param->count;
- if (param->endless)
- count = 0x80;
- else if (count & 0x80)
+ if (param->endless) {
+ if (AR_SREV_9271(ah))
+ count = 0;
+ else
+ count = 0x80;
+ } else if (count & 0x80)
count = 0x7f;
REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
index ac8301ef5242..06ab71db6e80 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
@@ -121,13 +121,12 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
return iscaldone;
}
-static bool ar9003_hw_calibrate(struct ath_hw *ah,
- struct ath9k_channel *chan,
- u8 rxchainmask,
- bool longcal)
+static int ar9003_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal)
{
bool iscaldone = true;
struct ath9k_cal_list *currCal = ah->cal_list_curr;
+ int ret;
/*
* For given calibration:
@@ -163,7 +162,9 @@ static bool ar9003_hw_calibrate(struct ath_hw *ah,
* NF is slow time-variant, so it is OK to use a historical
* value.
*/
- ath9k_hw_loadnf(ah, ah->curchan);
+ ret = ath9k_hw_loadnf(ah, ah->curchan);
+ if (ret < 0)
+ return ret;
/* start NF calibration, without updating BB NF register */
ath9k_hw_start_nfcal(ah, false);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 80c6eacbda53..08225a0067c2 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -4079,27 +4079,28 @@ static int ar9003_hw_get_thermometer(struct ath_hw *ah)
static void ar9003_hw_thermometer_apply(struct ath_hw *ah)
{
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
int thermometer = ar9003_hw_get_thermometer(ah);
u8 therm_on = (thermometer < 0) ? 0 : 1;
REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR, therm_on);
- if (ah->caps.tx_chainmask & BIT(1))
+ if (pCap->chip_chainmask & BIT(1))
REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR, therm_on);
- if (ah->caps.tx_chainmask & BIT(2))
+ if (pCap->chip_chainmask & BIT(2))
REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR, therm_on);
therm_on = (thermometer < 0) ? 0 : (thermometer == 0);
REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON, therm_on);
- if (ah->caps.tx_chainmask & BIT(1)) {
+ if (pCap->chip_chainmask & BIT(1)) {
therm_on = (thermometer < 0) ? 0 : (thermometer == 1);
REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON, therm_on);
}
- if (ah->caps.tx_chainmask & BIT(2)) {
+ if (pCap->chip_chainmask & BIT(2)) {
therm_on = (thermometer < 0) ? 0 : (thermometer == 2);
REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON, therm_on);
@@ -4376,6 +4377,25 @@ static u8 ar9003_hw_eeprom_get_cck_tgt_pwr(struct ath_hw *ah,
targetPowerArray, numPiers);
}
+static void ar9003_hw_selfgen_tpc_txpower(struct ath_hw *ah,
+ struct ath9k_channel *chan,
+ u8 *pwr_array)
+{
+ u32 val;
+
+ /* target power values for self generated frames (ACK,RTS/CTS) */
+ if (IS_CHAN_2GHZ(chan)) {
+ val = SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_ACK) |
+ SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_CTS) |
+ SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+ } else {
+ val = SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_ACK) |
+ SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_CTS) |
+ SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+ }
+ REG_WRITE(ah, AR_TPC, val);
+}
+
/* Set tx power registers to array of values passed in */
static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray)
{
@@ -5311,6 +5331,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
struct ar9300_modal_eep_header *modal_hdr;
u8 targetPowerValT2[ar9300RateSize];
u8 target_power_val_t2_eep[ar9300RateSize];
+ u8 targetPowerValT2_tpc[ar9300RateSize];
unsigned int i = 0, paprd_scale_factor = 0;
u8 pwr_idx, min_pwridx = 0;
@@ -5362,6 +5383,9 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
twiceAntennaReduction,
powerLimit);
+ memcpy(targetPowerValT2_tpc, targetPowerValT2,
+ sizeof(targetPowerValT2));
+
if (ar9003_is_paprd_enabled(ah)) {
for (i = 0; i < ar9300RateSize; i++) {
if ((ah->paprd_ratemask & (1 << i)) &&
@@ -5395,6 +5419,30 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
ar9003_hw_calibration_apply(ah, chan->channel);
ar9003_paprd_set_txpower(ah, chan, targetPowerValT2);
+
+ ar9003_hw_selfgen_tpc_txpower(ah, chan, targetPowerValT2);
+
+ /* TPC initializations */
+ if (ah->tpc_enabled) {
+ u32 val;
+
+ ar9003_hw_init_rate_txpower(ah, targetPowerValT2_tpc, chan);
+
+ /* Enable TPC */
+ REG_WRITE(ah, AR_PHY_PWRTX_MAX,
+ AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+ /* Disable per chain power reduction */
+ val = REG_READ(ah, AR_PHY_POWER_TX_SUB);
+ if (AR_SREV_9340(ah))
+ REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+ val & 0xFFFFFFC0);
+ else
+ REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+ val & 0xFFFFF000);
+ } else {
+ /* Disable TPC */
+ REG_WRITE(ah, AR_PHY_PWRTX_MAX, 0);
+ }
}
static u16 ath9k_hw_ar9300_get_spur_channel(struct ath_hw *ah,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index ddef9eedbac6..06ad2172030e 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -333,12 +333,29 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
qca953x_1p0_soc_preamble);
INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
qca953x_1p0_soc_postamble);
- INIT_INI_ARRAY(&ah->iniModesRxGain,
- qca953x_1p0_common_wo_xlna_rx_gain_table);
- INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
- qca953x_1p0_common_wo_xlna_rx_gain_bounds);
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- qca953x_1p0_modes_no_xpa_tx_gain_table);
+
+ if (AR_SREV_9531_20(ah)) {
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ qca953x_2p0_common_wo_xlna_rx_gain_table);
+ INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+ qca953x_2p0_common_wo_xlna_rx_gain_bounds);
+ } else {
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ qca953x_1p0_common_wo_xlna_rx_gain_table);
+ INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+ qca953x_1p0_common_wo_xlna_rx_gain_bounds);
+ }
+
+ if (AR_SREV_9531_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_2p0_modes_no_xpa_tx_gain_table);
+ else if (AR_SREV_9531_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_1p1_modes_no_xpa_tx_gain_table);
+ else
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_1p0_modes_no_xpa_tx_gain_table);
+
INIT_INI_ARRAY(&ah->iniModesFastClock,
qca953x_1p0_modes_fast_clock);
} else if (AR_SREV_9580(ah)) {
@@ -518,9 +535,15 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah)
else if (AR_SREV_9550(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar955x_1p0_modes_xpa_tx_gain_table);
- else if (AR_SREV_9531(ah))
+ else if (AR_SREV_9531_10(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_1p0_modes_xpa_tx_gain_table);
+ else if (AR_SREV_9531_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_1p1_modes_xpa_tx_gain_table);
+ else if (AR_SREV_9531_20(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
- qca953x_1p0_modes_xpa_tx_gain_table);
+ qca953x_2p0_modes_xpa_tx_gain_table);
else if (AR_SREV_9580(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9580_1p0_lowest_ob_db_tx_gain_table);
@@ -562,7 +585,10 @@ static void ar9003_tx_gain_table_mode1(struct ath_hw *ah)
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar955x_1p0_modes_no_xpa_tx_gain_table);
else if (AR_SREV_9531(ah)) {
- if (AR_SREV_9531_11(ah))
+ if (AR_SREV_9531_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_2p0_modes_no_xpa_tx_gain_table);
+ else if (AR_SREV_9531_11(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
qca953x_1p1_modes_no_xpa_tx_gain_table);
else
@@ -670,9 +696,6 @@ static void ar9003_tx_gain_table_mode5(struct ath_hw *ah)
if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485Modes_green_ob_db_tx_gain_1_1);
- else if (AR_SREV_9340(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9340Modes_ub124_tx_gain_table_1p0);
else if (AR_SREV_9580(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9580_1p0_type5_tx_gain_table);
@@ -792,11 +815,16 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah)
ar955x_1p0_common_wo_xlna_rx_gain_table);
INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
ar955x_1p0_common_wo_xlna_rx_gain_bounds);
- } else if (AR_SREV_9531(ah)) {
+ } else if (AR_SREV_9531_10(ah) || AR_SREV_9531_11(ah)) {
INIT_INI_ARRAY(&ah->iniModesRxGain,
qca953x_1p0_common_wo_xlna_rx_gain_table);
INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
qca953x_1p0_common_wo_xlna_rx_gain_bounds);
+ } else if (AR_SREV_9531_20(ah)) {
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ qca953x_2p0_common_wo_xlna_rx_gain_table);
+ INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+ qca953x_2p0_common_wo_xlna_rx_gain_bounds);
} else if (AR_SREV_9580(ah))
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9580_1p0_wo_xlna_rx_gain_table);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index 057b1657c428..da84b705cbcd 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -101,7 +101,7 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen)
| (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
- | SM(i->txpower, AR_XmitPower0)
+ | SM(i->txpower[0], AR_XmitPower0)
| (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
| (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
| (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0)
@@ -152,9 +152,9 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
- ACCESS_ONCE(ads->ctl20) = SM(i->txpower, AR_XmitPower1);
- ACCESS_ONCE(ads->ctl21) = SM(i->txpower, AR_XmitPower2);
- ACCESS_ONCE(ads->ctl22) = SM(i->txpower, AR_XmitPower3);
+ ACCESS_ONCE(ads->ctl20) = SM(i->txpower[1], AR_XmitPower1);
+ ACCESS_ONCE(ads->ctl21) = SM(i->txpower[2], AR_XmitPower2);
+ ACCESS_ONCE(ads->ctl22) = SM(i->txpower[3], AR_XmitPower3);
}
static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 1e8ea5e4d4ca..ae6cde273414 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -18,6 +18,21 @@
#include "hw.h"
#include "ar9003_phy.h"
+#define AR9300_OFDM_RATES 8
+#define AR9300_HT_SS_RATES 8
+#define AR9300_HT_DS_RATES 8
+#define AR9300_HT_TS_RATES 8
+
+#define AR9300_11NA_OFDM_SHIFT 0
+#define AR9300_11NA_HT_SS_SHIFT 8
+#define AR9300_11NA_HT_DS_SHIFT 16
+#define AR9300_11NA_HT_TS_SHIFT 24
+
+#define AR9300_11NG_OFDM_SHIFT 4
+#define AR9300_11NG_HT_SS_SHIFT 12
+#define AR9300_11NG_HT_DS_SHIFT 20
+#define AR9300_11NG_HT_TS_SHIFT 28
+
static const int firstep_table[] =
/* level: 0 1 2 3 4 5 6 7 8 */
{ -4, -2, 0, 2, 4, 6, 8, 10, 12 }; /* lvl 0-8, default 2 */
@@ -40,6 +55,71 @@ static const int m2ThreshLowExt_off = 127;
static const int m1ThreshExt_off = 127;
static const int m2ThreshExt_off = 127;
+static const u8 ofdm2pwr[] = {
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_36,
+ ALL_TARGET_LEGACY_48,
+ ALL_TARGET_LEGACY_54
+};
+
+static const u8 mcs2pwr_ht20[] = {
+ ALL_TARGET_HT20_0_8_16,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_4,
+ ALL_TARGET_HT20_5,
+ ALL_TARGET_HT20_6,
+ ALL_TARGET_HT20_7,
+ ALL_TARGET_HT20_0_8_16,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_12,
+ ALL_TARGET_HT20_13,
+ ALL_TARGET_HT20_14,
+ ALL_TARGET_HT20_15,
+ ALL_TARGET_HT20_0_8_16,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_20,
+ ALL_TARGET_HT20_21,
+ ALL_TARGET_HT20_22,
+ ALL_TARGET_HT20_23
+};
+
+static const u8 mcs2pwr_ht40[] = {
+ ALL_TARGET_HT40_0_8_16,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_4,
+ ALL_TARGET_HT40_5,
+ ALL_TARGET_HT40_6,
+ ALL_TARGET_HT40_7,
+ ALL_TARGET_HT40_0_8_16,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_12,
+ ALL_TARGET_HT40_13,
+ ALL_TARGET_HT40_14,
+ ALL_TARGET_HT40_15,
+ ALL_TARGET_HT40_0_8_16,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_20,
+ ALL_TARGET_HT40_21,
+ ALL_TARGET_HT40_22,
+ ALL_TARGET_HT40_23,
+};
+
/**
* ar9003_hw_set_channel - set channel on single-chip device
* @ah: atheros hardware structure
@@ -1361,7 +1441,7 @@ static void ar9003_hw_set_radar_params(struct ath_hw *ah,
struct ath_hw_radar_conf *conf)
{
unsigned int regWrites = 0;
- u32 radar_0 = 0, radar_1 = 0;
+ u32 radar_0 = 0, radar_1;
if (!conf) {
REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
@@ -1375,6 +1455,9 @@ static void ar9003_hw_set_radar_params(struct ath_hw *ah,
radar_0 |= SM(conf->pulse_rssi, AR_PHY_RADAR_0_PRSSI);
radar_0 |= SM(conf->pulse_inband, AR_PHY_RADAR_0_INBAND);
+ radar_1 = REG_READ(ah, AR_PHY_RADAR_1);
+ radar_1 &= ~(AR_PHY_RADAR_1_MAXLEN | AR_PHY_RADAR_1_RELSTEP_THRESH |
+ AR_PHY_RADAR_1_RELPWR_THRESH);
radar_1 |= AR_PHY_RADAR_1_MAX_RRSSI;
radar_1 |= AR_PHY_RADAR_1_BLOCK_CHECK;
radar_1 |= SM(conf->pulse_maxlen, AR_PHY_RADAR_1_MAXLEN);
@@ -1401,7 +1484,7 @@ static void ar9003_hw_set_radar_conf(struct ath_hw *ah)
conf->fir_power = -28;
conf->radar_rssi = 0;
conf->pulse_height = 10;
- conf->pulse_rssi = 24;
+ conf->pulse_rssi = 15;
conf->pulse_inband = 8;
conf->pulse_maxlen = 255;
conf->pulse_inband_step = 12;
@@ -1796,6 +1879,100 @@ static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower)
ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14], 0));
}
+static void ar9003_hw_init_txpower_cck(struct ath_hw *ah, u8 *rate_array)
+{
+ ah->tx_power[0] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+ ah->tx_power[1] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+ ah->tx_power[2] = min(rate_array[ALL_TARGET_LEGACY_1L_5L],
+ rate_array[ALL_TARGET_LEGACY_5S]);
+ ah->tx_power[3] = min(rate_array[ALL_TARGET_LEGACY_11L],
+ rate_array[ALL_TARGET_LEGACY_11S]);
+}
+
+static void ar9003_hw_init_txpower_ofdm(struct ath_hw *ah, u8 *rate_array,
+ int offset)
+{
+ int i, j;
+
+ for (i = offset; i < offset + AR9300_OFDM_RATES; i++) {
+ /* OFDM rate to power table idx */
+ j = ofdm2pwr[i - offset];
+ ah->tx_power[i] = rate_array[j];
+ }
+}
+
+static void ar9003_hw_init_txpower_ht(struct ath_hw *ah, u8 *rate_array,
+ int ss_offset, int ds_offset,
+ int ts_offset, bool is_40)
+{
+ int i, j, mcs_idx = 0;
+ const u8 *mcs2pwr = (is_40) ? mcs2pwr_ht40 : mcs2pwr_ht20;
+
+ for (i = ss_offset; i < ss_offset + AR9300_HT_SS_RATES; i++) {
+ j = mcs2pwr[mcs_idx];
+ ah->tx_power[i] = rate_array[j];
+ mcs_idx++;
+ }
+
+ for (i = ds_offset; i < ds_offset + AR9300_HT_DS_RATES; i++) {
+ j = mcs2pwr[mcs_idx];
+ ah->tx_power[i] = rate_array[j];
+ mcs_idx++;
+ }
+
+ for (i = ts_offset; i < ts_offset + AR9300_HT_TS_RATES; i++) {
+ j = mcs2pwr[mcs_idx];
+ ah->tx_power[i] = rate_array[j];
+ mcs_idx++;
+ }
+}
+
+static void ar9003_hw_init_txpower_stbc(struct ath_hw *ah, int ss_offset,
+ int ds_offset, int ts_offset)
+{
+ memcpy(&ah->tx_power_stbc[ss_offset], &ah->tx_power[ss_offset],
+ AR9300_HT_SS_RATES);
+ memcpy(&ah->tx_power_stbc[ds_offset], &ah->tx_power[ds_offset],
+ AR9300_HT_DS_RATES);
+ memcpy(&ah->tx_power_stbc[ts_offset], &ah->tx_power[ts_offset],
+ AR9300_HT_TS_RATES);
+}
+
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+ struct ath9k_channel *chan)
+{
+ if (IS_CHAN_5GHZ(chan)) {
+ ar9003_hw_init_txpower_ofdm(ah, rate_array,
+ AR9300_11NA_OFDM_SHIFT);
+ if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+ ar9003_hw_init_txpower_ht(ah, rate_array,
+ AR9300_11NA_HT_SS_SHIFT,
+ AR9300_11NA_HT_DS_SHIFT,
+ AR9300_11NA_HT_TS_SHIFT,
+ IS_CHAN_HT40(chan));
+ ar9003_hw_init_txpower_stbc(ah,
+ AR9300_11NA_HT_SS_SHIFT,
+ AR9300_11NA_HT_DS_SHIFT,
+ AR9300_11NA_HT_TS_SHIFT);
+ }
+ } else {
+ ar9003_hw_init_txpower_cck(ah, rate_array);
+ ar9003_hw_init_txpower_ofdm(ah, rate_array,
+ AR9300_11NG_OFDM_SHIFT);
+ if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+ ar9003_hw_init_txpower_ht(ah, rate_array,
+ AR9300_11NG_HT_SS_SHIFT,
+ AR9300_11NG_HT_DS_SHIFT,
+ AR9300_11NG_HT_TS_SHIFT,
+ IS_CHAN_HT40(chan));
+ ar9003_hw_init_txpower_stbc(ah,
+ AR9300_11NG_HT_SS_SHIFT,
+ AR9300_11NG_HT_DS_SHIFT,
+ AR9300_11NG_HT_TS_SHIFT);
+ }
+ }
+}
+
void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
{
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.h b/drivers/net/wireless/ath/ath9k/ar9003_rtt.h
index a43b30d723a4..6290467a75a0 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_rtt.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.h
@@ -17,6 +17,7 @@
#ifndef AR9003_RTT_H
#define AR9003_RTT_H
+#ifdef CONFIG_ATH9K_PCOEM
void ar9003_hw_rtt_enable(struct ath_hw *ah);
void ar9003_hw_rtt_disable(struct ath_hw *ah);
void ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask);
@@ -25,5 +26,40 @@ void ar9003_hw_rtt_load_hist(struct ath_hw *ah);
void ar9003_hw_rtt_fill_hist(struct ath_hw *ah);
void ar9003_hw_rtt_clear_hist(struct ath_hw *ah);
bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan);
+#else
+static inline void ar9003_hw_rtt_enable(struct ath_hw *ah)
+{
+}
+
+static inline void ar9003_hw_rtt_disable(struct ath_hw *ah)
+{
+}
+
+static inline void ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask)
+{
+}
+
+static inline bool ar9003_hw_rtt_force_restore(struct ath_hw *ah)
+{
+ return false;
+}
+
+static inline void ar9003_hw_rtt_load_hist(struct ath_hw *ah)
+{
+}
+
+static inline void ar9003_hw_rtt_fill_hist(struct ath_hw *ah)
+{
+}
+
+static inline void ar9003_hw_rtt_clear_hist(struct ath_hw *ah)
+{
+}
+
+static inline bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+ return false;
+}
+#endif
#endif
diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
index 812a9d787bf3..159cc6fd2362 100644
--- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
@@ -20,6 +20,8 @@
#define qca953x_1p0_mac_postamble ar9300_2p2_mac_postamble
+#define qca953x_1p0_soc_preamble ar955x_1p0_soc_preamble
+
#define qca953x_1p0_soc_postamble ar9300_2p2_soc_postamble
#define qca953x_1p0_common_rx_gain_table ar9300Common_rx_gain_table_2p2
@@ -28,6 +30,10 @@
#define qca953x_1p0_modes_fast_clock ar9300Modes_fast_clock_2p2
+#define qca953x_1p0_common_wo_xlna_rx_gain_bounds ar955x_1p0_common_wo_xlna_rx_gain_bounds
+
+#define qca953x_1p0_common_rx_gain_bounds ar955x_1p0_common_rx_gain_bounds
+
static const u32 qca953x_1p0_mac_core[][2] = {
/* Addr allmodes */
{0x00000008, 0x00000000},
@@ -490,35 +496,6 @@ static const u32 qca953x_1p0_radio_postamble[][5] = {
{0x00016540, 0x10804008, 0x10804008, 0x50804000, 0x50804000},
};
-static const u32 qca953x_1p0_soc_preamble[][2] = {
- /* Addr allmodes */
- {0x00007000, 0x00000000},
- {0x00007004, 0x00000000},
- {0x00007008, 0x00000000},
- {0x0000700c, 0x00000000},
- {0x0000701c, 0x00000000},
- {0x00007020, 0x00000000},
- {0x00007024, 0x00000000},
- {0x00007028, 0x00000000},
- {0x0000702c, 0x00000000},
- {0x00007030, 0x00000000},
- {0x00007034, 0x00000002},
- {0x00007038, 0x000004c2},
- {0x00007048, 0x00000000},
-};
-
-static const u32 qca953x_1p0_common_rx_gain_bounds[][5] = {
- /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
- {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
- {0x00009e48, 0x5030201a, 0x5030201a, 0x50302018, 0x50302018},
-};
-
-static const u32 qca953x_1p0_common_wo_xlna_rx_gain_bounds[][5] = {
- /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
- {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
- {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
-};
-
static const u32 qca953x_1p0_modes_xpa_tx_gain_table[][2] = {
/* Addr allmodes */
{0x0000a2dc, 0xfffd5aaa},
@@ -715,8 +692,73 @@ static const u32 qca953x_1p1_modes_no_xpa_tx_gain_table[][2] = {
{0x00016448, 0x6c927a70},
};
+static const u32 qca953x_1p1_modes_xpa_tx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a2dc, 0xfffb52aa},
+ {0x0000a2e0, 0xfffd64cc},
+ {0x0000a2e4, 0xfffe80f0},
+ {0x0000a2e8, 0xffffff00},
+ {0x0000a410, 0x000050d5},
+ {0x0000a500, 0x00000000},
+ {0x0000a504, 0x04000002},
+ {0x0000a508, 0x08000004},
+ {0x0000a50c, 0x0c000006},
+ {0x0000a510, 0x1000000a},
+ {0x0000a514, 0x1400000c},
+ {0x0000a518, 0x1800000e},
+ {0x0000a51c, 0x1c000048},
+ {0x0000a520, 0x2000004a},
+ {0x0000a524, 0x2400004c},
+ {0x0000a528, 0x2800004e},
+ {0x0000a52c, 0x2b00024a},
+ {0x0000a530, 0x2f00024c},
+ {0x0000a534, 0x3300024e},
+ {0x0000a538, 0x36000668},
+ {0x0000a53c, 0x38000669},
+ {0x0000a540, 0x3a000868},
+ {0x0000a544, 0x3d00086a},
+ {0x0000a548, 0x4000086c},
+ {0x0000a54c, 0x4200086e},
+ {0x0000a550, 0x43000a6e},
+ {0x0000a554, 0x43000a6e},
+ {0x0000a558, 0x43000a6e},
+ {0x0000a55c, 0x43000a6e},
+ {0x0000a560, 0x43000a6e},
+ {0x0000a564, 0x43000a6e},
+ {0x0000a568, 0x43000a6e},
+ {0x0000a56c, 0x43000a6e},
+ {0x0000a570, 0x43000a6e},
+ {0x0000a574, 0x43000a6e},
+ {0x0000a578, 0x43000a6e},
+ {0x0000a57c, 0x43000a6e},
+ {0x0000a600, 0x00000000},
+ {0x0000a604, 0x00000000},
+ {0x0000a608, 0x00000000},
+ {0x0000a60c, 0x03804000},
+ {0x0000a610, 0x03804e01},
+ {0x0000a614, 0x03804e01},
+ {0x0000a618, 0x03804e01},
+ {0x0000a61c, 0x04009002},
+ {0x0000a620, 0x04009002},
+ {0x0000a624, 0x04009002},
+ {0x0000a628, 0x04009002},
+ {0x0000a62c, 0x04009002},
+ {0x0000a630, 0x04009002},
+ {0x0000a634, 0x04009002},
+ {0x0000a638, 0x04009002},
+ {0x0000a63c, 0x04009002},
+ {0x0000b2dc, 0xfffb52aa},
+ {0x0000b2e0, 0xfffd64cc},
+ {0x0000b2e4, 0xfffe80f0},
+ {0x0000b2e8, 0xffffff00},
+ {0x00016044, 0x024922db},
+ {0x00016048, 0x6c927a70},
+ {0x00016444, 0x024922db},
+ {0x00016448, 0x6c927a70},
+};
+
static const u32 qca953x_2p0_baseband_core[][2] = {
- /* Addr allmodes */
+ /* Addr allmodes */
{0x00009800, 0xafe68e30},
{0x00009804, 0xfd14e000},
{0x00009808, 0x9c0a9f6b},
@@ -914,4 +956,400 @@ static const u32 qca953x_2p0_baseband_postamble[][5] = {
{0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
};
+static const u32 qca953x_2p0_common_wo_xlna_rx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x03820190},
+ {0x0000a030, 0x03840383},
+ {0x0000a034, 0x03880385},
+ {0x0000a038, 0x038a0389},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x29292929},
+ {0x0000a084, 0x29292929},
+ {0x0000a088, 0x29292929},
+ {0x0000a08c, 0x29292929},
+ {0x0000a090, 0x22292929},
+ {0x0000a094, 0x1d1d2222},
+ {0x0000a098, 0x0c111117},
+ {0x0000a09c, 0x00030303},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x32323232},
+ {0x0000b084, 0x2f2f3232},
+ {0x0000b088, 0x23282a2d},
+ {0x0000b08c, 0x1c1e2123},
+ {0x0000b090, 0x14171919},
+ {0x0000b094, 0x0e0e1214},
+ {0x0000b098, 0x03050707},
+ {0x0000b09c, 0x00030303},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 qca953x_2p0_common_wo_xlna_rx_gain_bounds[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
+ {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+};
+
+static const u32 qca953x_2p0_modes_xpa_tx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a2dc, 0xfffb52aa},
+ {0x0000a2e0, 0xfffd64cc},
+ {0x0000a2e4, 0xfffe80f0},
+ {0x0000a2e8, 0xffffff00},
+ {0x0000a410, 0x000050d5},
+ {0x0000a500, 0x00000000},
+ {0x0000a504, 0x04000002},
+ {0x0000a508, 0x08000004},
+ {0x0000a50c, 0x0c000006},
+ {0x0000a510, 0x1000000a},
+ {0x0000a514, 0x1400000c},
+ {0x0000a518, 0x1800000e},
+ {0x0000a51c, 0x1c000048},
+ {0x0000a520, 0x2000004a},
+ {0x0000a524, 0x2400004c},
+ {0x0000a528, 0x2800004e},
+ {0x0000a52c, 0x2b00024a},
+ {0x0000a530, 0x2f00024c},
+ {0x0000a534, 0x3300024e},
+ {0x0000a538, 0x36000668},
+ {0x0000a53c, 0x38000669},
+ {0x0000a540, 0x3a000868},
+ {0x0000a544, 0x3d00086a},
+ {0x0000a548, 0x4000086c},
+ {0x0000a54c, 0x4200086e},
+ {0x0000a550, 0x43000a6e},
+ {0x0000a554, 0x43000a6e},
+ {0x0000a558, 0x43000a6e},
+ {0x0000a55c, 0x43000a6e},
+ {0x0000a560, 0x43000a6e},
+ {0x0000a564, 0x43000a6e},
+ {0x0000a568, 0x43000a6e},
+ {0x0000a56c, 0x43000a6e},
+ {0x0000a570, 0x43000a6e},
+ {0x0000a574, 0x43000a6e},
+ {0x0000a578, 0x43000a6e},
+ {0x0000a57c, 0x43000a6e},
+ {0x0000a600, 0x00000000},
+ {0x0000a604, 0x00000000},
+ {0x0000a608, 0x00000000},
+ {0x0000a60c, 0x03804000},
+ {0x0000a610, 0x03804e01},
+ {0x0000a614, 0x03804e01},
+ {0x0000a618, 0x03804e01},
+ {0x0000a61c, 0x04009002},
+ {0x0000a620, 0x04009002},
+ {0x0000a624, 0x04009002},
+ {0x0000a628, 0x04009002},
+ {0x0000a62c, 0x04009002},
+ {0x0000a630, 0x04009002},
+ {0x0000a634, 0x04009002},
+ {0x0000a638, 0x04009002},
+ {0x0000a63c, 0x04009002},
+ {0x0000b2dc, 0xfffb52aa},
+ {0x0000b2e0, 0xfffd64cc},
+ {0x0000b2e4, 0xfffe80f0},
+ {0x0000b2e8, 0xffffff00},
+ {0x00016044, 0x024922db},
+ {0x00016048, 0x6c927a70},
+ {0x00016444, 0x024922db},
+ {0x00016448, 0x6c927a70},
+};
+
+static const u32 qca953x_2p0_modes_no_xpa_tx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a2dc, 0xffd5f552},
+ {0x0000a2e0, 0xffe60664},
+ {0x0000a2e4, 0xfff80780},
+ {0x0000a2e8, 0xfffff800},
+ {0x0000a410, 0x000050de},
+ {0x0000a500, 0x00000061},
+ {0x0000a504, 0x04000063},
+ {0x0000a508, 0x08000065},
+ {0x0000a50c, 0x0c000261},
+ {0x0000a510, 0x10000263},
+ {0x0000a514, 0x14000265},
+ {0x0000a518, 0x18000482},
+ {0x0000a51c, 0x1b000484},
+ {0x0000a520, 0x1f000486},
+ {0x0000a524, 0x240008c2},
+ {0x0000a528, 0x28000cc1},
+ {0x0000a52c, 0x2d000ce3},
+ {0x0000a530, 0x31000ce5},
+ {0x0000a534, 0x350010e5},
+ {0x0000a538, 0x360012e5},
+ {0x0000a53c, 0x380014e5},
+ {0x0000a540, 0x3b0018e5},
+ {0x0000a544, 0x3d001d04},
+ {0x0000a548, 0x3e001d05},
+ {0x0000a54c, 0x40001d07},
+ {0x0000a550, 0x42001f27},
+ {0x0000a554, 0x43001f67},
+ {0x0000a558, 0x46001fe7},
+ {0x0000a55c, 0x47001f2b},
+ {0x0000a560, 0x49001f0d},
+ {0x0000a564, 0x4b001ed2},
+ {0x0000a568, 0x4c001ed4},
+ {0x0000a56c, 0x4e001f15},
+ {0x0000a570, 0x4f001ff6},
+ {0x0000a574, 0x4f001ff6},
+ {0x0000a578, 0x4f001ff6},
+ {0x0000a57c, 0x4f001ff6},
+ {0x0000a600, 0x00000000},
+ {0x0000a604, 0x00000000},
+ {0x0000a608, 0x00000000},
+ {0x0000a60c, 0x00804201},
+ {0x0000a610, 0x01008201},
+ {0x0000a614, 0x0180c402},
+ {0x0000a618, 0x0180c603},
+ {0x0000a61c, 0x0180c603},
+ {0x0000a620, 0x01c10603},
+ {0x0000a624, 0x01c10704},
+ {0x0000a628, 0x02c18b05},
+ {0x0000a62c, 0x02c14c07},
+ {0x0000a630, 0x01008704},
+ {0x0000a634, 0x01c10402},
+ {0x0000a638, 0x0301cc07},
+ {0x0000a63c, 0x0301cc07},
+ {0x0000b2dc, 0xffd5f552},
+ {0x0000b2e0, 0xffe60664},
+ {0x0000b2e4, 0xfff80780},
+ {0x0000b2e8, 0xfffff800},
+ {0x00016044, 0x049242db},
+ {0x00016048, 0x6c927a70},
+ {0x00016444, 0x049242db},
+ {0x00016448, 0x6c927a70},
+};
+
#endif /* INITVALS_953X_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
index 74d8bc05b317..fd6a84ccd49e 100644
--- a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
@@ -507,7 +507,7 @@ static const u32 ar955x_1p0_baseband_core[][2] = {
{0x00009d04, 0x40206c10},
{0x00009d08, 0x009c4060},
{0x00009d0c, 0x9883800a},
- {0x00009d10, 0x01834061},
+ {0x00009d10, 0x01884061},
{0x00009d14, 0x00c0040b},
{0x00009d18, 0x00000000},
{0x00009e08, 0x0038230c},
@@ -545,9 +545,9 @@ static const u32 ar955x_1p0_baseband_core[][2] = {
{0x0000a370, 0x00000000},
{0x0000a390, 0x00000001},
{0x0000a394, 0x00000444},
- {0x0000a398, 0x1f020503},
- {0x0000a39c, 0x29180c03},
- {0x0000a3a0, 0x9a8b6844},
+ {0x0000a398, 0x001f0e0f},
+ {0x0000a39c, 0x0075393f},
+ {0x0000a3a0, 0xb79f6427},
{0x0000a3a4, 0x00000000},
{0x0000a3a8, 0xaaaaaaaa},
{0x0000a3ac, 0x3c466478},
diff --git a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
index a5ca65240af3..5d4629f96c15 100644
--- a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
@@ -24,7 +24,149 @@
#define ar9580_1p0_soc_postamble ar9300_2p2_soc_postamble
-#define ar9580_1p0_radio_core ar9300_2p2_radio_core
+static const u32 ar9580_1p0_radio_core[][2] = {
+ /* Addr allmodes */
+ {0x00016000, 0x36db2db6},
+ {0x00016004, 0x6db6db40},
+ {0x00016008, 0x73f00000},
+ {0x0001600c, 0x00000000},
+ {0x00016040, 0x7f80fff8},
+ {0x0001604c, 0x76d005b5},
+ {0x00016050, 0x556cf031},
+ {0x00016054, 0x13449440},
+ {0x00016058, 0x0c51c92c},
+ {0x0001605c, 0x3db7fffc},
+ {0x00016060, 0xfffffffc},
+ {0x00016064, 0x000f0278},
+ {0x0001606c, 0x6db60000},
+ {0x00016080, 0x00000000},
+ {0x00016084, 0x0e48048c},
+ {0x00016088, 0x54214514},
+ {0x0001608c, 0x119f481e},
+ {0x00016090, 0x24926490},
+ {0x00016098, 0xd2888888},
+ {0x000160a0, 0x0a108ffe},
+ {0x000160a4, 0x812fc370},
+ {0x000160a8, 0x423c8000},
+ {0x000160b4, 0x92480080},
+ {0x000160c0, 0x00adb6d0},
+ {0x000160c4, 0x6db6db60},
+ {0x000160c8, 0x6db6db6c},
+ {0x000160cc, 0x01e6c000},
+ {0x00016100, 0x3fffbe01},
+ {0x00016104, 0xfff80000},
+ {0x00016108, 0x00080010},
+ {0x00016144, 0x02084080},
+ {0x00016148, 0x00000000},
+ {0x00016280, 0x058a0001},
+ {0x00016284, 0x3d840208},
+ {0x00016288, 0x05a20408},
+ {0x0001628c, 0x00038c07},
+ {0x00016290, 0x00000004},
+ {0x00016294, 0x458a214f},
+ {0x00016380, 0x00000000},
+ {0x00016384, 0x00000000},
+ {0x00016388, 0x00800700},
+ {0x0001638c, 0x00800700},
+ {0x00016390, 0x00800700},
+ {0x00016394, 0x00000000},
+ {0x00016398, 0x00000000},
+ {0x0001639c, 0x00000000},
+ {0x000163a0, 0x00000001},
+ {0x000163a4, 0x00000001},
+ {0x000163a8, 0x00000000},
+ {0x000163ac, 0x00000000},
+ {0x000163b0, 0x00000000},
+ {0x000163b4, 0x00000000},
+ {0x000163b8, 0x00000000},
+ {0x000163bc, 0x00000000},
+ {0x000163c0, 0x000000a0},
+ {0x000163c4, 0x000c0000},
+ {0x000163c8, 0x14021402},
+ {0x000163cc, 0x00001402},
+ {0x000163d0, 0x00000000},
+ {0x000163d4, 0x00000000},
+ {0x00016400, 0x36db2db6},
+ {0x00016404, 0x6db6db40},
+ {0x00016408, 0x73f00000},
+ {0x0001640c, 0x00000000},
+ {0x00016440, 0x7f80fff8},
+ {0x0001644c, 0x76d005b5},
+ {0x00016450, 0x556cf031},
+ {0x00016454, 0x13449440},
+ {0x00016458, 0x0c51c92c},
+ {0x0001645c, 0x3db7fffc},
+ {0x00016460, 0xfffffffc},
+ {0x00016464, 0x000f0278},
+ {0x0001646c, 0x6db60000},
+ {0x00016500, 0x3fffbe01},
+ {0x00016504, 0xfff80000},
+ {0x00016508, 0x00080010},
+ {0x00016544, 0x02084080},
+ {0x00016548, 0x00000000},
+ {0x00016780, 0x00000000},
+ {0x00016784, 0x00000000},
+ {0x00016788, 0x00800700},
+ {0x0001678c, 0x00800700},
+ {0x00016790, 0x00800700},
+ {0x00016794, 0x00000000},
+ {0x00016798, 0x00000000},
+ {0x0001679c, 0x00000000},
+ {0x000167a0, 0x00000001},
+ {0x000167a4, 0x00000001},
+ {0x000167a8, 0x00000000},
+ {0x000167ac, 0x00000000},
+ {0x000167b0, 0x00000000},
+ {0x000167b4, 0x00000000},
+ {0x000167b8, 0x00000000},
+ {0x000167bc, 0x00000000},
+ {0x000167c0, 0x000000a0},
+ {0x000167c4, 0x000c0000},
+ {0x000167c8, 0x14021402},
+ {0x000167cc, 0x00001402},
+ {0x000167d0, 0x00000000},
+ {0x000167d4, 0x00000000},
+ {0x00016800, 0x36db2db6},
+ {0x00016804, 0x6db6db40},
+ {0x00016808, 0x73f00000},
+ {0x0001680c, 0x00000000},
+ {0x00016840, 0x7f80fff8},
+ {0x0001684c, 0x76d005b5},
+ {0x00016850, 0x556cf031},
+ {0x00016854, 0x13449440},
+ {0x00016858, 0x0c51c92c},
+ {0x0001685c, 0x3db7fffc},
+ {0x00016860, 0xfffffffc},
+ {0x00016864, 0x000f0278},
+ {0x0001686c, 0x6db60000},
+ {0x00016900, 0x3fffbe01},
+ {0x00016904, 0xfff80000},
+ {0x00016908, 0x00080010},
+ {0x00016944, 0x02084080},
+ {0x00016948, 0x00000000},
+ {0x00016b80, 0x00000000},
+ {0x00016b84, 0x00000000},
+ {0x00016b88, 0x00800700},
+ {0x00016b8c, 0x00800700},
+ {0x00016b90, 0x00800700},
+ {0x00016b94, 0x00000000},
+ {0x00016b98, 0x00000000},
+ {0x00016b9c, 0x00000000},
+ {0x00016ba0, 0x00000001},
+ {0x00016ba4, 0x00000001},
+ {0x00016ba8, 0x00000000},
+ {0x00016bac, 0x00000000},
+ {0x00016bb0, 0x00000000},
+ {0x00016bb4, 0x00000000},
+ {0x00016bb8, 0x00000000},
+ {0x00016bbc, 0x00000000},
+ {0x00016bc0, 0x000000a0},
+ {0x00016bc4, 0x000c0000},
+ {0x00016bc8, 0x14021402},
+ {0x00016bcc, 0x00001402},
+ {0x00016bd0, 0x00000000},
+ {0x00016bd4, 0x00000000},
+};
#define ar9580_1p0_mac_postamble ar9300_2p2_mac_postamble
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 01a7db061c6a..1a9fe0983a6b 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -28,7 +28,6 @@
#include "debug.h"
#include "mci.h"
#include "dfs.h"
-#include "spectral.h"
struct ath_node;
struct ath_vif;
@@ -190,6 +189,7 @@ struct ath_frame_info {
u8 rtscts_rate;
u8 retries : 7;
u8 baw_tracked : 1;
+ u8 tx_power;
};
struct ath_rxbuf {
@@ -345,7 +345,9 @@ struct ath_chanctx {
u64 tsf_val;
u32 last_beacon;
+ int flush_timeout;
u16 txpower;
+ u16 cur_txpower;
bool offchannel;
bool stopped;
bool active;
@@ -362,7 +364,7 @@ enum ath_chanctx_event {
ATH_CHANCTX_EVENT_BEACON_SENT,
ATH_CHANCTX_EVENT_TSF_TIMER,
ATH_CHANCTX_EVENT_BEACON_RECEIVED,
- ATH_CHANCTX_EVENT_ASSOC,
+ ATH_CHANCTX_EVENT_AUTHORIZED,
ATH_CHANCTX_EVENT_SWITCH,
ATH_CHANCTX_EVENT_ASSIGN,
ATH_CHANCTX_EVENT_UNASSIGN,
@@ -380,10 +382,12 @@ enum ath_chanctx_state {
struct ath_chanctx_sched {
bool beacon_pending;
+ bool beacon_adjust;
bool offchannel_pending;
bool wait_switch;
bool force_noa_update;
bool extend_absence;
+ bool mgd_prepare_tx;
enum ath_chanctx_state state;
u8 beacon_miss;
@@ -468,6 +472,7 @@ void ath_chanctx_set_next(struct ath_softc *sc, bool force);
void ath_offchannel_next(struct ath_softc *sc);
void ath_scan_complete(struct ath_softc *sc, bool abort);
void ath_roc_complete(struct ath_softc *sc, bool abort);
+struct ath_chanctx* ath_is_go_chanctx_present(struct ath_softc *sc);
#else
@@ -540,7 +545,6 @@ static inline void ath_chanctx_check_active(struct ath_softc *sc,
#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
-int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
void ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
u32 ath_calcrxfilter(struct ath_softc *sc);
@@ -595,7 +599,7 @@ struct ath_vif {
u16 seq_no;
/* BSS info */
- u8 bssid[ETH_ALEN];
+ u8 bssid[ETH_ALEN] __aligned(2);
u16 aid;
bool assoc;
@@ -618,6 +622,7 @@ struct ath_vif {
u32 noa_start;
u32 noa_duration;
bool periodic_noa;
+ bool oneshot_noa;
};
struct ath9k_vif_iter_data {
@@ -715,7 +720,8 @@ int ath_update_survey_stats(struct ath_softc *sc);
void ath_update_survey_nf(struct ath_softc *sc, int channel);
void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
void ath_ps_full_sleep(unsigned long data);
-void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop,
+ bool sw_pending, bool timeout_override);
/**********/
/* BTCOEX */
@@ -927,6 +933,7 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
#define ATH9K_PCI_AR9565_2ANT 0x0100
#define ATH9K_PCI_NO_PLL_PWRSAVE 0x0200
#define ATH9K_PCI_KILLER 0x0400
+#define ATH9K_PCI_LED_ACT_HI 0x0800
/*
* Default cache line size, in bytes.
@@ -975,6 +982,7 @@ struct ath_softc {
struct ath_chanctx_sched sched;
struct ath_offchannel offchannel;
struct ath_chanctx *next_chan;
+ struct completion go_beacon;
#endif
unsigned long driver_data;
@@ -982,7 +990,6 @@ struct ath_softc {
u8 gtt_cnt;
u32 intrstatus;
u16 ps_flags; /* PS_* */
- u16 curtxpow;
bool ps_enabled;
bool ps_idle;
short nbcnvifs;
@@ -1023,10 +1030,8 @@ struct ath_softc {
struct dfs_pattern_detector *dfs_detector;
u64 dfs_prev_pulse_ts;
u32 wow_enabled;
- /* relay(fs) channel for spectral scan */
- struct rchan *rfs_chan_spec_scan;
- enum spectral_mode spectral_mode;
- struct ath_spec_scan spec_config;
+
+ struct ath_spec_scan_priv spec_priv;
struct ieee80211_vif *tx99_vif;
struct sk_buff *tx99_skb;
@@ -1069,7 +1074,7 @@ void ath9k_tasklet(unsigned long data);
int ath_cabq_update(struct ath_softc *);
u8 ath9k_parse_mpdudensity(u8 mpdudensity);
irqreturn_t ath_isr(int irq, void *dev);
-int ath_reset(struct ath_softc *sc);
+int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan);
void ath_cancel_work(struct ath_softc *sc);
void ath_restart_work(struct ath_softc *sc);
int ath9k_init_device(u16 devid, struct ath_softc *sc,
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index ecb783beeec2..cb366adc820b 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -78,7 +78,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
struct ath_tx_info info;
struct ieee80211_supported_band *sband;
u8 chainmask = ah->txchainmask;
- u8 rate = 0;
+ u8 i, rate = 0;
sband = &common->sbands[sc->cur_chandef.chan->band];
rate = sband->bitrates[rateidx].hw_value;
@@ -88,7 +88,8 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
memset(&info, 0, sizeof(info));
info.pkt_len = skb->len + FCS_LEN;
info.type = ATH9K_PKT_TYPE_BEACON;
- info.txpower = MAX_RATE_POWER;
+ for (i = 0; i < 4; i++)
+ info.txpower[i] = MAX_RATE_POWER;
info.keyix = ATH9K_TXKEYIX_INVALID;
info.keytype = ATH9K_KEY_TYPE_CLEAR;
info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK;
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index 278365b8a895..e200a6e3aca5 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -234,7 +234,7 @@ void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update)
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
}
-void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
+int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
{
struct ath9k_nfcal_hist *h = NULL;
unsigned i, j;
@@ -301,7 +301,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
ath_dbg(common, ANY,
"Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
REG_READ(ah, AR_PHY_AGC_CONTROL));
- return;
+ return -ETIMEDOUT;
}
/*
@@ -322,6 +322,8 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
}
}
REGWRITE_BUFFER_FLUSH(ah);
+
+ return 0;
}
diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h
index b8ed95e9a335..87badf4bb8a4 100644
--- a/drivers/net/wireless/ath/ath9k/calib.h
+++ b/drivers/net/wireless/ath/ath9k/calib.h
@@ -109,7 +109,7 @@ struct ath9k_pacal_info{
bool ath9k_hw_reset_calvalid(struct ath_hw *ah);
void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update);
-void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
+int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan);
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
struct ath9k_channel *chan);
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 945c89826b14..206665059d66 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -66,7 +66,7 @@ static int ath_set_channel(struct ath_softc *sc)
}
hchan = &sc->sc_ah->channels[pos];
- r = ath_reset_internal(sc, hchan);
+ r = ath_reset(sc, hchan);
if (r)
return r;
@@ -92,8 +92,8 @@ static int ath_set_channel(struct ath_softc *sc)
} else {
/* perform spectral scan if requested. */
if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
- sc->spectral_mode == SPECTRAL_CHANSCAN)
- ath9k_spectral_scan_trigger(hw);
+ sc->spec_priv.spectral_mode == SPECTRAL_CHANSCAN)
+ ath9k_cmn_spectral_scan_trigger(common, &sc->spec_priv);
}
return 0;
@@ -117,6 +117,7 @@ void ath_chanctx_init(struct ath_softc *sc)
cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
INIT_LIST_HEAD(&ctx->vifs);
ctx->txpower = ATH_TXPOWER_MAX;
+ ctx->flush_timeout = HZ / 5; /* 200ms */
for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
INIT_LIST_HEAD(&ctx->acq[j]);
}
@@ -145,6 +146,36 @@ void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
+/*************/
+/* Utilities */
+/*************/
+
+struct ath_chanctx* ath_is_go_chanctx_present(struct ath_softc *sc)
+{
+ struct ath_chanctx *ctx;
+ struct ath_vif *avp;
+ struct ieee80211_vif *vif;
+
+ spin_lock_bh(&sc->chan_lock);
+
+ ath_for_each_chanctx(sc, ctx) {
+ if (!ctx->active)
+ continue;
+
+ list_for_each_entry(avp, &ctx->vifs, list) {
+ vif = avp->vif;
+
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO) {
+ spin_unlock_bh(&sc->chan_lock);
+ return ctx;
+ }
+ }
+ }
+
+ spin_unlock_bh(&sc->chan_lock);
+ return NULL;
+}
+
/**********************************************************/
/* Functions to handle the channel context state machine. */
/**********************************************************/
@@ -171,7 +202,7 @@ static const char *chanctx_event_string(enum ath_chanctx_event ev)
case_rtn_string(ATH_CHANCTX_EVENT_BEACON_SENT);
case_rtn_string(ATH_CHANCTX_EVENT_TSF_TIMER);
case_rtn_string(ATH_CHANCTX_EVENT_BEACON_RECEIVED);
- case_rtn_string(ATH_CHANCTX_EVENT_ASSOC);
+ case_rtn_string(ATH_CHANCTX_EVENT_AUTHORIZED);
case_rtn_string(ATH_CHANCTX_EVENT_SWITCH);
case_rtn_string(ATH_CHANCTX_EVENT_ASSIGN);
case_rtn_string(ATH_CHANCTX_EVENT_UNASSIGN);
@@ -198,6 +229,7 @@ static const char *chanctx_state_string(enum ath_chanctx_state state)
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_chanctx *ictx;
struct ath_vif *avp;
bool active = false;
u8 n_active = 0;
@@ -205,6 +237,28 @@ void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
if (!ctx)
return;
+ if (ctx == &sc->offchannel.chan) {
+ spin_lock_bh(&sc->chan_lock);
+
+ if (likely(sc->sched.channel_switch_time))
+ ctx->flush_timeout =
+ usecs_to_jiffies(sc->sched.channel_switch_time);
+ else
+ ctx->flush_timeout =
+ msecs_to_jiffies(10);
+
+ spin_unlock_bh(&sc->chan_lock);
+
+ /*
+ * There is no need to iterate over the
+ * active/assigned channel contexts if
+ * the current context is offchannel.
+ */
+ return;
+ }
+
+ ictx = ctx;
+
list_for_each_entry(avp, &ctx->vifs, list) {
struct ieee80211_vif *vif = avp->vif;
@@ -227,12 +281,23 @@ void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
n_active++;
}
+ spin_lock_bh(&sc->chan_lock);
+
if (n_active <= 1) {
+ ictx->flush_timeout = HZ / 5;
clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags);
+ spin_unlock_bh(&sc->chan_lock);
return;
}
- if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+
+ ictx->flush_timeout = usecs_to_jiffies(sc->sched.channel_switch_time);
+
+ if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
+ spin_unlock_bh(&sc->chan_lock);
return;
+ }
+
+ spin_unlock_bh(&sc->chan_lock);
if (ath9k_is_chanctx_enabled()) {
ath_chanctx_event(sc, NULL,
@@ -301,6 +366,111 @@ static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
"Setup chanctx timer with timeout: %d ms\n", jiffies_to_msecs(tsf_time));
}
+static void ath_chanctx_handle_bmiss(struct ath_softc *sc,
+ struct ath_chanctx *ctx,
+ struct ath_vif *avp)
+{
+ /*
+ * Clear the extend_absence flag if it had been
+ * set during the previous beacon transmission,
+ * since we need to revert to the normal NoA
+ * schedule.
+ */
+ if (ctx->active && sc->sched.extend_absence) {
+ avp->noa_duration = 0;
+ sc->sched.extend_absence = false;
+ }
+
+ /* If at least two consecutive beacons were missed on the STA
+ * chanctx, stay on the STA channel for one extra beacon period,
+ * to resync the timer properly.
+ */
+ if (ctx->active && sc->sched.beacon_miss >= 2) {
+ avp->noa_duration = 0;
+ sc->sched.extend_absence = true;
+ }
+}
+
+static void ath_chanctx_offchannel_noa(struct ath_softc *sc,
+ struct ath_chanctx *ctx,
+ struct ath_vif *avp,
+ u32 tsf_time)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ avp->noa_index++;
+ avp->offchannel_start = tsf_time;
+ avp->offchannel_duration = sc->sched.offchannel_duration;
+
+ ath_dbg(common, CHAN_CTX,
+ "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n",
+ avp->offchannel_duration,
+ avp->offchannel_start,
+ avp->noa_index);
+
+ /*
+ * When multiple contexts are active, the NoA
+ * has to be recalculated and advertised after
+ * an offchannel operation.
+ */
+ if (ctx->active && avp->noa_duration)
+ avp->noa_duration = 0;
+}
+
+static void ath_chanctx_set_periodic_noa(struct ath_softc *sc,
+ struct ath_vif *avp,
+ struct ath_beacon_config *cur_conf,
+ u32 tsf_time,
+ u32 beacon_int)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ avp->noa_index++;
+ avp->noa_start = tsf_time;
+
+ if (sc->sched.extend_absence)
+ avp->noa_duration = (3 * beacon_int / 2) +
+ sc->sched.channel_switch_time;
+ else
+ avp->noa_duration =
+ TU_TO_USEC(cur_conf->beacon_interval) / 2 +
+ sc->sched.channel_switch_time;
+
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags) ||
+ sc->sched.extend_absence)
+ avp->periodic_noa = false;
+ else
+ avp->periodic_noa = true;
+
+ ath_dbg(common, CHAN_CTX,
+ "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
+ avp->noa_duration,
+ avp->noa_start,
+ avp->noa_index,
+ avp->periodic_noa);
+}
+
+static void ath_chanctx_set_oneshot_noa(struct ath_softc *sc,
+ struct ath_vif *avp,
+ u32 tsf_time,
+ u32 duration)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ avp->noa_index++;
+ avp->noa_start = tsf_time;
+ avp->periodic_noa = false;
+ avp->oneshot_noa = true;
+ avp->noa_duration = duration + sc->sched.channel_switch_time;
+
+ ath_dbg(common, CHAN_CTX,
+ "oneshot noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
+ avp->noa_duration,
+ avp->noa_start,
+ avp->noa_index,
+ avp->periodic_noa);
+}
+
void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
enum ath_chanctx_event ev)
{
@@ -327,6 +497,14 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
if (avp->offchannel_duration)
avp->offchannel_duration = 0;
+ if (avp->oneshot_noa) {
+ avp->noa_duration = 0;
+ avp->oneshot_noa = false;
+
+ ath_dbg(common, CHAN_CTX,
+ "Clearing oneshot NoA\n");
+ }
+
if (avp->chanctx != sc->cur_chan) {
ath_dbg(common, CHAN_CTX,
"Contexts differ, not preparing beacon\n");
@@ -356,6 +534,24 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
"Move chanctx state from WAIT_FOR_TIMER to WAIT_FOR_BEACON\n");
}
+ if (sc->sched.mgd_prepare_tx)
+ sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+
+ /*
+ * When a context becomes inactive, for example,
+ * disassociation of a station context, the NoA
+ * attribute needs to be removed from subsequent
+ * beacons.
+ */
+ if (!ctx->active && avp->noa_duration &&
+ sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) {
+ avp->noa_duration = 0;
+ avp->periodic_noa = false;
+
+ ath_dbg(common, CHAN_CTX,
+ "Clearing NoA schedule\n");
+ }
+
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
break;
@@ -378,45 +574,22 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
* values and increment the index.
*/
if (sc->next_chan == &sc->offchannel.chan) {
- avp->noa_index++;
- avp->offchannel_start = tsf_time;
- avp->offchannel_duration = sc->sched.offchannel_duration;
-
- ath_dbg(common, CHAN_CTX,
- "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n",
- avp->offchannel_duration,
- avp->offchannel_start,
- avp->noa_index);
-
- /*
- * When multiple contexts are active, the NoA
- * has to be recalculated and advertised after
- * an offchannel operation.
- */
- if (ctx->active && avp->noa_duration)
- avp->noa_duration = 0;
-
+ ath_chanctx_offchannel_noa(sc, ctx, avp, tsf_time);
break;
}
- /*
- * Clear the extend_absence flag if it had been
- * set during the previous beacon transmission,
- * since we need to revert to the normal NoA
- * schedule.
- */
- if (ctx->active && sc->sched.extend_absence) {
- avp->noa_duration = 0;
- sc->sched.extend_absence = false;
- }
+ ath_chanctx_handle_bmiss(sc, ctx, avp);
- /* If at least two consecutive beacons were missed on the STA
- * chanctx, stay on the STA channel for one extra beacon period,
- * to resync the timer properly.
+ /*
+ * If a mgd_prepare_tx() has been called by mac80211,
+ * a one-shot NoA needs to be sent. This can happen
+ * with one or more active channel contexts - in both
+ * cases, a new NoA schedule has to be advertised.
*/
- if (ctx->active && sc->sched.beacon_miss >= 2) {
- avp->noa_duration = 0;
- sc->sched.extend_absence = true;
+ if (sc->sched.mgd_prepare_tx) {
+ ath_chanctx_set_oneshot_noa(sc, avp, tsf_time,
+ jiffies_to_usecs(HZ / 5));
+ break;
}
/* Prevent wrap-around issues */
@@ -429,31 +602,9 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
* announcement.
*/
if (ctx->active &&
- (!avp->noa_duration || sc->sched.force_noa_update)) {
- avp->noa_index++;
- avp->noa_start = tsf_time;
-
- if (sc->sched.extend_absence)
- avp->noa_duration = (3 * beacon_int / 2) +
- sc->sched.channel_switch_time;
- else
- avp->noa_duration =
- TU_TO_USEC(cur_conf->beacon_interval) / 2 +
- sc->sched.channel_switch_time;
-
- if (test_bit(ATH_OP_SCANNING, &common->op_flags) ||
- sc->sched.extend_absence)
- avp->periodic_noa = false;
- else
- avp->periodic_noa = true;
-
- ath_dbg(common, CHAN_CTX,
- "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
- avp->noa_duration,
- avp->noa_start,
- avp->noa_index,
- avp->periodic_noa);
- }
+ (!avp->noa_duration || sc->sched.force_noa_update))
+ ath_chanctx_set_periodic_noa(sc, avp, cur_conf,
+ tsf_time, beacon_int);
if (ctx->active && sc->sched.force_noa_update)
sc->sched.force_noa_update = false;
@@ -467,6 +618,15 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
}
sc->sched.beacon_pending = false;
+
+ if (sc->sched.mgd_prepare_tx) {
+ sc->sched.mgd_prepare_tx = false;
+ complete(&sc->go_beacon);
+ ath_dbg(common, CHAN_CTX,
+ "Beacon sent, complete go_beacon\n");
+ break;
+ }
+
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
break;
@@ -495,10 +655,16 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
sc->cur_chan == &sc->offchannel.chan)
break;
- ath_chanctx_adjust_tbtt_delta(sc);
sc->sched.beacon_pending = false;
sc->sched.beacon_miss = 0;
+ if (sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE ||
+ !sc->sched.beacon_adjust ||
+ !sc->cur_chan->tsf_val)
+ break;
+
+ ath_chanctx_adjust_tbtt_delta(sc);
+
/* TSF time might have been updated by the incoming beacon,
* need update the channel switch timer to reflect the change.
*/
@@ -507,10 +673,10 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL);
tsf_time += ath9k_hw_gettsf32(ah);
-
+ sc->sched.beacon_adjust = false;
ath_chanctx_setup_timer(sc, tsf_time);
break;
- case ATH_CHANCTX_EVENT_ASSOC:
+ case ATH_CHANCTX_EVENT_AUTHORIZED:
if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE ||
avp->chanctx != sc->cur_chan)
break;
@@ -552,6 +718,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
ath_chanctx_setup_timer(sc, tsf_time);
sc->sched.beacon_pending = true;
+ sc->sched.beacon_adjust = true;
break;
case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
if (sc->cur_chan == &sc->offchannel.chan ||
@@ -578,22 +745,6 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
break;
case ATH_CHANCTX_EVENT_ASSIGN:
- /*
- * When adding a new channel context, check if a scan
- * is in progress and abort it since the addition of
- * a new channel context is usually followed by VIF
- * assignment, in which case we have to start multi-channel
- * operation.
- */
- if (test_bit(ATH_OP_SCANNING, &common->op_flags)) {
- ath_dbg(common, CHAN_CTX,
- "Aborting HW scan to add new context\n");
-
- spin_unlock_bh(&sc->chan_lock);
- del_timer_sync(&sc->offchannel.timer);
- ath_scan_complete(sc, true);
- spin_lock_bh(&sc->chan_lock);
- }
break;
case ATH_CHANCTX_EVENT_CHANGE:
break;
@@ -751,6 +902,11 @@ void ath_offchannel_next(struct ath_softc *sc)
sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
} else {
+ spin_lock_bh(&sc->chan_lock);
+ sc->sched.offchannel_pending = false;
+ sc->sched.wait_switch = false;
+ spin_unlock_bh(&sc->chan_lock);
+
ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
NULL);
sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
@@ -770,8 +926,7 @@ void ath_roc_complete(struct ath_softc *sc, bool abort)
sc->offchannel.roc_vif = NULL;
sc->offchannel.roc_chan = NULL;
- if (!abort)
- ieee80211_remain_on_channel_expired(sc->hw);
+ ieee80211_remain_on_channel_expired(sc->hw);
ath_offchannel_next(sc);
ath9k_ps_restore(sc);
}
@@ -808,7 +963,7 @@ static void ath_scan_send_probe(struct ath_softc *sc,
struct ieee80211_tx_info *info;
int band = sc->offchannel.chan.chandef.chan->band;
- skb = ieee80211_probereq_get(sc->hw, vif,
+ skb = ieee80211_probereq_get(sc->hw, vif->addr,
ssid->ssid, ssid->ssid_len, req->ie_len);
if (!skb)
return;
@@ -902,9 +1057,8 @@ static void ath_offchannel_timer(unsigned long data)
break;
case ATH_OFFCHANNEL_ROC_START:
case ATH_OFFCHANNEL_ROC_WAIT:
- ctx = ath_chanctx_get_oper_chan(sc, false);
sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE;
- ath_chanctx_switch(sc, ctx, NULL);
+ ath_roc_complete(sc, false);
break;
default:
break;
@@ -1034,7 +1188,6 @@ static void ath_offchannel_channel_change(struct ath_softc *sc)
ieee80211_ready_on_channel(sc->hw);
break;
case ATH_OFFCHANNEL_ROC_DONE:
- ath_roc_complete(sc, false);
break;
default:
break;
@@ -1082,10 +1235,11 @@ void ath_chanctx_set_next(struct ath_softc *sc, bool force)
ath9k_chanctx_stop_queues(sc, sc->cur_chan);
queues_stopped = true;
- __ath9k_flush(sc->hw, ~0, true);
+ __ath9k_flush(sc->hw, ~0, true, false, false);
if (ath_chanctx_send_ps_frame(sc, true))
- __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false);
+ __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO),
+ false, false, false);
send_ps = true;
spin_lock_bh(&sc->chan_lock);
@@ -1177,6 +1331,8 @@ void ath9k_init_channel_context(struct ath_softc *sc)
(unsigned long)sc);
setup_timer(&sc->sched.timer, ath_chanctx_timer,
(unsigned long)sc);
+
+ init_completion(&sc->go_beacon);
}
void ath9k_deinit_channel_context(struct ath_softc *sc)
diff --git a/drivers/net/wireless/ath/ath9k/spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c
index 8f68426ca653..ec93ddf0863a 100644
--- a/drivers/net/wireless/ath/ath9k/spectral.c
+++ b/drivers/net/wireless/ath/ath9k/common-spectral.c
@@ -24,23 +24,24 @@ static s8 fix_rssi_inv_only(u8 rssi_val)
return (s8) rssi_val;
}
-static void ath_debug_send_fft_sample(struct ath_softc *sc,
+static void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv,
struct fft_sample_tlv *fft_sample_tlv)
{
int length;
- if (!sc->rfs_chan_spec_scan)
+ if (!spec_priv->rfs_chan_spec_scan)
return;
length = __be16_to_cpu(fft_sample_tlv->length) +
sizeof(*fft_sample_tlv);
- relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length);
+ relay_write(spec_priv->rfs_chan_spec_scan, fft_sample_tlv, length);
}
/* returns 1 if this was a spectral frame, even if not handled. */
-int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
+int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr,
struct ath_rx_status *rs, u64 tsf)
{
- struct ath_hw *ah = sc->sc_ah;
+ struct ath_hw *ah = spec_priv->ah;
+ struct ath_common *common = ath9k_hw_common(spec_priv->ah);
u8 num_bins, *bins, *vdata = (u8 *)hdr;
struct fft_sample_ht20 fft_sample_20;
struct fft_sample_ht20_40 fft_sample_40;
@@ -67,7 +68,7 @@ int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
return 0;
- chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
+ chan_type = cfg80211_get_chandef_type(&common->hw->conf.chandef);
if ((chan_type == NL80211_CHAN_HT40MINUS) ||
(chan_type == NL80211_CHAN_HT40PLUS)) {
fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
@@ -199,10 +200,11 @@ int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
tlv = (struct fft_sample_tlv *)&fft_sample_20;
}
- ath_debug_send_fft_sample(sc, tlv);
+ ath_debug_send_fft_sample(spec_priv, tlv);
return 1;
}
+EXPORT_SYMBOL(ath_cmn_process_fft);
/*********************/
/* spectral_scan_ctl */
@@ -211,11 +213,11 @@ int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char *mode = "";
unsigned int len;
- switch (sc->spectral_mode) {
+ switch (spec_priv->spectral_mode) {
case SPECTRAL_DISABLED:
mode = "disable";
break;
@@ -233,12 +235,84 @@ static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
return simple_read_from_buffer(user_buf, count, ppos, mode, len);
}
+void ath9k_cmn_spectral_scan_trigger(struct ath_common *common,
+ struct ath_spec_scan_priv *spec_priv)
+{
+ struct ath_hw *ah = spec_priv->ah;
+ u32 rxfilter;
+
+ if (config_enabled(CONFIG_ATH9K_TX99))
+ return;
+
+ if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
+ ath_err(common, "spectrum analyzer not implemented on this hardware\n");
+ return;
+ }
+
+ ath_ps_ops(common)->wakeup(common);
+ rxfilter = ath9k_hw_getrxfilter(ah);
+ ath9k_hw_setrxfilter(ah, rxfilter |
+ ATH9K_RX_FILTER_PHYRADAR |
+ ATH9K_RX_FILTER_PHYERR);
+
+ /* TODO: usually this should not be neccesary, but for some reason
+ * (or in some mode?) the trigger must be called after the
+ * configuration, otherwise the register will have its values reset
+ * (on my ar9220 to value 0x01002310)
+ */
+ ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode);
+ ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
+ ath_ps_ops(common)->restore(common);
+}
+EXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger);
+
+int ath9k_cmn_spectral_scan_config(struct ath_common *common,
+ struct ath_spec_scan_priv *spec_priv,
+ enum spectral_mode spectral_mode)
+{
+ struct ath_hw *ah = spec_priv->ah;
+
+ if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
+ ath_err(common, "spectrum analyzer not implemented on this hardware\n");
+ return -1;
+ }
+
+ switch (spectral_mode) {
+ case SPECTRAL_DISABLED:
+ spec_priv->spec_config.enabled = 0;
+ break;
+ case SPECTRAL_BACKGROUND:
+ /* send endless samples.
+ * TODO: is this really useful for "background"?
+ */
+ spec_priv->spec_config.endless = 1;
+ spec_priv->spec_config.enabled = 1;
+ break;
+ case SPECTRAL_CHANSCAN:
+ case SPECTRAL_MANUAL:
+ spec_priv->spec_config.endless = 0;
+ spec_priv->spec_config.enabled = 1;
+ break;
+ default:
+ return -1;
+ }
+
+ ath_ps_ops(common)->wakeup(common);
+ ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config);
+ ath_ps_ops(common)->restore(common);
+
+ spec_priv->spectral_mode = spectral_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL(ath9k_cmn_spectral_scan_config);
+
static ssize_t write_file_spec_scan_ctl(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
+ struct ath_common *common = ath9k_hw_common(spec_priv->ah);
char buf[32];
ssize_t len;
@@ -252,18 +326,18 @@ static ssize_t write_file_spec_scan_ctl(struct file *file,
buf[len] = '\0';
if (strncmp("trigger", buf, 7) == 0) {
- ath9k_spectral_scan_trigger(sc->hw);
+ ath9k_cmn_spectral_scan_trigger(common, spec_priv);
} else if (strncmp("background", buf, 10) == 0) {
- ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND);
+ ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND);
ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
} else if (strncmp("chanscan", buf, 8) == 0) {
- ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN);
+ ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN);
ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
} else if (strncmp("manual", buf, 6) == 0) {
- ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL);
+ ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL);
ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
} else if (strncmp("disable", buf, 7) == 0) {
- ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED);
+ ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED);
ath_dbg(common, CONFIG, "spectral scan: disabled\n");
} else {
return -EINVAL;
@@ -288,11 +362,11 @@ static ssize_t read_file_spectral_short_repeat(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char buf[32];
unsigned int len;
- len = sprintf(buf, "%d\n", sc->spec_config.short_repeat);
+ len = sprintf(buf, "%d\n", spec_priv->spec_config.short_repeat);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -300,7 +374,7 @@ static ssize_t write_file_spectral_short_repeat(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
@@ -316,7 +390,7 @@ static ssize_t write_file_spectral_short_repeat(struct file *file,
if (val > 1)
return -EINVAL;
- sc->spec_config.short_repeat = val;
+ spec_priv->spec_config.short_repeat = val;
return count;
}
@@ -336,11 +410,11 @@ static ssize_t read_file_spectral_count(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char buf[32];
unsigned int len;
- len = sprintf(buf, "%d\n", sc->spec_config.count);
+ len = sprintf(buf, "%d\n", spec_priv->spec_config.count);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -348,7 +422,7 @@ static ssize_t write_file_spectral_count(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
@@ -364,7 +438,7 @@ static ssize_t write_file_spectral_count(struct file *file,
if (val > 255)
return -EINVAL;
- sc->spec_config.count = val;
+ spec_priv->spec_config.count = val;
return count;
}
@@ -384,11 +458,11 @@ static ssize_t read_file_spectral_period(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char buf[32];
unsigned int len;
- len = sprintf(buf, "%d\n", sc->spec_config.period);
+ len = sprintf(buf, "%d\n", spec_priv->spec_config.period);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -396,7 +470,7 @@ static ssize_t write_file_spectral_period(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
@@ -412,7 +486,7 @@ static ssize_t write_file_spectral_period(struct file *file,
if (val > 255)
return -EINVAL;
- sc->spec_config.period = val;
+ spec_priv->spec_config.period = val;
return count;
}
@@ -432,11 +506,11 @@ static ssize_t read_file_spectral_fft_period(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char buf[32];
unsigned int len;
- len = sprintf(buf, "%d\n", sc->spec_config.fft_period);
+ len = sprintf(buf, "%d\n", spec_priv->spec_config.fft_period);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -444,7 +518,7 @@ static ssize_t write_file_spectral_fft_period(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
@@ -460,7 +534,7 @@ static ssize_t write_file_spectral_fft_period(struct file *file,
if (val > 15)
return -EINVAL;
- sc->spec_config.fft_period = val;
+ spec_priv->spec_config.fft_period = val;
return count;
}
@@ -506,38 +580,41 @@ static struct rchan_callbacks rfs_spec_scan_cb = {
/* Debug Init/Deinit */
/*********************/
-void ath9k_spectral_deinit_debug(struct ath_softc *sc)
+void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv)
{
- if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) {
- relay_close(sc->rfs_chan_spec_scan);
- sc->rfs_chan_spec_scan = NULL;
+ if (config_enabled(CONFIG_ATH9K_DEBUGFS) && spec_priv->rfs_chan_spec_scan) {
+ relay_close(spec_priv->rfs_chan_spec_scan);
+ spec_priv->rfs_chan_spec_scan = NULL;
}
}
+EXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug);
-void ath9k_spectral_init_debug(struct ath_softc *sc)
+void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv,
+ struct dentry *debugfs_phy)
{
- sc->rfs_chan_spec_scan = relay_open("spectral_scan",
- sc->debug.debugfs_phy,
+ spec_priv->rfs_chan_spec_scan = relay_open("spectral_scan",
+ debugfs_phy,
1024, 256, &rfs_spec_scan_cb,
NULL);
debugfs_create_file("spectral_scan_ctl",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spec_scan_ctl);
debugfs_create_file("spectral_short_repeat",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spectral_short_repeat);
debugfs_create_file("spectral_count",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spectral_count);
debugfs_create_file("spectral_period",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spectral_period);
debugfs_create_file("spectral_fft_period",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spectral_fft_period);
}
+EXPORT_SYMBOL(ath9k_cmn_spectral_init_debug);
diff --git a/drivers/net/wireless/ath/ath9k/spectral.h b/drivers/net/wireless/ath/ath9k/common-spectral.h
index 7b410c6858b0..82d9dd29652c 100644
--- a/drivers/net/wireless/ath/ath9k/spectral.h
+++ b/drivers/net/wireless/ath/ath9k/common-spectral.h
@@ -92,6 +92,13 @@ struct ath_ht20_40_fft_packet {
struct ath_radar_info radar_info;
} __packed;
+struct ath_spec_scan_priv {
+ struct ath_hw *ah;
+ /* relay(fs) channel for spectral scan */
+ struct rchan *rfs_chan_spec_scan;
+ enum spectral_mode spectral_mode;
+ struct ath_spec_scan spec_config;
+};
#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet))
@@ -123,23 +130,15 @@ static inline u8 spectral_bitmap_weight(u8 *bins)
return bins[0] & 0x3f;
}
-void ath9k_spectral_init_debug(struct ath_softc *sc);
-void ath9k_spectral_deinit_debug(struct ath_softc *sc);
+void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, struct dentry *debugfs_phy);
+void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv);
-void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
-int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
+void ath9k_cmn_spectral_scan_trigger(struct ath_common *common,
+ struct ath_spec_scan_priv *spec_priv);
+int ath9k_cmn_spectral_scan_config(struct ath_common *common,
+ struct ath_spec_scan_priv *spec_priv,
enum spectral_mode spectral_mode);
-
-#ifdef CONFIG_ATH9K_DEBUGFS
-int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
+int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr,
struct ath_rx_status *rs, u64 tsf);
-#else
-static inline int ath_process_fft(struct ath_softc *sc,
- struct ieee80211_hdr *hdr,
- struct ath_rx_status *rs, u64 tsf)
-{
- return 0;
-}
-#endif /* CONFIG_ATH9K_DEBUGFS */
#endif /* SPECTRAL_H */
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index 33b0c7aef2ea..e8c699446470 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -159,7 +159,7 @@ void ath9k_cmn_rx_skb_postprocess(struct ath_common *common,
if (test_bit(keyix, common->keymap))
rxs->flag |= RX_FLAG_DECRYPTED;
}
- if (ah->sw_mgmt_crypto &&
+ if (ah->sw_mgmt_crypto_rx &&
(rxs->flag & RX_FLAG_DECRYPTED) &&
ieee80211_is_mgmt(fc))
/* Use software decrypt for management frames. */
diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h
index ffc454b18637..2b79a568e803 100644
--- a/drivers/net/wireless/ath/ath9k/common.h
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -24,6 +24,7 @@
#include "common-init.h"
#include "common-beacon.h"
#include "common-debug.h"
+#include "common-spectral.h"
/* Common header for Atheros 802.11n base driver cores */
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 5c45e787814e..696e3d5309c6 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -828,13 +828,14 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
i = 0;
ath_for_each_chanctx(sc, ctx) {
- if (!ctx->assigned || list_empty(&ctx->vifs))
+ if (list_empty(&ctx->vifs))
continue;
ath9k_calculate_iter_data(sc, ctx, &iter_data);
len += scnprintf(buf + len, sizeof(buf) - len,
- "VIF-COUNTS: CTX %i AP: %i STA: %i MESH: %i WDS: %i",
- i++, iter_data.naps, iter_data.nstations,
+ "VIFS: CTX %i(%i) AP: %i STA: %i MESH: %i WDS: %i",
+ i++, (int)(ctx->assigned), iter_data.naps,
+ iter_data.nstations,
iter_data.nmeshes, iter_data.nwds);
len += scnprintf(buf + len, sizeof(buf) - len,
" ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
@@ -852,36 +853,31 @@ static ssize_t read_file_reset(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
+ static const char * const reset_cause[__RESET_TYPE_MAX] = {
+ [RESET_TYPE_BB_HANG] = "Baseband Hang",
+ [RESET_TYPE_BB_WATCHDOG] = "Baseband Watchdog",
+ [RESET_TYPE_FATAL_INT] = "Fatal HW Error",
+ [RESET_TYPE_TX_ERROR] = "TX HW error",
+ [RESET_TYPE_TX_GTT] = "Transmit timeout",
+ [RESET_TYPE_TX_HANG] = "TX Path Hang",
+ [RESET_TYPE_PLL_HANG] = "PLL RX Hang",
+ [RESET_TYPE_MAC_HANG] = "MAC Hang",
+ [RESET_TYPE_BEACON_STUCK] = "Stuck Beacon",
+ [RESET_TYPE_MCI] = "MCI Reset",
+ [RESET_TYPE_CALIBRATION] = "Calibration error",
+ };
char buf[512];
unsigned int len = 0;
+ int i;
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "Baseband Hang",
- sc->debug.stats.reset[RESET_TYPE_BB_HANG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "Baseband Watchdog",
- sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "Fatal HW Error",
- sc->debug.stats.reset[RESET_TYPE_FATAL_INT]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "TX HW error",
- sc->debug.stats.reset[RESET_TYPE_TX_ERROR]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "TX Path Hang",
- sc->debug.stats.reset[RESET_TYPE_TX_HANG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "PLL RX Hang",
- sc->debug.stats.reset[RESET_TYPE_PLL_HANG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "MAC Hang",
- sc->debug.stats.reset[RESET_TYPE_MAC_HANG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "Stuck Beacon",
- sc->debug.stats.reset[RESET_TYPE_BEACON_STUCK]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "MCI Reset",
- sc->debug.stats.reset[RESET_TYPE_MCI]);
+ for (i = 0; i < ARRAY_SIZE(reset_cause); i++) {
+ if (!reset_cause[i])
+ continue;
+
+ len += scnprintf(buf + len, sizeof(buf) - len,
+ "%17s: %2d\n", reset_cause[i],
+ sc->debug.stats.reset[i]);
+ }
if (len > sizeof(buf))
len = sizeof(buf);
@@ -1315,7 +1311,7 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw,
void ath9k_deinit_debug(struct ath_softc *sc)
{
- ath9k_spectral_deinit_debug(sc);
+ ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
}
int ath9k_init_debug(struct ath_hw *ah)
@@ -1335,7 +1331,7 @@ int ath9k_init_debug(struct ath_hw *ah)
ath9k_dfs_init_debug(sc);
ath9k_tx99_init_debug(sc);
- ath9k_spectral_init_debug(sc);
+ ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_dma);
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 53ae15bd0c9d..bd75b1f716db 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -49,6 +49,7 @@ enum ath_reset_type {
RESET_TYPE_MAC_HANG,
RESET_TYPE_BEACON_STUCK,
RESET_TYPE_MCI,
+ RESET_TYPE_CALIBRATION,
__RESET_TYPE_MAX
};
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index 3218ca994746..122b846b8ec0 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -262,7 +262,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
{
struct ar5416_eeprom_def *eep = &ah->eeprom.def;
struct ath_common *common = ath9k_hw_common(ah);
- u16 *eepdata, temp, magic, magic2;
+ u16 *eepdata, temp, magic;
u32 sum = 0, el;
bool need_swap = false;
int i, addr, size;
@@ -272,27 +272,16 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
return false;
}
- if (!ath9k_hw_use_flash(ah)) {
- ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic);
-
- if (magic != AR5416_EEPROM_MAGIC) {
- magic2 = swab16(magic);
-
- if (magic2 == AR5416_EEPROM_MAGIC) {
- size = sizeof(struct ar5416_eeprom_def);
- need_swap = true;
- eepdata = (u16 *) (&ah->eeprom);
+ if (swab16(magic) == AR5416_EEPROM_MAGIC &&
+ !(ah->ah_flags & AH_NO_EEP_SWAP)) {
+ size = sizeof(struct ar5416_eeprom_def);
+ need_swap = true;
+ eepdata = (u16 *) (&ah->eeprom);
- for (addr = 0; addr < size / sizeof(u16); addr++) {
- temp = swab16(*eepdata);
- *eepdata = temp;
- eepdata++;
- }
- } else {
- ath_err(common,
- "Invalid EEPROM Magic. Endianness mismatch.\n");
- return -EINVAL;
- }
+ for (addr = 0; addr < size / sizeof(u16); addr++) {
+ temp = swab16(*eepdata);
+ *eepdata = temp;
+ eepdata++;
}
}
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index b1956bf6e01e..2fef7a480fec 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -25,7 +25,12 @@ static void ath_led_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (brightness == LED_OFF));
+ u32 val = (brightness == LED_OFF);
+
+ if (sc->sc_ah->config.led_active_high)
+ val = !val;
+
+ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
}
void ath_deinit_leds(struct ath_softc *sc)
@@ -82,7 +87,7 @@ void ath_fill_led_pin(struct ath_softc *sc)
ath9k_hw_cfg_output(ah, ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
/* LED off, active low */
- ath9k_hw_set_gpio(ah, ah->led_pin, 1);
+ ath9k_hw_set_gpio(ah, ah->led_pin, (ah->config.led_active_high) ? 0 : 1);
}
#endif
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 09a5d72f3ff5..9dde265d3f84 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -481,6 +481,7 @@ struct ath9k_htc_priv {
unsigned long op_flags;
struct ath9k_hw_cal_data caldata;
+ struct ath_spec_scan_priv spec_priv;
spinlock_t beacon_lock;
struct ath_beacon_config cur_beacon_conf;
@@ -625,8 +626,12 @@ int ath9k_htc_resume(struct htc_target *htc_handle);
#endif
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
int ath9k_htc_init_debug(struct ath_hw *ah);
+void ath9k_htc_deinit_debug(struct ath9k_htc_priv *priv);
#else
static inline int ath9k_htc_init_debug(struct ath_hw *ah) { return 0; };
+static inline void ath9k_htc_deinit_debug(struct ath9k_htc_priv *priv)
+{
+}
#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
#endif /* HTC_H */
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index 8b529e4b8ac4..8cef1edcc621 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -490,6 +490,10 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
WARN_ON(i != ATH9K_HTC_SSTATS_LEN);
}
+void ath9k_htc_deinit_debug(struct ath9k_htc_priv *priv)
+{
+ ath9k_cmn_spectral_deinit_debug(&priv->spec_priv);
+}
int ath9k_htc_init_debug(struct ath_hw *ah)
{
@@ -501,6 +505,8 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
if (!priv->debug.debugfs_phy)
return -ENOMEM;
+ ath9k_cmn_spectral_init_debug(&priv->spec_priv, priv->debug.debugfs_phy);
+
debugfs_create_file("tgt_int_stats", S_IRUSR, priv->debug.debugfs_phy,
priv, &fops_tgt_int_stats);
debugfs_create_file("tgt_tx_stats", S_IRUSR, priv->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index 4014c4be6e79..e8fa9448da24 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -53,6 +53,21 @@ static const struct ieee80211_tpt_blink ath9k_htc_tpt_blink[] = {
};
#endif
+static void ath9k_htc_op_ps_wakeup(struct ath_common *common)
+{
+ ath9k_htc_ps_wakeup((struct ath9k_htc_priv *) common->priv);
+}
+
+static void ath9k_htc_op_ps_restore(struct ath_common *common)
+{
+ ath9k_htc_ps_restore((struct ath9k_htc_priv *) common->priv);
+}
+
+static struct ath_ps_ops ath9k_htc_ps_ops = {
+ .wakeup = ath9k_htc_op_ps_wakeup,
+ .restore = ath9k_htc_op_ps_restore,
+};
+
static int ath9k_htc_wait_for_target(struct ath9k_htc_priv *priv)
{
int time_left;
@@ -87,6 +102,7 @@ static void ath9k_deinit_device(struct ath9k_htc_priv *priv)
wiphy_rfkill_stop_polling(hw->wiphy);
ath9k_deinit_leds(priv);
+ ath9k_htc_deinit_debug(priv);
ieee80211_unregister_hw(hw);
ath9k_rx_cleanup(priv);
ath9k_tx_cleanup(priv);
@@ -449,6 +465,14 @@ static void ath9k_init_misc(struct ath9k_htc_priv *priv)
common->last_rssi = ATH_RSSI_DUMMY_MARKER;
priv->ah->opmode = NL80211_IFTYPE_STATION;
+
+ priv->spec_priv.ah = priv->ah;
+ priv->spec_priv.spec_config.enabled = 0;
+ priv->spec_priv.spec_config.short_repeat = false;
+ priv->spec_priv.spec_config.count = 8;
+ priv->spec_priv.spec_config.endless = false;
+ priv->spec_priv.spec_config.period = 0x12;
+ priv->spec_priv.spec_config.fft_period = 0x02;
}
static int ath9k_init_priv(struct ath9k_htc_priv *priv,
@@ -478,6 +502,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
common = ath9k_hw_common(ah);
common->ops = &ah->reg_ops;
+ common->ps_ops = &ath9k_htc_ps_ops;
common->bus_ops = &ath9k_usb_bus_ops;
common->ah = ah;
common->hw = priv->hw;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 994fff1ff519..92d5a6c5a225 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -314,6 +314,10 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
mod_timer(&priv->tx.cleanup_timer,
jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
+ /* perform spectral scan if requested. */
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
+ priv->spec_priv.spectral_mode == SPECTRAL_CHANSCAN)
+ ath9k_cmn_spectral_scan_trigger(common, &priv->spec_priv);
err:
ath9k_htc_ps_restore(priv);
return ret;
@@ -1443,7 +1447,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw,
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
- if (priv->ah->sw_mgmt_crypto &&
+ if (priv->ah->sw_mgmt_crypto_tx &&
key->cipher == WLAN_CIPHER_SUITE_CCMP)
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
ret = 0;
@@ -1687,7 +1691,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
return ret;
}
-static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
+static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct ath9k_htc_priv *priv = hw->priv;
struct ath_common *common = ath9k_hw_common(priv->ah);
@@ -1701,7 +1707,8 @@ static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
mutex_unlock(&priv->mutex);
}
-static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
+static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct ath9k_htc_priv *priv = hw->priv;
struct ath_common *common = ath9k_hw_common(priv->ah);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index f0484b1b617e..a0f58e2aa553 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -946,7 +946,7 @@ static inline void convert_htc_flag(struct ath_rx_status *rx_stats,
static void rx_status_htc_to_ath(struct ath_rx_status *rx_stats,
struct ath_htc_rx_status *rxstatus)
{
- rx_stats->rs_datalen = rxstatus->rs_datalen;
+ rx_stats->rs_datalen = be16_to_cpu(rxstatus->rs_datalen);
rx_stats->rs_status = rxstatus->rs_status;
rx_stats->rs_phyerr = rxstatus->rs_phyerr;
rx_stats->rs_rssi = rxstatus->rs_rssi;
@@ -1012,6 +1012,20 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
* separately to avoid doing two lookups for a rate for each frame.
*/
hdr = (struct ieee80211_hdr *)skb->data;
+
+ /*
+ * Process PHY errors and return so that the packet
+ * can be dropped.
+ */
+ if (rx_stats.rs_status & ATH9K_RXERR_PHY) {
+ /* TODO: Not using DFS processing now. */
+ if (ath_cmn_process_fft(&priv->spec_priv, hdr,
+ &rx_stats, rx_status->mactime)) {
+ /* TODO: Code to collect spectral scan statistics */
+ }
+ goto rx_next;
+ }
+
if (!ath9k_cmn_rx_accept(common, hdr, rx_status, &rx_stats,
&decrypt_error, priv->rxfilter))
goto rx_next;
diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
index 8e85efeaeffc..88769b64b20b 100644
--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
@@ -41,10 +41,9 @@ static inline void ath9k_hw_set_desc_link(struct ath_hw *ah, void *ds,
ath9k_hw_ops(ah)->set_desc_link(ds, link);
}
-static inline bool ath9k_hw_calibrate(struct ath_hw *ah,
- struct ath9k_channel *chan,
- u8 rxchainmask,
- bool longcal)
+static inline int ath9k_hw_calibrate(struct ath_hw *ah,
+ struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal)
{
return ath9k_hw_ops(ah)->calibrate(ah, chan, rxchainmask, longcal);
}
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 2ad605760e21..6d4b273469b1 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/time.h>
#include <linux/bitops.h>
+#include <linux/etherdevice.h>
#include <asm/unaligned.h>
#include "hw.h"
@@ -446,8 +447,16 @@ static int ath9k_hw_init_macaddr(struct ath_hw *ah)
common->macaddr[2 * i] = eeval >> 8;
common->macaddr[2 * i + 1] = eeval & 0xff;
}
- if (sum == 0 || sum == 0xffff * 3)
- return -EADDRNOTAVAIL;
+ if (!is_valid_ether_addr(common->macaddr)) {
+ ath_err(common,
+ "eeprom contains invalid mac address: %pM\n",
+ common->macaddr);
+
+ random_ether_addr(common->macaddr);
+ ath_err(common,
+ "random mac address will be used: %pM\n",
+ common->macaddr);
+ }
return 0;
}
@@ -1576,16 +1585,22 @@ static void ath9k_hw_init_mfp(struct ath_hw *ah)
* frames when constructing CCMP AAD. */
REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT,
0xc7ff);
- ah->sw_mgmt_crypto = false;
+ if (AR_SREV_9271(ah) || AR_DEVID_7010(ah))
+ ah->sw_mgmt_crypto_tx = true;
+ else
+ ah->sw_mgmt_crypto_tx = false;
+ ah->sw_mgmt_crypto_rx = false;
} else if (AR_SREV_9160_10_OR_LATER(ah)) {
/* Disable hardware crypto for management frames */
REG_CLR_BIT(ah, AR_PCU_MISC_MODE2,
AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE);
REG_SET_BIT(ah, AR_PCU_MISC_MODE2,
AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT);
- ah->sw_mgmt_crypto = true;
+ ah->sw_mgmt_crypto_tx = true;
+ ah->sw_mgmt_crypto_rx = true;
} else {
- ah->sw_mgmt_crypto = true;
+ ah->sw_mgmt_crypto_tx = true;
+ ah->sw_mgmt_crypto_rx = true;
}
}
@@ -1932,6 +1947,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
REGWRITE_BUFFER_FLUSH(ah);
+ ath9k_hw_gen_timer_start_tsf2(ah);
+
ath9k_hw_init_desc(ah);
if (ath9k_hw_btcoex_is_enabled(ah))
@@ -1940,8 +1957,10 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
if (ath9k_hw_mci_is_enabled(ah))
ar9003_mci_check_bt(ah);
- ath9k_hw_loadnf(ah, chan);
- ath9k_hw_start_nfcal(ah, true);
+ if (AR_SREV_9300_20_OR_LATER(ah)) {
+ ath9k_hw_loadnf(ah, chan);
+ ath9k_hw_start_nfcal(ah, true);
+ }
if (AR_SREV_9300_20_OR_LATER(ah))
ar9003_hw_bb_watchdog_config(ah);
@@ -2309,7 +2328,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ath_common *common = ath9k_hw_common(ah);
- unsigned int chip_chainmask;
u16 eeval;
u8 ant_div_ctl1, tx_chainmask, rx_chainmask;
@@ -2329,31 +2347,40 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
}
eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE);
- if ((eeval & (AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A)) == 0) {
- ath_err(common,
- "no band has been marked as supported in EEPROM\n");
- return -EINVAL;
+
+ if (eeval & AR5416_OPFLAGS_11A) {
+ if (ah->disable_5ghz)
+ ath_warn(common, "disabling 5GHz band\n");
+ else
+ pCap->hw_caps |= ATH9K_HW_CAP_5GHZ;
}
- if (eeval & AR5416_OPFLAGS_11A)
- pCap->hw_caps |= ATH9K_HW_CAP_5GHZ;
+ if (eeval & AR5416_OPFLAGS_11G) {
+ if (ah->disable_2ghz)
+ ath_warn(common, "disabling 2GHz band\n");
+ else
+ pCap->hw_caps |= ATH9K_HW_CAP_2GHZ;
+ }
- if (eeval & AR5416_OPFLAGS_11G)
- pCap->hw_caps |= ATH9K_HW_CAP_2GHZ;
+ if ((pCap->hw_caps & (ATH9K_HW_CAP_2GHZ | ATH9K_HW_CAP_5GHZ)) == 0) {
+ ath_err(common, "both bands are disabled\n");
+ return -EINVAL;
+ }
if (AR_SREV_9485(ah) ||
AR_SREV_9285(ah) ||
AR_SREV_9330(ah) ||
AR_SREV_9565(ah))
- chip_chainmask = 1;
- else if (AR_SREV_9462(ah))
- chip_chainmask = 3;
+ pCap->chip_chainmask = 1;
else if (!AR_SREV_9280_20_OR_LATER(ah))
- chip_chainmask = 7;
- else if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9340(ah))
- chip_chainmask = 3;
+ pCap->chip_chainmask = 7;
+ else if (!AR_SREV_9300_20_OR_LATER(ah) ||
+ AR_SREV_9340(ah) ||
+ AR_SREV_9462(ah) ||
+ AR_SREV_9531(ah))
+ pCap->chip_chainmask = 3;
else
- chip_chainmask = 7;
+ pCap->chip_chainmask = 7;
pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK);
/*
@@ -2371,8 +2398,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
/* Use rx_chainmask from EEPROM. */
pCap->rx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_RX_MASK);
- pCap->tx_chainmask = fixup_chainmask(chip_chainmask, pCap->tx_chainmask);
- pCap->rx_chainmask = fixup_chainmask(chip_chainmask, pCap->rx_chainmask);
+ pCap->tx_chainmask = fixup_chainmask(pCap->chip_chainmask, pCap->tx_chainmask);
+ pCap->rx_chainmask = fixup_chainmask(pCap->chip_chainmask, pCap->rx_chainmask);
ah->txchainmask = pCap->tx_chainmask;
ah->rxchainmask = pCap->rx_chainmask;
@@ -2886,6 +2913,16 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah)
}
EXPORT_SYMBOL(ath9k_hw_gettsf32);
+void ath9k_hw_gen_timer_start_tsf2(struct ath_hw *ah)
+{
+ struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
+
+ if (timer_table->tsf2_enabled) {
+ REG_SET_BIT(ah, AR_DIRECT_CONNECT, AR_DC_AP_STA_EN);
+ REG_SET_BIT(ah, AR_RESET_TSF, AR_RESET_TSF2_ONCE);
+ }
+}
+
struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
void (*trigger)(void *),
void (*overflow)(void *),
@@ -2896,7 +2933,11 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
struct ath_gen_timer *timer;
if ((timer_index < AR_FIRST_NDP_TIMER) ||
- (timer_index >= ATH_MAX_GEN_TIMER))
+ (timer_index >= ATH_MAX_GEN_TIMER))
+ return NULL;
+
+ if ((timer_index > AR_FIRST_NDP_TIMER) &&
+ !AR_SREV_9300_20_OR_LATER(ah))
return NULL;
timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL);
@@ -2910,6 +2951,11 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
timer->overflow = overflow;
timer->arg = arg;
+ if ((timer_index > AR_FIRST_NDP_TIMER) && !timer_table->tsf2_enabled) {
+ timer_table->tsf2_enabled = true;
+ ath9k_hw_gen_timer_start_tsf2(ah);
+ }
+
return timer;
}
EXPORT_SYMBOL(ath_gen_timer_alloc);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 975074fc11bc..1cbd33551513 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -217,8 +217,8 @@
#define AH_WOW_BEACON_MISS BIT(3)
enum ath_hw_txq_subtype {
- ATH_TXQ_AC_BE = 0,
- ATH_TXQ_AC_BK = 1,
+ ATH_TXQ_AC_BK = 0,
+ ATH_TXQ_AC_BE = 1,
ATH_TXQ_AC_VI = 2,
ATH_TXQ_AC_VO = 3,
};
@@ -244,13 +244,20 @@ enum ath9k_hw_caps {
ATH9K_HW_CAP_2GHZ = BIT(11),
ATH9K_HW_CAP_5GHZ = BIT(12),
ATH9K_HW_CAP_APM = BIT(13),
+#ifdef CONFIG_ATH9K_PCOEM
ATH9K_HW_CAP_RTT = BIT(14),
ATH9K_HW_CAP_MCI = BIT(15),
- ATH9K_HW_CAP_DFS = BIT(16),
- ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17),
- ATH9K_HW_CAP_PAPRD = BIT(18),
- ATH9K_HW_CAP_FCC_BAND_SWITCH = BIT(19),
- ATH9K_HW_CAP_BT_ANT_DIV = BIT(20),
+ ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(16),
+ ATH9K_HW_CAP_BT_ANT_DIV = BIT(17),
+#else
+ ATH9K_HW_CAP_RTT = 0,
+ ATH9K_HW_CAP_MCI = 0,
+ ATH9K_HW_WOW_DEVICE_CAPABLE = 0,
+ ATH9K_HW_CAP_BT_ANT_DIV = 0,
+#endif
+ ATH9K_HW_CAP_DFS = BIT(18),
+ ATH9K_HW_CAP_PAPRD = BIT(19),
+ ATH9K_HW_CAP_FCC_BAND_SWITCH = BIT(20),
};
/*
@@ -269,6 +276,7 @@ struct ath9k_hw_capabilities {
u16 rts_aggr_limit;
u8 tx_chainmask;
u8 rx_chainmask;
+ u8 chip_chainmask;
u8 max_txchains;
u8 max_rxchains;
u8 num_gpio_pins;
@@ -322,6 +330,7 @@ struct ath9k_ops_config {
bool alt_mingainidx;
bool no_pll_pwrsave;
bool tx_gain_buffalo;
+ bool led_active_high;
};
enum ath9k_int {
@@ -517,6 +526,7 @@ struct ath_gen_timer {
struct ath_gen_timer_table {
struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER];
u16 timer_mask;
+ bool tsf2_enabled;
};
struct ath_hw_antcomb_conf {
@@ -681,10 +691,8 @@ struct ath_hw_ops {
bool power_off);
void (*rx_enable)(struct ath_hw *ah);
void (*set_desc_link)(void *ds, u32 link);
- bool (*calibrate)(struct ath_hw *ah,
- struct ath9k_channel *chan,
- u8 rxchainmask,
- bool longcal);
+ int (*calibrate)(struct ath_hw *ah, struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal);
bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked,
u32 *sync_cause_p);
void (*set_txdesc)(struct ath_hw *ah, void *ds,
@@ -726,6 +734,7 @@ enum ath_cal_list {
#define AH_USE_EEPROM 0x1
#define AH_UNPLUGGED 0x2 /* The card has been physically removed. */
#define AH_FASTCC 0x4
+#define AH_NO_EEP_SWAP 0x8 /* Do not swap EEPROM data */
struct ath_hw {
struct ath_ops reg_ops;
@@ -747,7 +756,8 @@ struct ath_hw {
} eeprom;
const struct eeprom_ops *eep_ops;
- bool sw_mgmt_crypto;
+ bool sw_mgmt_crypto_tx;
+ bool sw_mgmt_crypto_rx;
bool is_pciexpress;
bool aspm_enabled;
bool is_monitoring;
@@ -924,10 +934,16 @@ struct ath_hw {
bool is_clk_25mhz;
int (*get_mac_revision)(void);
int (*external_reset)(void);
+ bool disable_2ghz;
+ bool disable_5ghz;
const struct firmware *eeprom_blob;
struct ath_dynack dynack;
+
+ bool tpc_enabled;
+ u8 tx_power[Ar5416RateSize];
+ u8 tx_power_stbc[Ar5416RateSize];
};
struct ath_bus_ops {
@@ -1027,6 +1043,7 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah,
struct ath_gen_timer *timer,
u32 timer_next,
u32 timer_period);
+void ath9k_hw_gen_timer_start_tsf2(struct ath_hw *ah);
void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer);
void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer);
@@ -1067,6 +1084,8 @@ int ar9003_paprd_init_table(struct ath_hw *ah);
bool ar9003_paprd_is_done(struct ath_hw *ah);
bool ar9003_is_paprd_enabled(struct ath_hw *ah);
void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+ struct ath9k_channel *chan);
/* Hardware family op attach helpers */
int ar5008_hw_attach_phy_ops(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 3bd030494986..d1c39346b264 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -88,6 +88,21 @@ static const struct ieee80211_tpt_blink ath9k_tpt_blink[] = {
static void ath9k_deinit_softc(struct ath_softc *sc);
+static void ath9k_op_ps_wakeup(struct ath_common *common)
+{
+ ath9k_ps_wakeup((struct ath_softc *) common->priv);
+}
+
+static void ath9k_op_ps_restore(struct ath_common *common)
+{
+ ath9k_ps_restore((struct ath_softc *) common->priv);
+}
+
+static struct ath_ps_ops ath9k_ps_ops = {
+ .wakeup = ath9k_op_ps_wakeup,
+ .restore = ath9k_op_ps_restore,
+};
+
/*
* Read and write, they both share the same lock. We do this to serialize
* reads and writes on Atheros 802.11n PCI devices only. This is required
@@ -172,17 +187,20 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
ath_reg_notifier_apply(wiphy, request, reg);
/* Set tx power */
- if (ah->curchan) {
- sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
- ath9k_ps_wakeup(sc);
- ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
- sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
- /* synchronize DFS detector if regulatory domain changed */
- if (sc->dfs_detector != NULL)
- sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
- request->dfs_region);
- ath9k_ps_restore(sc);
- }
+ if (!ah->curchan)
+ return;
+
+ sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
+ ath9k_ps_wakeup(sc);
+ ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
+ ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
+ sc->cur_chan->txpower,
+ &sc->cur_chan->cur_txpower);
+ /* synchronize DFS detector if regulatory domain changed */
+ if (sc->dfs_detector != NULL)
+ sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
+ request->dfs_region);
+ ath9k_ps_restore(sc);
}
/*
@@ -348,12 +366,13 @@ static void ath9k_init_misc(struct ath_softc *sc)
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
- sc->spec_config.enabled = 0;
- sc->spec_config.short_repeat = true;
- sc->spec_config.count = 8;
- sc->spec_config.endless = false;
- sc->spec_config.period = 0xFF;
- sc->spec_config.fft_period = 0xF;
+ sc->spec_priv.ah = sc->sc_ah;
+ sc->spec_priv.spec_config.enabled = 0;
+ sc->spec_priv.spec_config.short_repeat = true;
+ sc->spec_priv.spec_config.count = 8;
+ sc->spec_priv.spec_config.endless = false;
+ sc->spec_priv.spec_config.period = 0xFF;
+ sc->spec_priv.spec_config.fft_period = 0xF;
}
static void ath9k_init_pcoem_platform(struct ath_softc *sc)
@@ -362,6 +381,9 @@ static void ath9k_init_pcoem_platform(struct ath_softc *sc)
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_common *common = ath9k_hw_common(ah);
+ if (!IS_ENABLED(CONFIG_ATH9K_PCOEM))
+ return;
+
if (common->bus_ops->ath_bus_type != ATH_PCI)
return;
@@ -419,6 +441,9 @@ static void ath9k_init_pcoem_platform(struct ath_softc *sc)
ah->config.no_pll_pwrsave = true;
ath_info(common, "Disable PLL PowerSave\n");
}
+
+ if (sc->driver_data & ATH9K_PCI_LED_ACT_HI)
+ ah->config.led_active_high = true;
}
static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
@@ -507,10 +532,14 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
ah->reg_ops.read = ath9k_ioread32;
ah->reg_ops.write = ath9k_iowrite32;
ah->reg_ops.rmw = ath9k_reg_rmw;
- sc->sc_ah = ah;
pCap = &ah->caps;
common = ath9k_hw_common(ah);
+
+ /* Will be cleared in ath9k_start() */
+ set_bit(ATH_OP_INVALID, &common->op_flags);
+
+ sc->sc_ah = ah;
sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
sc->tx99_power = MAX_RATE_POWER + 1;
init_waitqueue_head(&sc->tx_wait);
@@ -528,10 +557,15 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
ah->is_clk_25mhz = pdata->is_clk_25mhz;
ah->get_mac_revision = pdata->get_mac_revision;
ah->external_reset = pdata->external_reset;
+ ah->disable_2ghz = pdata->disable_2ghz;
+ ah->disable_5ghz = pdata->disable_5ghz;
+ if (!pdata->endian_check)
+ ah->ah_flags |= AH_NO_EEP_SWAP;
}
common->ops = &ah->reg_ops;
common->bus_ops = bus_ops;
+ common->ps_ops = &ath9k_ps_ops;
common->ah = ah;
common->hw = sc->hw;
common->priv = sc;
@@ -866,9 +900,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
common = ath9k_hw_common(ah);
ath9k_set_hw_capab(sc, hw);
- /* Will be cleared in ath9k_start() */
- set_bit(ATH_OP_INVALID, &common->op_flags);
-
/* Initialize regulatory */
error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
ath9k_reg_notifier);
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 2343f56e6498..b829263e3d0a 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -371,9 +371,15 @@ void ath_ani_calibrate(unsigned long data)
/* Perform calibration if necessary */
if (longcal || shortcal) {
- common->ani.caldone =
- ath9k_hw_calibrate(ah, ah->curchan,
- ah->rxchainmask, longcal);
+ int ret = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask,
+ longcal);
+ if (ret < 0) {
+ common->ani.caldone = 0;
+ ath9k_queue_reset(sc, RESET_TYPE_CALIBRATION);
+ return;
+ }
+
+ common->ani.caldone = ret;
}
ath_dbg(common, ANI,
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 275205ab5f15..3e58bfa0c1fd 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -311,14 +311,7 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type,
q = ATH9K_NUM_TX_QUEUES - 3;
break;
case ATH9K_TX_QUEUE_DATA:
- for (q = 0; q < ATH9K_NUM_TX_QUEUES; q++)
- if (ah->txq[q].tqi_type ==
- ATH9K_TX_QUEUE_INACTIVE)
- break;
- if (q == ATH9K_NUM_TX_QUEUES) {
- ath_err(common, "No available TX queue\n");
- return -1;
- }
+ q = qinfo->tqi_subtype;
break;
default:
ath_err(common, "Invalid TX queue type: %u\n", type);
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index aa69ceaad0be..e55fa11894b6 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -704,7 +704,7 @@ struct ath_tx_info {
enum ath9k_pkt_type type;
enum ath9k_key_type keytype;
u8 keyix;
- u8 txpower;
+ u8 txpower[4];
};
struct ath_hw;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 4f18a6be0c7d..9a72640237cb 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -54,7 +54,8 @@ u8 ath9k_parse_mpdudensity(u8 mpdudensity)
}
}
-static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
+static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq,
+ bool sw_pending)
{
bool pending = false;
@@ -65,6 +66,9 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
goto out;
}
+ if (!sw_pending)
+ goto out;
+
if (txq->mac80211_qnum >= 0) {
struct list_head *list;
@@ -229,8 +233,9 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
ath9k_calculate_summary_state(sc, sc->cur_chan);
ath_startrecv(sc);
- ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->cur_chan->txpower, &sc->curtxpow);
+ ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
+ sc->cur_chan->txpower,
+ &sc->cur_chan->cur_txpower);
clear_bit(ATH_OP_HW_RESET, &common->op_flags);
if (!sc->cur_chan->offchannel && start) {
@@ -270,7 +275,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
return true;
}
-int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
+static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@@ -281,6 +286,7 @@ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
__ath_cancel_work(sc);
tasklet_disable(&sc->intr_tq);
+ tasklet_disable(&sc->bcon_tasklet);
spin_lock_bh(&sc->sc_pcu_lock);
if (!sc->cur_chan->offchannel) {
@@ -326,6 +332,7 @@ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
out:
spin_unlock_bh(&sc->sc_pcu_lock);
+ tasklet_enable(&sc->bcon_tasklet);
tasklet_enable(&sc->intr_tq);
return r;
@@ -505,16 +512,13 @@ irqreturn_t ath_isr(int irq, void *dev)
if (!ah || test_bit(ATH_OP_INVALID, &common->op_flags))
return IRQ_NONE;
- /* shared irq, not for us */
+ if (!AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
+ return IRQ_NONE;
+ /* shared irq, not for us */
if (!ath9k_hw_intrpend(ah))
return IRQ_NONE;
- if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
- ath9k_hw_kill_interrupts(ah);
- return IRQ_HANDLED;
- }
-
/*
* Figure out the reason(s) for the interrupt. Note
* that the hal returns a pseudo-ISR that may include
@@ -525,6 +529,9 @@ irqreturn_t ath_isr(int irq, void *dev)
ath9k_debug_sync_cause(sc, sync_cause);
status &= ah->imask; /* discard unasked-for bits */
+ if (AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
+ return IRQ_HANDLED;
+
/*
* If there are no status bits set, then this interrupt was not
* for me (should have been caught above).
@@ -539,11 +546,10 @@ irqreturn_t ath_isr(int irq, void *dev)
sched = true;
/*
- * If a FATAL or RXORN interrupt is received, we have to reset the
- * chip immediately.
+ * If a FATAL interrupt is received, we have to reset the chip
+ * immediately.
*/
- if ((status & ATH9K_INT_FATAL) || ((status & ATH9K_INT_RXORN) &&
- !(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)))
+ if (status & ATH9K_INT_FATAL)
goto chip_reset;
if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) &&
@@ -598,23 +604,37 @@ chip_reset:
#undef SCHED_INTR
}
-int ath_reset(struct ath_softc *sc)
+/*
+ * This function is called when a HW reset cannot be deferred
+ * and has to be immediate.
+ */
+int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int r;
+ ath9k_hw_kill_interrupts(sc->sc_ah);
+ set_bit(ATH_OP_HW_RESET, &common->op_flags);
+
ath9k_ps_wakeup(sc);
- r = ath_reset_internal(sc, NULL);
+ r = ath_reset_internal(sc, hchan);
ath9k_ps_restore(sc);
return r;
}
+/*
+ * When a HW reset can be deferred, it is added to the
+ * hw_reset_work workqueue, but we set ATH_OP_HW_RESET before
+ * queueing.
+ */
void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
#ifdef CONFIG_ATH9K_DEBUGFS
RESET_STAT_INC(sc, type);
#endif
+ ath9k_hw_kill_interrupts(sc->sc_ah);
set_bit(ATH_OP_HW_RESET, &common->op_flags);
ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
}
@@ -623,7 +643,9 @@ void ath_reset_work(struct work_struct *work)
{
struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work);
- ath_reset(sc);
+ ath9k_ps_wakeup(sc);
+ ath_reset_internal(sc, NULL);
+ ath9k_ps_restore(sc);
}
/**********************/
@@ -707,7 +729,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
if (ah->led_pin >= 0) {
ath9k_hw_cfg_output(ah, ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
- ath9k_hw_set_gpio(ah, ah->led_pin, 0);
+ ath9k_hw_set_gpio(ah, ah->led_pin,
+ (ah->config.led_active_high) ? 1 : 0);
}
/*
@@ -849,7 +872,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
spin_lock_bh(&sc->sc_pcu_lock);
if (ah->led_pin >= 0) {
- ath9k_hw_set_gpio(ah, ah->led_pin, 1);
+ ath9k_hw_set_gpio(ah, ah->led_pin,
+ (ah->config.led_active_high) ? 0 : 1);
ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
}
@@ -865,6 +889,9 @@ static void ath9k_stop(struct ieee80211_hw *hw)
&sc->cur_chan->chandef);
ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
+
+ set_bit(ATH_OP_INVALID, &common->op_flags);
+
ath9k_hw_phy_disable(ah);
ath9k_hw_configpcipowersave(ah, true);
@@ -873,7 +900,6 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath9k_ps_restore(sc);
- set_bit(ATH_OP_INVALID, &common->op_flags);
sc->ps_idle = prev_idle;
mutex_unlock(&sc->mutex);
@@ -1036,7 +1062,7 @@ static void ath9k_set_offchannel_state(struct ath_softc *sc)
eth_zero_addr(common->curbssid);
eth_broadcast_addr(common->bssidmask);
- ether_addr_copy(common->macaddr, vif->addr);
+ memcpy(common->macaddr, vif->addr, ETH_ALEN);
common->curaid = 0;
ah->opmode = vif->type;
ah->imask &= ~ATH9K_INT_SWBA;
@@ -1077,7 +1103,7 @@ void ath9k_calculate_summary_state(struct ath_softc *sc,
ath9k_calculate_iter_data(sc, ctx, &iter_data);
if (iter_data.has_hw_macaddr)
- ether_addr_copy(common->macaddr, iter_data.hw_macaddr);
+ memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN);
memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
ath_hw_setbssidmask(common);
@@ -1167,7 +1193,8 @@ static void ath9k_assign_hw_queues(struct ieee80211_hw *hw,
for (i = 0; i < IEEE80211_NUM_ACS; i++)
vif->hw_queue[i] = i;
- if (vif->type == NL80211_IFTYPE_AP)
+ if (vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_MESH_POINT)
vif->cab_queue = hw->queues - 2;
else
vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
@@ -1323,78 +1350,6 @@ static void ath9k_disable_ps(struct ath_softc *sc)
ath_dbg(common, PS, "PowerSave disabled\n");
}
-void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw)
-{
- struct ath_softc *sc = hw->priv;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- u32 rxfilter;
-
- if (config_enabled(CONFIG_ATH9K_TX99))
- return;
-
- if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
- ath_err(common, "spectrum analyzer not implemented on this hardware\n");
- return;
- }
-
- ath9k_ps_wakeup(sc);
- rxfilter = ath9k_hw_getrxfilter(ah);
- ath9k_hw_setrxfilter(ah, rxfilter |
- ATH9K_RX_FILTER_PHYRADAR |
- ATH9K_RX_FILTER_PHYERR);
-
- /* TODO: usually this should not be neccesary, but for some reason
- * (or in some mode?) the trigger must be called after the
- * configuration, otherwise the register will have its values reset
- * (on my ar9220 to value 0x01002310)
- */
- ath9k_spectral_scan_config(hw, sc->spectral_mode);
- ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
- ath9k_ps_restore(sc);
-}
-
-int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
- enum spectral_mode spectral_mode)
-{
- struct ath_softc *sc = hw->priv;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
-
- if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
- ath_err(common, "spectrum analyzer not implemented on this hardware\n");
- return -1;
- }
-
- switch (spectral_mode) {
- case SPECTRAL_DISABLED:
- sc->spec_config.enabled = 0;
- break;
- case SPECTRAL_BACKGROUND:
- /* send endless samples.
- * TODO: is this really useful for "background"?
- */
- sc->spec_config.endless = 1;
- sc->spec_config.enabled = 1;
- break;
- case SPECTRAL_CHANSCAN:
- case SPECTRAL_MANUAL:
- sc->spec_config.endless = 0;
- sc->spec_config.enabled = 1;
- break;
- default:
- return -1;
- }
-
- ath9k_ps_wakeup(sc);
- ath9k_hw_ops(ah)->spectral_scan_config(ah, &sc->spec_config);
- ath9k_ps_restore(sc);
-
- sc->spectral_mode = spectral_mode;
-
- return 0;
-}
-
static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
{
struct ath_softc *sc = hw->priv;
@@ -1455,8 +1410,9 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
if (changed & IEEE80211_CONF_CHANGE_POWER) {
ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level);
sc->cur_chan->txpower = 2 * conf->power_level;
- ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->cur_chan->txpower, &sc->curtxpow);
+ ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
+ sc->cur_chan->txpower,
+ &sc->cur_chan->cur_txpower);
}
mutex_unlock(&sc->mutex);
@@ -1553,6 +1509,40 @@ static int ath9k_sta_remove(struct ieee80211_hw *hw,
return 0;
}
+static int ath9k_sta_state(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ int ret = 0;
+
+ if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_ASSOC) {
+ ret = ath9k_sta_add(hw, vif, sta);
+ ath_dbg(common, CONFIG,
+ "Add station: %pM\n", sta->addr);
+ } else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTH) {
+ ret = ath9k_sta_remove(hw, vif, sta);
+ ath_dbg(common, CONFIG,
+ "Remove station: %pM\n", sta->addr);
+ }
+
+ if (ath9k_is_chanctx_enabled()) {
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTHORIZED)
+ ath_chanctx_event(sc, vif,
+ ATH_CHANCTX_EVENT_AUTHORIZED);
+ }
+ }
+
+ return ret;
+}
+
static void ath9k_sta_set_tx_filter(struct ath_hw *ah,
struct ath_node *an,
bool set)
@@ -1677,7 +1667,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
- if (sc->sc_ah->sw_mgmt_crypto &&
+ if (sc->sc_ah->sw_mgmt_crypto_tx &&
key->cipher == WLAN_CIPHER_SUITE_CCMP)
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
ret = 0;
@@ -1737,17 +1727,11 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n",
bss_conf->bssid, bss_conf->assoc);
- ether_addr_copy(avp->bssid, bss_conf->bssid);
+ memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
avp->aid = bss_conf->aid;
avp->assoc = bss_conf->assoc;
ath9k_calculate_summary_state(sc, avp->chanctx);
-
- if (ath9k_is_chanctx_enabled()) {
- if (bss_conf->assoc)
- ath_chanctx_event(sc, vif,
- ATH_CHANCTX_EVENT_ASSOC);
- }
}
if (changed & BSS_CHANGED_IBSS) {
@@ -1843,6 +1827,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
u16 tid, u16 *ssn, u8 buf_size)
{
struct ath_softc *sc = hw->priv;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
bool flush = false;
int ret = 0;
@@ -1854,6 +1839,12 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_RX_STOP:
break;
case IEEE80211_AMPDU_TX_START:
+ if (ath9k_is_chanctx_enabled()) {
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags)) {
+ ret = -EBUSY;
+ break;
+ }
+ }
ath9k_ps_wakeup(sc);
ret = ath_tx_aggr_start(sc, sta, tid, ssn);
if (!ret)
@@ -1967,7 +1958,8 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw,
mutex_unlock(&sc->mutex);
}
-static bool ath9k_has_tx_pending(struct ath_softc *sc)
+static bool ath9k_has_tx_pending(struct ath_softc *sc,
+ bool sw_pending)
{
int i, npend = 0;
@@ -1975,7 +1967,8 @@ static bool ath9k_has_tx_pending(struct ath_softc *sc)
if (!ATH_TXQ_SETUP(sc, i))
continue;
- npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i]);
+ npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i],
+ sw_pending);
if (npend)
break;
}
@@ -1987,18 +1980,38 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop)
{
struct ath_softc *sc = hw->priv;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ if (ath9k_is_chanctx_enabled()) {
+ if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+ goto flush;
+ /*
+ * If MCC is active, extend the flush timeout
+ * and wait for the HW/SW queues to become
+ * empty. This needs to be done outside the
+ * sc->mutex lock to allow the channel scheduler
+ * to switch channel contexts.
+ *
+ * The vif queues have been stopped in mac80211,
+ * so there won't be any incoming frames.
+ */
+ __ath9k_flush(hw, queues, drop, true, true);
+ return;
+ }
+flush:
mutex_lock(&sc->mutex);
- __ath9k_flush(hw, queues, drop);
+ __ath9k_flush(hw, queues, drop, true, false);
mutex_unlock(&sc->mutex);
}
-void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop,
+ bool sw_pending, bool timeout_override)
{
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- int timeout = HZ / 5; /* 200 ms */
+ int timeout;
bool drain_txq;
cancel_delayed_work_sync(&sc->tx_complete_work);
@@ -2013,7 +2026,17 @@ void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
return;
}
- if (wait_event_timeout(sc->tx_wait, !ath9k_has_tx_pending(sc),
+ spin_lock_bh(&sc->chan_lock);
+ if (timeout_override)
+ timeout = HZ / 5;
+ else
+ timeout = sc->cur_chan->flush_timeout;
+ spin_unlock_bh(&sc->chan_lock);
+
+ ath_dbg(common, CHAN_CTX,
+ "Flush timeout: %d\n", jiffies_to_msecs(timeout));
+
+ if (wait_event_timeout(sc->tx_wait, !ath9k_has_tx_pending(sc, sw_pending),
timeout) > 0)
drop = false;
@@ -2024,7 +2047,7 @@ void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
spin_unlock_bh(&sc->sc_pcu_lock);
if (!drain_txq)
- ath_reset(sc);
+ ath_reset(sc, NULL);
ath9k_ps_restore(sc);
}
@@ -2036,7 +2059,7 @@ static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
- return ath9k_has_tx_pending(sc);
+ return ath9k_has_tx_pending(sc, true);
}
static int ath9k_tx_last_beacon(struct ieee80211_hw *hw)
@@ -2167,14 +2190,17 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
return 0;
}
-static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
+static void ath9k_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
set_bit(ATH_OP_SCANNING, &common->op_flags);
}
-static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
+static void ath9k_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -2183,6 +2209,28 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
+static void ath9k_cancel_pending_offchannel(struct ath_softc *sc)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ if (sc->offchannel.roc_vif) {
+ ath_dbg(common, CHAN_CTX,
+ "%s: Aborting RoC\n", __func__);
+
+ del_timer_sync(&sc->offchannel.timer);
+ if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
+ ath_roc_complete(sc, true);
+ }
+
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags)) {
+ ath_dbg(common, CHAN_CTX,
+ "%s: Aborting HW scan\n", __func__);
+
+ del_timer_sync(&sc->offchannel.timer);
+ ath_scan_complete(sc, true);
+ }
+}
+
static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_scan_request *hw_req)
{
@@ -2313,7 +2361,6 @@ static int ath9k_add_chanctx(struct ieee80211_hw *hw,
conf->def.chan->center_freq);
ath_chanctx_set_channel(sc, ctx, &conf->def);
- ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ASSIGN);
mutex_unlock(&sc->mutex);
return 0;
@@ -2370,6 +2417,8 @@ static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw,
struct ath_chanctx *ctx = ath_chanctx_get(conf);
int i;
+ ath9k_cancel_pending_offchannel(sc);
+
mutex_lock(&sc->mutex);
ath_dbg(common, CHAN_CTX,
@@ -2399,6 +2448,8 @@ static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw,
struct ath_chanctx *ctx = ath_chanctx_get(conf);
int ac;
+ ath9k_cancel_pending_offchannel(sc);
+
mutex_lock(&sc->mutex);
ath_dbg(common, CHAN_CTX,
@@ -2422,7 +2473,11 @@ static void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw,
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (struct ath_vif *) vif->drv_priv;
+ struct ath_beacon_config *cur_conf;
+ struct ath_chanctx *go_ctx;
+ unsigned long timeout;
bool changed = false;
+ u32 beacon_int;
if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
return;
@@ -2433,19 +2488,57 @@ static void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw,
mutex_lock(&sc->mutex);
spin_lock_bh(&sc->chan_lock);
- if (sc->next_chan || (sc->cur_chan != avp->chanctx)) {
- sc->next_chan = avp->chanctx;
+ if (sc->next_chan || (sc->cur_chan != avp->chanctx))
changed = true;
+ spin_unlock_bh(&sc->chan_lock);
+
+ if (!changed)
+ goto out;
+
+ ath9k_cancel_pending_offchannel(sc);
+
+ go_ctx = ath_is_go_chanctx_present(sc);
+
+ if (go_ctx) {
+ /*
+ * Wait till the GO interface gets a chance
+ * to send out an NoA.
+ */
+ spin_lock_bh(&sc->chan_lock);
+ sc->sched.mgd_prepare_tx = true;
+ cur_conf = &go_ctx->beacon;
+ beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
+ spin_unlock_bh(&sc->chan_lock);
+
+ timeout = usecs_to_jiffies(beacon_int * 2);
+ init_completion(&sc->go_beacon);
+
+ mutex_unlock(&sc->mutex);
+
+ if (wait_for_completion_timeout(&sc->go_beacon,
+ timeout) == 0) {
+ ath_dbg(common, CHAN_CTX,
+ "Failed to send new NoA\n");
+
+ spin_lock_bh(&sc->chan_lock);
+ sc->sched.mgd_prepare_tx = false;
+ spin_unlock_bh(&sc->chan_lock);
+ }
+
+ mutex_lock(&sc->mutex);
}
+
ath_dbg(common, CHAN_CTX,
- "%s: Set chanctx state to FORCE_ACTIVE, changed: %d\n",
- __func__, changed);
+ "%s: Set chanctx state to FORCE_ACTIVE for vif: %pM\n",
+ __func__, vif->addr);
+
+ spin_lock_bh(&sc->chan_lock);
+ sc->next_chan = avp->chanctx;
sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE;
spin_unlock_bh(&sc->chan_lock);
- if (changed)
- ath_chanctx_set_next(sc, true);
-
+ ath_chanctx_set_next(sc, true);
+out:
mutex_unlock(&sc->mutex);
}
@@ -2468,6 +2561,24 @@ void ath9k_fill_chanctx_ops(void)
#endif
+static int ath9k_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ int *dbm)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_vif *avp = (void *)vif->drv_priv;
+
+ mutex_lock(&sc->mutex);
+ if (avp->chanctx)
+ *dbm = avp->chanctx->cur_txpower;
+ else
+ *dbm = sc->cur_chan->cur_txpower;
+ mutex_unlock(&sc->mutex);
+
+ *dbm /= 2;
+
+ return 0;
+}
+
struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
@@ -2477,8 +2588,7 @@ struct ieee80211_ops ath9k_ops = {
.remove_interface = ath9k_remove_interface,
.config = ath9k_config,
.configure_filter = ath9k_configure_filter,
- .sta_add = ath9k_sta_add,
- .sta_remove = ath9k_sta_remove,
+ .sta_state = ath9k_sta_state,
.sta_notify = ath9k_sta_notify,
.conf_tx = ath9k_conf_tx,
.bss_info_changed = ath9k_bss_info_changed,
@@ -2515,4 +2625,5 @@ struct ieee80211_ops ath9k_ops = {
#endif
.sw_scan_start = ath9k_sw_scan_start,
.sw_scan_complete = ath9k_sw_scan_complete,
+ .get_txpower = ath9k_get_txpower,
};
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index c018dea0b2e8..f009b5b57e5e 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -30,6 +30,7 @@ static const struct pci_device_id ath_pci_id_table[] = {
{ PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+#ifdef CONFIG_ATH9K_PCOEM
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x002A,
PCI_VENDOR_ID_AZWAVE,
@@ -82,6 +83,7 @@ static const struct pci_device_id ath_pci_id_table[] = {
PCI_VENDOR_ID_AZWAVE,
0x2C37),
.driver_data = ATH9K_PCI_BT_ANT_DIV },
+#endif
{ PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */
@@ -102,6 +104,7 @@ static const struct pci_device_id ath_pci_id_table[] = {
{ PCI_VDEVICE(ATHEROS, 0x0030) }, /* PCI-E AR9300 */
+#ifdef CONFIG_ATH9K_PCOEM
/* PCI-E CUS198 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x0032,
@@ -294,10 +297,12 @@ static const struct pci_device_id ath_pci_id_table[] = {
PCI_VENDOR_ID_ASUSTEK,
0x850D),
.driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+#endif
{ PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */
{ PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */
+#ifdef CONFIG_ATH9K_PCOEM
/* PCI-E CUS217 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x0034,
@@ -652,11 +657,14 @@ static const struct pci_device_id ath_pci_id_table[] = {
0x0036,
PCI_VENDOR_ID_DELL,
0x020E),
- .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+ .driver_data = ATH9K_PCI_AR9565_2ANT |
+ ATH9K_PCI_BT_ANT_DIV |
+ ATH9K_PCI_LED_ACT_HI},
/* PCI-E AR9565 (WB335) */
{ PCI_VDEVICE(ATHEROS, 0x0036),
.driver_data = ATH9K_PCI_BT_ANT_DIV },
+#endif
{ 0 }
};
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 6914e21816e4..7395afbc5124 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -870,7 +870,7 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
*/
if (rx_stats->rs_status & ATH9K_RXERR_PHY) {
ath9k_dfs_process_phyerr(sc, hdr, rx_stats, rx_status->mactime);
- if (ath_process_fft(sc, hdr, rx_stats, rx_status->mactime))
+ if (ath_cmn_process_fft(&sc->spec_priv, hdr, rx_stats, rx_status->mactime))
RX_STAT_INC(rx_spectral);
return -EINVAL;
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index 2a938f4feac5..fb11a9172f38 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -892,10 +892,21 @@
(AR_SREV_9330((_ah)) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_12))
+#ifdef CONFIG_ATH9K_PCOEM
+#define AR_SREV_9462(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462))
#define AR_SREV_9485(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485))
+#define AR_SREV_9565(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565))
+#else
+#define AR_SREV_9462(_ah) 0
+#define AR_SREV_9485(_ah) 0
+#define AR_SREV_9565(_ah) 0
+#endif
+
#define AR_SREV_9485_11_OR_LATER(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485) && \
+ (AR_SREV_9485(_ah) && \
((_ah)->hw_version.macRev >= AR_SREV_REVISION_9485_11))
#define AR_SREV_9485_OR_LATER(_ah) \
(((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9485))
@@ -915,34 +926,30 @@
(AR_SREV_9285_12_OR_LATER(_ah) && \
((REG_READ(_ah, AR_AN_SYNTH9) & 0x7) == 0x1))
-#define AR_SREV_9462(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462))
#define AR_SREV_9462_20(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ (AR_SREV_9462(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20))
#define AR_SREV_9462_21(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ (AR_SREV_9462(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_21))
#define AR_SREV_9462_20_OR_LATER(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ (AR_SREV_9462(_ah) && \
((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_20))
#define AR_SREV_9462_21_OR_LATER(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ (AR_SREV_9462(_ah) && \
((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_21))
-#define AR_SREV_9565(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565))
#define AR_SREV_9565_10(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \
+ (AR_SREV_9565(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_10))
#define AR_SREV_9565_101(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \
+ (AR_SREV_9565(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_101))
#define AR_SREV_9565_11(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \
+ (AR_SREV_9565(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_11))
#define AR_SREV_9565_11_OR_LATER(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \
+ (AR_SREV_9565(_ah) && \
((_ah)->hw_version.macRev >= AR_SREV_REVISION_9565_11))
#define AR_SREV_9550(_ah) \
@@ -1598,6 +1605,7 @@ enum {
#define AR_RESET_TSF 0x8020
#define AR_RESET_TSF_ONCE 0x01000000
+#define AR_RESET_TSF2_ONCE 0x02000000
#define AR_MAX_CFP_DUR 0x8038
#define AR_CFP_VAL 0x0000FFFF
@@ -1716,6 +1724,8 @@ enum {
#define AR_TPC_CTS_S 8
#define AR_TPC_CHIRP 0x003f0000
#define AR_TPC_CHIRP_S 16
+#define AR_TPC_RPT 0x3f000000
+#define AR_TPC_RPT_S 24
#define AR_QUIET1 0x80fc
#define AR_QUIET1_NEXT_QUIET_S 0
@@ -1959,6 +1969,8 @@ enum {
#define AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET 0x80000000
#define AR_MAC_PCU_GEN_TIMER_TSF_SEL 0x83d8
+#define AR_DIRECT_CONNECT 0x83a0
+#define AR_DC_AP_STA_EN 0x00000001
#define AR_AES_MUTE_MASK0 0x805c
#define AR_AES_MUTE_MASK0_FC 0x0000FFFF
diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c
index 40ab65e6882f..ac4781f37e78 100644
--- a/drivers/net/wireless/ath/ath9k/tx99.c
+++ b/drivers/net/wireless/ath/ath9k/tx99.c
@@ -99,7 +99,7 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
static void ath9k_tx99_deinit(struct ath_softc *sc)
{
- ath_reset(sc);
+ ath_reset(sc, NULL);
ath9k_ps_wakeup(sc);
ath9k_tx99_stop(sc);
@@ -127,7 +127,7 @@ static int ath9k_tx99_init(struct ath_softc *sc)
memset(&txctl, 0, sizeof(txctl));
txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
- ath_reset(sc);
+ ath_reset(sc, NULL);
ath9k_ps_wakeup(sc);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index d6e54a3c88f6..e9bd02c2e844 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1096,6 +1096,37 @@ void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop)
}
}
+static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
+ u8 rateidx)
+{
+ u8 max_power;
+ struct ath_hw *ah = sc->sc_ah;
+
+ if (sc->tx99_state)
+ return MAX_RATE_POWER;
+
+ if (!AR_SREV_9300_20_OR_LATER(ah)) {
+ /* ar9002 is not sipported for the moment */
+ return MAX_RATE_POWER;
+ }
+
+ if (!bf->bf_state.bfs_paprd) {
+ struct sk_buff *skb = bf->bf_mpdu;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ath_frame_info *fi = get_frame_info(skb);
+
+ if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC))
+ max_power = min(ah->tx_power_stbc[rateidx],
+ fi->tx_power);
+ else
+ max_power = min(ah->tx_power[rateidx], fi->tx_power);
+ } else {
+ max_power = ah->paprd_training_power;
+ }
+
+ return max_power;
+}
+
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_info *info, int len, bool rts)
{
@@ -1166,6 +1197,8 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
is_40, is_sgi, is_sp);
if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC;
+
+ info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
continue;
}
@@ -1193,6 +1226,8 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
phy, rate->bitrate * 100, len, rix, is_sp);
+
+ info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
}
/* For AR5416 - RTS cannot be followed by a frame larger than 8K */
@@ -1239,7 +1274,6 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
memset(&info, 0, sizeof(info));
info.is_first = true;
info.is_last = true;
- info.txpower = MAX_RATE_POWER;
info.qcu = txq->axq_qnum;
while (bf) {
@@ -2063,6 +2097,7 @@ static void setup_frame_info(struct ieee80211_hw *hw,
fi->keyix = ATH9K_TXKEYIX_INVALID;
fi->keytype = keytype;
fi->framelen = framelen;
+ fi->tx_power = MAX_RATE_POWER;
if (!rate)
return;
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
index b80b2138ce3c..dca6df13fd5b 100644
--- a/drivers/net/wireless/ath/carl9170/phy.c
+++ b/drivers/net/wireless/ath/carl9170/phy.c
@@ -994,7 +994,7 @@ static int carl9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz,
refsel0 = 0;
refsel1 = 1;
}
- chansel = byte_rev_table[chansel];
+ chansel = bitrev8(chansel);
} else {
if (freq == 2484) {
chansel = 10 + (freq - 2274) / 5;
@@ -1002,7 +1002,7 @@ static int carl9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz,
} else
chansel = 16 + (freq - 2272) / 5;
chansel *= 4;
- chansel = byte_rev_table[chansel];
+ chansel = bitrev8(chansel);
}
d1 = chansel;
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c
index 650be79c7ac9..cfd0554cf140 100644
--- a/drivers/net/wireless/ath/dfs_pattern_detector.c
+++ b/drivers/net/wireless/ath/dfs_pattern_detector.c
@@ -86,7 +86,7 @@ static const struct radar_detector_specs fcc_radar_ref_types[] = {
FCC_PATTERN(1, 0, 5, 150, 230, 1, 23),
FCC_PATTERN(2, 6, 10, 200, 500, 1, 16),
FCC_PATTERN(3, 11, 20, 200, 500, 1, 12),
- FCC_PATTERN(4, 50, 100, 1000, 2000, 20, 1),
+ FCC_PATTERN(4, 50, 100, 1000, 2000, 1, 20),
FCC_PATTERN(5, 0, 1, 333, 333, 1, 9),
};
@@ -105,7 +105,7 @@ static const struct radar_detector_specs jp_radar_ref_types[] = {
JP_PATTERN(4, 0, 5, 150, 230, 1, 23),
JP_PATTERN(5, 6, 10, 200, 500, 1, 16),
JP_PATTERN(6, 11, 20, 200, 500, 1, 12),
- JP_PATTERN(7, 50, 100, 1000, 2000, 20, 1),
+ JP_PATTERN(7, 50, 100, 1000, 2000, 1, 20),
JP_PATTERN(5, 0, 1, 333, 333, 1, 9),
};
diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c
index b71d2b33532d..267c35d1f699 100644
--- a/drivers/net/wireless/ath/wcn36xx/main.c
+++ b/drivers/net/wireless/ath/wcn36xx/main.c
@@ -494,7 +494,9 @@ out:
return ret;
}
-static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw)
+static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct wcn36xx *wcn = hw->priv;
@@ -502,7 +504,8 @@ static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw)
wcn36xx_smd_start_scan(wcn);
}
-static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw)
+static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct wcn36xx *wcn = hw->priv;
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index d9f4b30dd343..38332a6dfb3a 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -792,12 +792,13 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
}
static int wil_cfg80211_del_station(struct wiphy *wiphy,
- struct net_device *dev, const u8 *mac)
+ struct net_device *dev,
+ struct station_del_parameters *params)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
mutex_lock(&wil->mutex);
- wil6210_disconnect(wil, mac);
+ wil6210_disconnect(wil, params->mac, params->reason_code, false);
mutex_unlock(&wil->mutex);
return 0;
diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
index 8d99021d27a8..3249562d93b4 100644
--- a/drivers/net/wireless/ath/wil6210/debug.c
+++ b/drivers/net/wireless/ath/wil6210/debug.c
@@ -32,6 +32,23 @@ void wil_err(struct wil6210_priv *wil, const char *fmt, ...)
va_end(args);
}
+void wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ if (net_ratelimit()) {
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ netdev_err(ndev, "%pV", &vaf);
+ trace_wil6210_log_err(&vaf);
+ va_end(args);
+ }
+}
+
void wil_info(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 54a6ddc6301b..4e6e14501c2f 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -573,8 +573,10 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
if (!frame)
return -ENOMEM;
- if (copy_from_user(frame, buf, len))
+ if (copy_from_user(frame, buf, len)) {
+ kfree(frame);
return -EIO;
+ }
params.buf = frame;
params.len = len;
@@ -614,8 +616,10 @@ static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
return -ENOMEM;
rc = simple_write_to_buffer(wmi, len, ppos, buf, len);
- if (rc < 0)
+ if (rc < 0) {
+ kfree(wmi);
return rc;
+ }
cmd = &wmi[1];
cmdid = le16_to_cpu(wmi->id);
diff --git a/drivers/net/wireless/ath/wil6210/fw.c b/drivers/net/wireless/ath/wil6210/fw.c
index 8c6f3b041f77..93c5cc16c515 100644
--- a/drivers/net/wireless/ath/wil6210/fw.c
+++ b/drivers/net/wireless/ath/wil6210/fw.c
@@ -15,7 +15,6 @@
*/
#include <linux/firmware.h>
#include <linux/module.h>
-#include <linux/pci.h>
#include <linux/crc32.h>
#include "wil6210.h"
#include "fw.h"
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index 44cb71f5ea5b..d4acf93a9a02 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -446,7 +446,7 @@ static int wil_fw_load(struct wil6210_priv *wil, const void *data, size_t size)
if (size >= sizeof(*hdr)) {
wil_err_fw(wil, "Stop at offset %ld"
" record type %d [%zd bytes]\n",
- (const void *)hdr - data,
+ (long)((const void *)hdr - data),
le16_to_cpu(hdr->type), hdr_sz);
}
return -EINVAL;
@@ -471,7 +471,7 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name)
size_t sz;
const void *d;
- rc = request_firmware(&fw, name, wil_to_pcie_dev(wil));
+ rc = request_firmware(&fw, name, wil_to_dev(wil));
if (rc) {
wil_err_fw(wil, "Failed to load firmware %s\n", name);
return rc;
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 90f416f239bd..4bcbd6297b3e 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -36,7 +36,8 @@
*/
#define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL)
-#define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE
+#define WIL6210_IMC_RX (BIT_DMA_EP_RX_ICR_RX_DONE | \
+ BIT_DMA_EP_RX_ICR_RX_HTRSH)
#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \
BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | \
@@ -171,6 +172,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
u32 isr = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR));
+ bool need_unmask = true;
trace_wil6210_irq_rx(isr);
wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
@@ -182,12 +184,24 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
wil6210_mask_irq_rx(wil);
- if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
+ /* RX_DONE and RX_HTRSH interrupts are the same if interrupt
+ * moderation is not used. Interrupt moderation may cause RX
+ * buffer overflow while RX_DONE is delayed. The required
+ * action is always the same - should empty the accumulated
+ * packets from the RX ring.
+ */
+ if (isr & (BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH)) {
wil_dbg_irq(wil, "RX done\n");
- isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
+
+ if (isr & BIT_DMA_EP_RX_ICR_RX_HTRSH)
+ wil_err_ratelimited(wil, "Received \"Rx buffer is in risk "
+ "of overflow\" interrupt\n");
+
+ isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH);
if (test_bit(wil_status_reset_done, &wil->status)) {
if (test_bit(wil_status_napi_en, &wil->status)) {
wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
+ need_unmask = false;
napi_schedule(&wil->napi_rx);
} else {
wil_err(wil, "Got Rx interrupt while "
@@ -204,6 +218,10 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
/* Rx IRQ will be enabled when NAPI processing finished */
atomic_inc(&wil->isr_count_rx);
+
+ if (unlikely(need_unmask))
+ wil6210_unmask_irq_rx(wil);
+
return IRQ_HANDLED;
}
@@ -213,6 +231,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
u32 isr = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR));
+ bool need_unmask = true;
trace_wil6210_irq_tx(isr);
wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
@@ -231,6 +250,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
isr &= ~(BIT(25) - 1UL);
if (test_bit(wil_status_reset_done, &wil->status)) {
wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
+ need_unmask = false;
napi_schedule(&wil->napi_tx);
} else {
wil_err(wil, "Got Tx interrupt while in reset\n");
@@ -243,6 +263,10 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
/* Tx IRQ will be enabled when NAPI processing finished */
atomic_inc(&wil->isr_count_tx);
+
+ if (unlikely(need_unmask))
+ wil6210_unmask_irq_tx(wil);
+
return IRQ_HANDLED;
}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 6500caf8d609..8ff3fe34fe05 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -38,6 +38,65 @@ static unsigned int itr_trsh = WIL6210_ITR_TRSH_DEFAULT;
module_param(itr_trsh, uint, S_IRUGO);
MODULE_PARM_DESC(itr_trsh, " Interrupt moderation threshold, usecs.");
+/* We allow allocation of more than 1 page buffers to support large packets.
+ * It is suboptimal behavior performance wise in case MTU above page size.
+ */
+unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - ETH_HLEN;
+static int mtu_max_set(const char *val, const struct kernel_param *kp)
+{
+ int ret;
+
+ /* sets mtu_max directly. no need to restore it in case of
+ * illegal value since we assume this will fail insmod
+ */
+ ret = param_set_uint(val, kp);
+ if (ret)
+ return ret;
+
+ if (mtu_max < 68 || mtu_max > IEEE80211_MAX_DATA_LEN_DMG)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static struct kernel_param_ops mtu_max_ops = {
+ .set = mtu_max_set,
+ .get = param_get_uint,
+};
+
+module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, S_IRUGO);
+MODULE_PARM_DESC(mtu_max, " Max MTU value.");
+
+static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
+static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
+
+static int ring_order_set(const char *val, const struct kernel_param *kp)
+{
+ int ret;
+ uint x;
+
+ ret = kstrtouint(val, 0, &x);
+ if (ret)
+ return ret;
+
+ if ((x < WIL_RING_SIZE_ORDER_MIN) || (x > WIL_RING_SIZE_ORDER_MAX))
+ return -EINVAL;
+
+ *((uint *)kp->arg) = x;
+
+ return 0;
+}
+
+static struct kernel_param_ops ring_order_ops = {
+ .set = ring_order_set,
+ .get = param_get_uint,
+};
+
+module_param_cb(rx_ring_order, &ring_order_ops, &rx_ring_order, S_IRUGO);
+MODULE_PARM_DESC(rx_ring_order, " Rx ring order; size = 1 << order");
+module_param_cb(tx_ring_order, &ring_order_ops, &tx_ring_order, S_IRUGO);
+MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order");
+
#define RST_DELAY (20) /* msec, for loop in @wil_target_reset */
#define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */
@@ -74,7 +133,8 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
__raw_writel(*s++, d++);
}
-static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
+static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
+ u16 reason_code, bool from_event)
{
uint i;
struct net_device *ndev = wil_to_ndev(wil);
@@ -86,7 +146,9 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
sta->data_port_open = false;
if (sta->status != wil_sta_unused) {
- wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
+ if (!from_event)
+ wmi_disconnect_sta(wil, sta->addr, reason_code);
+
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
@@ -118,7 +180,8 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
memset(&sta->stats, 0, sizeof(sta->stats));
}
-static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
+static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
+ u16 reason_code, bool from_event)
{
int cid = -ENOENT;
struct net_device *ndev = wil_to_ndev(wil);
@@ -133,10 +196,10 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
}
if (cid >= 0) /* disconnect 1 peer */
- wil_disconnect_cid(wil, cid);
+ wil_disconnect_cid(wil, cid, reason_code, from_event);
else /* disconnect all */
for (cid = 0; cid < WIL6210_MAX_CID; cid++)
- wil_disconnect_cid(wil, cid);
+ wil_disconnect_cid(wil, cid, reason_code, from_event);
/* link state */
switch (wdev->iftype) {
@@ -145,8 +208,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
wil_link_off(wil);
if (test_bit(wil_status_fwconnected, &wil->status)) {
clear_bit(wil_status_fwconnected, &wil->status);
- cfg80211_disconnected(ndev,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
+ cfg80211_disconnected(ndev, reason_code,
NULL, 0, GFP_KERNEL);
} else if (test_bit(wil_status_fwconnecting, &wil->status)) {
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
@@ -166,7 +228,7 @@ static void wil_disconnect_worker(struct work_struct *work)
struct wil6210_priv, disconnect_worker);
mutex_lock(&wil->mutex);
- _wil6210_disconnect(wil, NULL);
+ _wil6210_disconnect(wil, NULL, WLAN_REASON_UNSPECIFIED, false);
mutex_unlock(&wil->mutex);
}
@@ -188,6 +250,7 @@ static void wil_scan_timer_fn(ulong x)
clear_bit(wil_status_fwready, &wil->status);
wil_err(wil, "Scan timeout detected, start fw error recovery\n");
+ wil->recovery_state = fw_recovery_pending;
schedule_work(&wil->fw_error_worker);
}
@@ -223,6 +286,11 @@ static void wil_fw_error_worker(struct work_struct *work)
wil_dbg_misc(wil, "fw error worker\n");
+ if (!netif_running(wil_to_ndev(wil))) {
+ wil_info(wil, "No recovery - interface is down\n");
+ return;
+ }
+
/* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
* passed since last recovery attempt
*/
@@ -257,9 +325,12 @@ static void wil_fw_error_worker(struct work_struct *work)
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
+ wil_info(wil, "No recovery for AP-like interface\n");
/* recovery in these modes is done by upper layers */
break;
default:
+ wil_err(wil, "No recovery - unknown interface type %d\n",
+ wdev->iftype);
break;
}
mutex_unlock(&wil->mutex);
@@ -291,7 +362,7 @@ static void wil_connect_worker(struct work_struct *work)
wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
- rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0);
+ rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
wil->pending_connect_cid = -1;
if (rc == 0) {
wil->sta[cid].status = wil_sta_connected;
@@ -346,12 +417,23 @@ int wil_priv_init(struct wil6210_priv *wil)
return 0;
}
-void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
+/**
+ * wil6210_disconnect - disconnect one connection
+ * @wil: driver context
+ * @bssid: peer to disconnect, NULL to disconnect all
+ * @reason_code: Reason code for the Disassociation frame
+ * @from_event: whether is invoked from FW event handler
+ *
+ * Disconnect and release associated resources. If invoked not from the
+ * FW event handler, issue WMI command(s) to trigger MAC disconnect.
+ */
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
+ u16 reason_code, bool from_event)
{
wil_dbg_misc(wil, "%s()\n", __func__);
del_timer_sync(&wil->connect_timer);
- _wil6210_disconnect(wil, bssid);
+ _wil6210_disconnect(wil, bssid, reason_code, from_event);
}
void wil_priv_deinit(struct wil6210_priv *wil)
@@ -363,7 +445,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
cancel_work_sync(&wil->disconnect_worker);
cancel_work_sync(&wil->fw_error_worker);
mutex_lock(&wil->mutex);
- wil6210_disconnect(wil, NULL);
+ wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
mutex_unlock(&wil->mutex);
wmi_event_flush(wil);
destroy_workqueue(wil->wmi_wq_conn);
@@ -395,7 +477,7 @@ static inline void wil_release_cpu(struct wil6210_priv *wil)
static int wil_target_reset(struct wil6210_priv *wil)
{
int delay = 0;
- u32 hw_state;
+ u32 x;
u32 rev_id;
bool is_sparrow = (wil->board->board == WIL_BOARD_SPARROW);
@@ -410,9 +492,25 @@ static int wil_target_reset(struct wil6210_priv *wil)
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
wil_halt_cpu(wil);
- C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL); /* 40 MHz */
+
+ /* Clear Fw Download notification */
+ C(RGF_USER_USAGE_6, BIT(0));
if (is_sparrow) {
+ S(RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN);
+ /* XTAL stabilization should take about 3ms */
+ usleep_range(5000, 7000);
+ x = R(RGF_CAF_PLL_LOCK_STATUS);
+ if (!(x & BIT_CAF_OSC_DIG_XTAL_STABLE)) {
+ wil_err(wil, "Xtal stabilization timeout\n"
+ "RGF_CAF_PLL_LOCK_STATUS = 0x%08x\n", x);
+ return -ETIME;
+ }
+ /* switch 10k to XTAL*/
+ C(RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF);
+ /* 40 MHz */
+ C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL);
+
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf);
}
@@ -453,13 +551,13 @@ static int wil_target_reset(struct wil6210_priv *wil)
/* wait until device ready. typical time is 200..250 msec */
do {
msleep(RST_DELAY);
- hw_state = R(RGF_USER_HW_MACHINE_STATE);
+ x = R(RGF_USER_HW_MACHINE_STATE);
if (delay++ > RST_COUNT) {
wil_err(wil, "Reset not completed, hw_state 0x%08x\n",
- hw_state);
+ x);
return -ETIME;
}
- } while (hw_state != HW_MACHINE_BOOT_DONE);
+ } while (x != HW_MACHINE_BOOT_DONE);
/* TODO: Erez check rev_id != 1 */
if (!is_sparrow && (rev_id != 1))
@@ -535,7 +633,7 @@ int wil_reset(struct wil6210_priv *wil)
WARN_ON(test_bit(wil_status_napi_en, &wil->status));
cancel_work_sync(&wil->disconnect_worker);
- wil6210_disconnect(wil, NULL);
+ wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
wil->status = 0; /* prevent NAPI from being scheduled */
@@ -640,7 +738,7 @@ int __wil_up(struct wil6210_priv *wil)
return rc;
/* Rx VRING. After MAC and beacon */
- rc = wil_rx_init(wil);
+ rc = wil_rx_init(wil, 1 << rx_ring_order);
if (rc)
return rc;
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 239965106c05..e81703ca7701 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -41,7 +41,7 @@ static int wil_change_mtu(struct net_device *ndev, int new_mtu)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
- if (new_mtu < 68 || new_mtu > (TX_BUF_LEN - ETH_HLEN)) {
+ if (new_mtu < 68 || new_mtu > mtu_max) {
wil_err(wil, "invalid MTU %d\n", new_mtu);
return -EINVAL;
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 2936ef0c18cb..e3f8bdce5abc 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -206,12 +206,10 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
u32 i, int headroom)
{
struct device *dev = wil_to_dev(wil);
- unsigned int sz = RX_BUF_LEN;
+ unsigned int sz = mtu_max + ETH_HLEN;
struct vring_rx_desc dd, *d = &dd;
volatile struct vring_rx_desc *_d = &vring->va[i].rx;
dma_addr_t pa;
-
- /* TODO align */
struct sk_buff *skb = dev_alloc_skb(sz + headroom);
if (unlikely(!skb))
@@ -385,7 +383,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
struct vring_rx_desc *d;
struct sk_buff *skb;
dma_addr_t pa;
- unsigned int sz = RX_BUF_LEN;
+ unsigned int sz = mtu_max + ETH_HLEN;
u16 dmalen;
u8 ftype;
u8 ds_bits;
@@ -596,7 +594,7 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota)
wil_rx_refill(wil, v->size);
}
-int wil_rx_init(struct wil6210_priv *wil)
+int wil_rx_init(struct wil6210_priv *wil, u16 size)
{
struct vring *vring = &wil->vring_rx;
int rc;
@@ -608,7 +606,7 @@ int wil_rx_init(struct wil6210_priv *wil)
return -EINVAL;
}
- vring->size = WIL6210_RX_RING_SIZE;
+ vring->size = size;
rc = wil_vring_alloc(wil, vring);
if (rc)
return rc;
@@ -646,7 +644,8 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
.action = cpu_to_le32(WMI_VRING_CMD_ADD),
.vring_cfg = {
.tx_sw_ring = {
- .max_mpdu_size = cpu_to_le16(TX_BUF_LEN),
+ .max_mpdu_size =
+ cpu_to_le16(mtu_max + ETH_HLEN),
.ring_size = cpu_to_le16(size),
},
.ringid = id,
@@ -927,8 +926,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_dbg_txrx(wil, "%s()\n", __func__);
if (avail < 1 + nr_frags) {
- wil_err(wil, "Tx ring full. No space for %d fragments\n",
- 1 + nr_frags);
+ wil_err_ratelimited(wil,
+ "Tx ring full. No space for %d fragments\n",
+ 1 + nr_frags);
return -ENOMEM;
}
_d = &vring->va[i].tx;
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index de046716d2b7..630aeb5fa7f4 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -21,8 +21,8 @@
#define BUF_HW_OWNED (0)
/* size of max. Tx/Rx buffers, as supported by FW */
-#define RX_BUF_LEN (2242)
-#define TX_BUF_LEN (2242)
+#define TXRX_BUF_LEN_DEFAULT (2242)
+
/* how many bytes to reserve for rtap header? */
#define WIL6210_RTAP_SIZE (128)
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index ce6488e42091..c6ec5b99ac7d 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -24,6 +24,7 @@
#include "wil_platform.h"
extern bool no_fw_recovery;
+extern unsigned int mtu_max;
#define WIL_NAME "wil6210"
#define WIL_FW_NAME "wil6210.fw"
@@ -48,8 +49,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
#define WIL6210_MEM_SIZE (2*1024*1024UL)
-#define WIL6210_RX_RING_SIZE (128)
-#define WIL6210_TX_RING_SIZE (512)
+#define WIL_RX_RING_SIZE_ORDER_DEFAULT (9)
+#define WIL_TX_RING_SIZE_ORDER_DEFAULT (9)
+/* limit ring size in range [32..32k] */
+#define WIL_RING_SIZE_ORDER_MIN (5)
+#define WIL_RING_SIZE_ORDER_MAX (15)
#define WIL6210_MAX_TX_RINGS (24) /* HW limit */
#define WIL6210_MAX_CID (8) /* HW limit */
#define WIL6210_NAPI_BUDGET (16) /* arbitrary */
@@ -117,12 +121,15 @@ struct RGF_ICR {
#define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0 (0x880c18)
#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1 (0x880c2c)
+#define RGF_USER_SPARROW_M_4 (0x880c50) /* Sparrow */
+ #define BIT_SPARROW_M_4_SEL_SLEEP_OR_REF BIT(2)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
#define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */
#define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */
#define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0)
+ #define BIT_DMA_EP_RX_ICR_RX_HTRSH BIT(1)
#define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */
#define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0)
#define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1)
@@ -152,6 +159,10 @@ struct RGF_ICR {
#define RGF_MAC_MTRL_COUNTER_0 (0x886aa8)
#define RGF_CAF_ICR (0x88946c) /* struct RGF_ICR */
+#define RGF_CAF_OSC_CONTROL (0x88afa4)
+ #define BIT_CAF_OSC_XTAL_EN BIT(0)
+#define RGF_CAF_PLL_LOCK_STATUS (0x88afec)
+ #define BIT_CAF_OSC_DIG_XTAL_STABLE BIT(0)
/* popular locations */
#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
@@ -461,10 +472,14 @@ struct wil6210_priv {
#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
-#define wil_to_pcie_dev(i) (&i->pdev->dev)
+__printf(2, 3)
void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
+__printf(2, 3)
void wil_err(struct wil6210_priv *wil, const char *fmt, ...);
+__printf(2, 3)
+void wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...);
+__printf(2, 3)
void wil_info(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_dbg(wil, fmt, arg...) do { \
netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
@@ -575,9 +590,10 @@ void wil_wdev_free(struct wil6210_priv *wil);
int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);
int wmi_pcp_stop(struct wil6210_priv *wil);
-void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid);
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
+ u16 reason_code, bool from_event);
-int wil_rx_init(struct wil6210_priv *wil);
+int wil_rx_init(struct wil6210_priv *wil, u16 size);
void wil_rx_fini(struct wil6210_priv *wil);
/* TX API */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 4311df982c60..63476c86cd0e 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -478,15 +478,15 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
void *d, int len)
{
struct wmi_disconnect_event *evt = d;
+ u16 reason_code = le16_to_cpu(evt->protocol_reason_status);
- wil_dbg_wmi(wil, "Disconnect %pM reason %d proto %d wmi\n",
- evt->bssid,
- evt->protocol_reason_status, evt->disconnect_reason);
+ wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
+ evt->bssid, reason_code, evt->disconnect_reason);
wil->sinfo_gen++;
mutex_lock(&wil->mutex);
- wil6210_disconnect(wil, evt->bssid);
+ wil6210_disconnect(wil, evt->bssid, reason_code, true);
mutex_unlock(&wil->mutex);
}
@@ -1025,7 +1025,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
struct wmi_cfg_rx_chain_cmd cmd = {
.action = WMI_RX_CHAIN_ADD,
.rx_sw_ring = {
- .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
+ .max_mpdu_size = cpu_to_le16(mtu_max + ETH_HLEN),
.ring_mem_base = cpu_to_le64(vring->pa),
.ring_size = cpu_to_le16(vring->size),
},