diff options
author | Tony Lindgren <tony@atomide.com> | 2015-11-12 20:58:21 +0300 |
---|---|---|
committer | Tony Lindgren <tony@atomide.com> | 2015-11-12 20:58:21 +0300 |
commit | ff6d03b9d9cad592320ce21e9b22befe56ebe6b6 (patch) | |
tree | da02d83177a107f92c446dbf7a93bdc1bac96cdf /drivers/net/wireless/ath | |
parent | 7ef71b70e18a82bb363905f72672317d0e1e8810 (diff) | |
parent | 469689a45f78505d864210b3a5d75404eb7f24ee (diff) | |
download | linux-ff6d03b9d9cad592320ce21e9b22befe56ebe6b6.tar.xz |
Merge branch 'x15-audio-fixes' into omap-for-v4.4/fixes
Diffstat (limited to 'drivers/net/wireless/ath')
70 files changed, 3593 insertions, 1091 deletions
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index df7c7616533b..7d3231acfb24 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -82,6 +82,16 @@ enum bmi_cmd_id { #define BMI_NVRAM_SEG_NAME_SZ 16 +#define BMI_PARAM_GET_EEPROM_BOARD_ID 0x10 + +#define ATH10K_BMI_BOARD_ID_FROM_OTP_MASK 0x7c00 +#define ATH10K_BMI_BOARD_ID_FROM_OTP_LSB 10 + +#define ATH10K_BMI_CHIP_ID_FROM_OTP_MASK 0x18000 +#define ATH10K_BMI_CHIP_ID_FROM_OTP_LSB 15 + +#define ATH10K_BMI_BOARD_ID_STATUS_MASK 0xff + struct bmi_cmd { __le32 id; /* enum bmi_cmd_id */ union { diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index cf28fbebaedc..edf3629288bc 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -274,7 +274,7 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, { struct ath10k *ar = ce_state->ar; struct ath10k_ce_ring *src_ring = ce_state->src_ring; - struct ce_desc *desc, *sdesc; + struct ce_desc *desc, sdesc; unsigned int nentries_mask = src_ring->nentries_mask; unsigned int sw_index = src_ring->sw_index; unsigned int write_index = src_ring->write_index; @@ -294,7 +294,6 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, desc = CE_SRC_RING_TO_DESC(src_ring->base_addr_owner_space, write_index); - sdesc = CE_SRC_RING_TO_DESC(src_ring->shadow_base, write_index); desc_flags |= SM(transfer_id, CE_DESC_FLAGS_META_DATA); @@ -303,11 +302,11 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, if (flags & CE_SEND_FLAG_BYTE_SWAP) desc_flags |= CE_DESC_FLAGS_BYTE_SWAP; - sdesc->addr = __cpu_to_le32(buffer); - sdesc->nbytes = __cpu_to_le16(nbytes); - sdesc->flags = __cpu_to_le16(desc_flags); + sdesc.addr = __cpu_to_le32(buffer); + sdesc.nbytes = __cpu_to_le16(nbytes); + sdesc.flags = __cpu_to_le16(desc_flags); - *desc = *sdesc; + *desc = sdesc; src_ring->per_transfer_context[write_index] = per_transfer_context; @@ -413,7 +412,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr) lockdep_assert_held(&ar_pci->ce_lock); if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0) - return -EIO; + return -ENOSPC; desc->addr = __cpu_to_le32(paddr); desc->nbytes = 0; @@ -579,17 +578,13 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, * The caller takes responsibility for any necessary locking. */ 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) + void **per_transfer_contextp) { struct ath10k_ce_ring *src_ring = ce_state->src_ring; u32 ctrl_addr = ce_state->ctrl_addr; struct ath10k *ar = ce_state->ar; unsigned int nentries_mask = src_ring->nentries_mask; unsigned int sw_index = src_ring->sw_index; - struct ce_desc *sdesc, *sbase; unsigned int read_index; if (src_ring->hw_index == sw_index) { @@ -614,15 +609,6 @@ int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state, if (read_index == sw_index) return -EIO; - sbase = src_ring->shadow_base; - sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index); - - /* Return data from completed source descriptor */ - *bufferp = __le32_to_cpu(sdesc->addr); - *nbytesp = __le16_to_cpu(sdesc->nbytes); - *transfer_idp = MS(__le16_to_cpu(sdesc->flags), - CE_DESC_FLAGS_META_DATA); - if (per_transfer_contextp) *per_transfer_contextp = src_ring->per_transfer_context[sw_index]; @@ -697,10 +683,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, } int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, - void **per_transfer_contextp, - u32 *bufferp, - unsigned int *nbytesp, - unsigned int *transfer_idp) + void **per_transfer_contextp) { struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -708,9 +691,7 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, spin_lock_bh(&ar_pci->ce_lock); ret = ath10k_ce_completed_send_next_nolock(ce_state, - per_transfer_contextp, - bufferp, nbytesp, - transfer_idp); + per_transfer_contextp); spin_unlock_bh(&ar_pci->ce_lock); return ret; @@ -940,27 +921,6 @@ ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id, src_ring->base_addr_ce_space_unaligned, CE_DESC_RING_ALIGN); - /* - * Also allocate a shadow src ring in regular - * mem to use for faster access. - */ - src_ring->shadow_base_unaligned = - kmalloc((nentries * sizeof(struct ce_desc) + - CE_DESC_RING_ALIGN), GFP_KERNEL); - if (!src_ring->shadow_base_unaligned) { - dma_free_coherent(ar->dev, - (nentries * sizeof(struct ce_desc) + - CE_DESC_RING_ALIGN), - src_ring->base_addr_owner_space, - src_ring->base_addr_ce_space); - kfree(src_ring); - return ERR_PTR(-ENOMEM); - } - - src_ring->shadow_base = PTR_ALIGN( - src_ring->shadow_base_unaligned, - CE_DESC_RING_ALIGN); - return src_ring; } @@ -1076,9 +1036,7 @@ 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, - 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]; @@ -1104,10 +1062,10 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, ce_state->src_sz_max = attr->src_sz_max; if (attr->src_nentries) - ce_state->send_cb = send_cb; + ce_state->send_cb = attr->send_cb; if (attr->dest_nentries) - ce_state->recv_cb = recv_cb; + ce_state->recv_cb = attr->recv_cb; if (attr->src_nentries) { ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr); @@ -1141,7 +1099,6 @@ void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id) struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; if (ce_state->src_ring) { - kfree(ce_state->src_ring->shadow_base_unaligned); dma_free_coherent(ar->dev, (ce_state->src_ring->nentries * sizeof(struct ce_desc) + diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index 5c903e15dd65..47b734ce7ecf 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -100,12 +100,6 @@ struct ath10k_ce_ring { /* CE address space */ u32 base_addr_ce_space; - /* - * Start of shadow copy of descriptors, within regular memory. - * Aligned to descriptor-size boundary. - */ - void *shadow_base_unaligned; - struct ce_desc *shadow_base; /* keep last */ void *per_transfer_context[0]; @@ -192,16 +186,10 @@ int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state, * Pops 1 completed send buffer from Source ring. */ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, - void **per_transfer_contextp, - u32 *bufferp, - unsigned int *nbytesp, - unsigned int *transfer_idp); + void **per_transfer_contextp); 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); + void **per_transfer_contextp); /*==================CE Engine Initialization=======================*/ @@ -209,9 +197,7 @@ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id, 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, - void (*send_cb)(struct ath10k_ce_pipe *), - void (*recv_cb)(struct ath10k_ce_pipe *)); + const struct ce_attr *attr); void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id); /*==================CE Engine Shutdown=======================*/ @@ -277,6 +263,9 @@ struct ce_attr { /* #entries in destination ring - Must be a power of 2 */ unsigned int dest_nentries; + + void (*send_cb)(struct ath10k_ce_pipe *); + void (*recv_cb)(struct ath10k_ce_pipe *); }; #define SR_BA_ADDRESS 0x0000 diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index b87b98617073..aa9bd92ac4ed 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -34,16 +34,19 @@ unsigned int ath10k_debug_mask; static unsigned int ath10k_cryptmode_param; static bool uart_print; static bool skip_otp; +static bool rawmode; module_param_named(debug_mask, ath10k_debug_mask, uint, 0644); module_param_named(cryptmode, ath10k_cryptmode_param, uint, 0644); module_param(uart_print, bool, 0644); module_param(skip_otp, bool, 0644); +module_param(rawmode, bool, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); MODULE_PARM_DESC(uart_print, "Uart target debugging"); MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software"); +MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath"); static const struct ath10k_hw_params ath10k_hw_params_list[] = { { @@ -54,6 +57,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .has_shifted_cc_wraparound = true, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { .dir = QCA988X_HW_2_0_FW_DIR, .fw = QCA988X_HW_2_0_FW_FILE, @@ -70,6 +74,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .uart_pin = 6, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { .dir = QCA6174_HW_2_1_FW_DIR, .fw = QCA6174_HW_2_1_FW_FILE, @@ -86,6 +91,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .uart_pin = 6, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { .dir = QCA6174_HW_3_0_FW_DIR, .fw = QCA6174_HW_3_0_FW_FILE, @@ -102,6 +108,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .uart_pin = 6, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { /* uses same binaries as hw3.0 */ .dir = QCA6174_HW_3_0_FW_DIR, @@ -120,6 +127,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .otp_exe_param = 0x00000700, .continuous_frag_desc = true, .channel_counters_freq_hz = 150000, + .max_probe_resp_desc_thres = 24, .fw = { .dir = QCA99X0_HW_2_0_FW_DIR, .fw = QCA99X0_HW_2_0_FW_FILE, @@ -129,6 +137,21 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ, }, }, + { + .id = QCA9377_HW_1_0_DEV_VERSION, + .name = "qca9377 hw1.0", + .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR, + .uart_pin = 7, + .otp_exe_param = 0, + .fw = { + .dir = QCA9377_HW_1_0_FW_DIR, + .fw = QCA9377_HW_1_0_FW_FILE, + .otp = QCA9377_HW_1_0_OTP_FILE, + .board = QCA9377_HW_1_0_BOARD_DATA_FILE, + .board_size = QCA9377_BOARD_DATA_SZ, + .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, + }, + }, }; static const char *const ath10k_core_fw_feature_str[] = { @@ -142,12 +165,18 @@ static const char *const ath10k_core_fw_feature_str[] = { [ATH10K_FW_FEATURE_IGNORE_OTP_RESULT] = "ignore-otp", [ATH10K_FW_FEATURE_NO_NWIFI_DECAP_4ADDR_PADDING] = "no-4addr-pad", [ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT] = "skip-clock-init", + [ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode", + [ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA] = "adaptive-cca", }; static unsigned int ath10k_core_get_fw_feature_str(char *buf, size_t buf_len, enum ath10k_fw_features feat) { + /* make sure that ath10k_core_fw_feature_str[] gets updated */ + BUILD_BUG_ON(ARRAY_SIZE(ath10k_core_fw_feature_str) != + ATH10K_FW_FEATURE_COUNT); + if (feat >= ARRAY_SIZE(ath10k_core_fw_feature_str) || WARN_ON(!ath10k_core_fw_feature_str[feat])) { return scnprintf(buf, buf_len, "bit%d", feat); @@ -435,6 +464,56 @@ out: return ret; } +static int ath10k_core_get_board_id_from_otp(struct ath10k *ar) +{ + u32 result, address; + u8 board_id, chip_id; + int ret; + + address = ar->hw_params.patch_load_addr; + + if (!ar->otp_data || !ar->otp_len) { + ath10k_warn(ar, + "failed to retrieve board id because of invalid otp\n"); + return -ENODATA; + } + + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "boot upload otp to 0x%x len %zd for board id\n", + address, ar->otp_len); + + ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len); + if (ret) { + ath10k_err(ar, "could not write otp for board id check: %d\n", + ret); + return ret; + } + + ret = ath10k_bmi_execute(ar, address, BMI_PARAM_GET_EEPROM_BOARD_ID, + &result); + if (ret) { + ath10k_err(ar, "could not execute otp for board id check: %d\n", + ret); + return ret; + } + + board_id = MS(result, ATH10K_BMI_BOARD_ID_FROM_OTP); + chip_id = MS(result, ATH10K_BMI_CHIP_ID_FROM_OTP); + + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "boot get otp board id result 0x%08x board_id %d chip_id %d\n", + result, board_id, chip_id); + + if ((result & ATH10K_BMI_BOARD_ID_STATUS_MASK) != 0) + return -EOPNOTSUPP; + + ar->id.bmi_ids_valid = true; + ar->id.bmi_board_id = board_id; + ar->id.bmi_chip_id = chip_id; + + return 0; +} + static int ath10k_download_and_run_otp(struct ath10k *ar) { u32 result, address = ar->hw_params.patch_load_addr; @@ -473,8 +552,8 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result); if (!(skip_otp || test_bit(ATH10K_FW_FEATURE_IGNORE_OTP_RESULT, - ar->fw_features)) - && result != 0) { + ar->fw_features)) && + result != 0) { ath10k_err(ar, "otp calibration failed: %d", result); return -EINVAL; } @@ -497,7 +576,7 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode) data_len = ar->firmware_len; mode_name = "normal"; ret = ath10k_swap_code_seg_configure(ar, - ATH10K_SWAP_CODE_SEG_BIN_TYPE_FW); + ATH10K_SWAP_CODE_SEG_BIN_TYPE_FW); if (ret) { ath10k_err(ar, "failed to configure fw code swap: %d\n", ret); @@ -505,8 +584,8 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode) } break; case ATH10K_FIRMWARE_MODE_UTF: - data = ar->testmode.utf->data; - data_len = ar->testmode.utf->size; + data = ar->testmode.utf_firmware_data; + data_len = ar->testmode.utf_firmware_len; mode_name = "utf"; break; default: @@ -528,11 +607,18 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode) return ret; } -static void ath10k_core_free_firmware_files(struct ath10k *ar) +static void ath10k_core_free_board_files(struct ath10k *ar) { if (!IS_ERR(ar->board)) release_firmware(ar->board); + ar->board = NULL; + ar->board_data = NULL; + ar->board_len = 0; +} + +static void ath10k_core_free_firmware_files(struct ath10k *ar) +{ if (!IS_ERR(ar->otp)) release_firmware(ar->otp); @@ -544,10 +630,6 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar) ath10k_swap_code_seg_release(ar); - ar->board = NULL; - ar->board_data = NULL; - ar->board_len = 0; - ar->otp = NULL; ar->otp_data = NULL; ar->otp_len = 0; @@ -557,7 +639,6 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar) ar->firmware_len = 0; ar->cal_file = NULL; - } static int ath10k_fetch_cal_file(struct ath10k *ar) @@ -579,68 +660,251 @@ static int ath10k_fetch_cal_file(struct ath10k *ar) return 0; } -static int ath10k_core_fetch_spec_board_file(struct ath10k *ar) +static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar) { - char filename[100]; - - scnprintf(filename, sizeof(filename), "board-%s-%s.bin", - ath10k_bus_str(ar->hif.bus), ar->spec_board_id); + if (!ar->hw_params.fw.board) { + ath10k_err(ar, "failed to find board file fw entry\n"); + return -EINVAL; + } - ar->board = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, filename); + ar->board = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); if (IS_ERR(ar->board)) return PTR_ERR(ar->board); ar->board_data = ar->board->data; ar->board_len = ar->board->size; - ar->spec_board_loaded = true; return 0; } -static int ath10k_core_fetch_generic_board_file(struct ath10k *ar) +static int ath10k_core_parse_bd_ie_board(struct ath10k *ar, + const void *buf, size_t buf_len, + const char *boardname) { - if (!ar->hw_params.fw.board) { - ath10k_err(ar, "failed to find board file fw entry\n"); - return -EINVAL; + const struct ath10k_fw_ie *hdr; + bool name_match_found; + int ret, board_ie_id; + size_t board_ie_len; + const void *board_ie_data; + + name_match_found = false; + + /* go through ATH10K_BD_IE_BOARD_ elements */ + while (buf_len > sizeof(struct ath10k_fw_ie)) { + hdr = buf; + board_ie_id = le32_to_cpu(hdr->id); + board_ie_len = le32_to_cpu(hdr->len); + board_ie_data = hdr->data; + + buf_len -= sizeof(*hdr); + buf += sizeof(*hdr); + + if (buf_len < ALIGN(board_ie_len, 4)) { + ath10k_err(ar, "invalid ATH10K_BD_IE_BOARD length: %zu < %zu\n", + buf_len, ALIGN(board_ie_len, 4)); + ret = -EINVAL; + goto out; + } + + switch (board_ie_id) { + case ATH10K_BD_IE_BOARD_NAME: + ath10k_dbg_dump(ar, ATH10K_DBG_BOOT, "board name", "", + board_ie_data, board_ie_len); + + if (board_ie_len != strlen(boardname)) + break; + + ret = memcmp(board_ie_data, boardname, strlen(boardname)); + if (ret) + break; + + name_match_found = true; + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "boot found match for name '%s'", + boardname); + break; + case ATH10K_BD_IE_BOARD_DATA: + if (!name_match_found) + /* no match found */ + break; + + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "boot found board data for '%s'", + boardname); + + ar->board_data = board_ie_data; + ar->board_len = board_ie_len; + + ret = 0; + goto out; + default: + ath10k_warn(ar, "unknown ATH10K_BD_IE_BOARD found: %d\n", + board_ie_id); + break; + } + + /* jump over the padding */ + board_ie_len = ALIGN(board_ie_len, 4); + + buf_len -= board_ie_len; + buf += board_ie_len; } - ar->board = ath10k_fetch_fw_file(ar, - ar->hw_params.fw.dir, - ar->hw_params.fw.board); + /* no match found */ + ret = -ENOENT; + +out: + return ret; +} + +static int ath10k_core_fetch_board_data_api_n(struct ath10k *ar, + const char *boardname, + const char *filename) +{ + size_t len, magic_len, ie_len; + struct ath10k_fw_ie *hdr; + const u8 *data; + int ret, ie_id; + + ar->board = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, filename); if (IS_ERR(ar->board)) return PTR_ERR(ar->board); - ar->board_data = ar->board->data; - ar->board_len = ar->board->size; - ar->spec_board_loaded = false; + data = ar->board->data; + len = ar->board->size; + + /* magic has extra null byte padded */ + magic_len = strlen(ATH10K_BOARD_MAGIC) + 1; + if (len < magic_len) { + ath10k_err(ar, "failed to find magic value in %s/%s, file too short: %zu\n", + ar->hw_params.fw.dir, filename, len); + ret = -EINVAL; + goto err; + } + + if (memcmp(data, ATH10K_BOARD_MAGIC, magic_len)) { + ath10k_err(ar, "found invalid board magic\n"); + ret = -EINVAL; + goto err; + } + + /* magic is padded to 4 bytes */ + magic_len = ALIGN(magic_len, 4); + if (len < magic_len) { + ath10k_err(ar, "failed: %s/%s too small to contain board data, len: %zu\n", + ar->hw_params.fw.dir, filename, len); + ret = -EINVAL; + goto err; + } + + data += magic_len; + len -= magic_len; + + while (len > sizeof(struct ath10k_fw_ie)) { + hdr = (struct ath10k_fw_ie *)data; + ie_id = le32_to_cpu(hdr->id); + ie_len = le32_to_cpu(hdr->len); + + len -= sizeof(*hdr); + data = hdr->data; + + if (len < ALIGN(ie_len, 4)) { + ath10k_err(ar, "invalid length for board ie_id %d ie_len %zu len %zu\n", + ie_id, ie_len, len); + ret = -EINVAL; + goto err; + } + + switch (ie_id) { + case ATH10K_BD_IE_BOARD: + ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len, + boardname); + if (ret == -ENOENT) + /* no match found, continue */ + break; + else if (ret) + /* there was an error, bail out */ + goto err; + + /* board data found */ + goto out; + } + + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + + len -= ie_len; + data += ie_len; + } + +out: + if (!ar->board_data || !ar->board_len) { + ath10k_err(ar, + "failed to fetch board data for %s from %s/%s\n", + ar->hw_params.fw.dir, boardname, filename); + ret = -ENODATA; + goto err; + } + + return 0; + +err: + ath10k_core_free_board_files(ar); + return ret; +} + +static int ath10k_core_create_board_name(struct ath10k *ar, char *name, + size_t name_len) +{ + if (ar->id.bmi_ids_valid) { + scnprintf(name, name_len, + "bus=%s,bmi-chip-id=%d,bmi-board-id=%d", + ath10k_bus_str(ar->hif.bus), + ar->id.bmi_chip_id, + ar->id.bmi_board_id); + goto out; + } + + scnprintf(name, name_len, + "bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x", + ath10k_bus_str(ar->hif.bus), + ar->id.vendor, ar->id.device, + ar->id.subsystem_vendor, ar->id.subsystem_device); + +out: + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using board name '%s'\n", name); return 0; } static int ath10k_core_fetch_board_file(struct ath10k *ar) { + char boardname[100]; int ret; - if (strlen(ar->spec_board_id) > 0) { - ret = ath10k_core_fetch_spec_board_file(ar); - if (ret) { - ath10k_info(ar, "failed to load spec board file, falling back to generic: %d\n", - ret); - goto generic; - } - - ath10k_dbg(ar, ATH10K_DBG_BOOT, "found specific board file for %s\n", - ar->spec_board_id); - return 0; + ret = ath10k_core_create_board_name(ar, boardname, sizeof(boardname)); + if (ret) { + ath10k_err(ar, "failed to create board name: %d", ret); + return ret; } -generic: - ret = ath10k_core_fetch_generic_board_file(ar); + ar->bd_api = 2; + ret = ath10k_core_fetch_board_data_api_n(ar, boardname, + ATH10K_BOARD_API2_FILE); + if (!ret) + goto success; + + ar->bd_api = 1; + ret = ath10k_core_fetch_board_data_api_1(ar); if (ret) { - ath10k_err(ar, "failed to fetch generic board data: %d\n", ret); + ath10k_err(ar, "failed to fetch board data\n"); return ret; } +success: + ath10k_dbg(ar, ATH10K_DBG_BOOT, "using board api %d\n", ar->bd_api); return 0; } @@ -872,12 +1136,6 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) /* calibration file is optional, don't check for any errors */ ath10k_fetch_cal_file(ar); - ret = ath10k_core_fetch_board_file(ar); - if (ret) { - ath10k_err(ar, "failed to fetch board file: %d\n", ret); - return ret; - } - ar->fw_api = 5; ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); @@ -1117,6 +1375,15 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) ar->htt.max_num_amsdu = ATH10K_HTT_MAX_NUM_AMSDU_DEFAULT; ar->htt.max_num_ampdu = ATH10K_HTT_MAX_NUM_AMPDU_DEFAULT; + if (rawmode) { + if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT, + ar->fw_features)) { + ath10k_err(ar, "rawmode = 1 requires support from firmware"); + return -EINVAL; + } + set_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags); + } + if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { ar->wmi.rx_decap_mode = ATH10K_HW_TXRX_RAW; @@ -1241,10 +1508,10 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) goto err; /* Some of of qca988x solutions are having global reset issue - * during target initialization. Bypassing PLL setting before - * downloading firmware and letting the SoC run on REF_CLK is - * fixing the problem. Corresponding firmware change is also needed - * to set the clock source once the target is initialized. + * during target initialization. Bypassing PLL setting before + * downloading firmware and letting the SoC run on REF_CLK is + * fixing the problem. Corresponding firmware change is also needed + * to set the clock source once the target is initialized. */ if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT, ar->fw_features)) { @@ -1478,6 +1745,19 @@ static int ath10k_core_probe_fw(struct ath10k *ar) goto err_power_down; } + ret = ath10k_core_get_board_id_from_otp(ar); + if (ret && ret != -EOPNOTSUPP) { + ath10k_err(ar, "failed to get board id from otp for qca99x0: %d\n", + ret); + return ret; + } + + ret = ath10k_core_fetch_board_file(ar); + if (ret) { + ath10k_err(ar, "failed to fetch board file: %d\n", ret); + goto err_free_firmware_files; + } + ret = ath10k_core_init_firmware_features(ar); if (ret) { ath10k_err(ar, "fatal problem with firmware features: %d\n", @@ -1605,6 +1885,7 @@ void ath10k_core_unregister(struct ath10k *ar) ath10k_testmode_destroy(ar); ath10k_core_free_firmware_files(ar); + ath10k_core_free_board_files(ar); ath10k_debug_unregister(ar); } @@ -1635,6 +1916,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, ar->hw_values = &qca988x_values; break; case ATH10K_HW_QCA6174: + case ATH10K_HW_QCA9377: ar->regs = &qca6174_regs; ar->hw_values = &qca6174_values; break; @@ -1714,6 +1996,7 @@ void ath10k_core_destroy(struct ath10k *ar) destroy_workqueue(ar->workqueue_aux); ath10k_debug_destroy(ar); + ath10k_wmi_free_host_mem(ar); ath10k_mac_destroy(ar); } EXPORT_SYMBOL(ath10k_core_destroy); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 12542144fe12..018c64f4fd25 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -214,6 +214,7 @@ struct ath10k_fw_stats_pdev { s32 hw_queued; s32 hw_reaped; s32 underrun; + u32 hw_paused; s32 tx_abort; s32 mpdus_requed; u32 tx_ko; @@ -226,6 +227,16 @@ struct ath10k_fw_stats_pdev { u32 pdev_resets; u32 phy_underrun; u32 txop_ovf; + u32 seq_posted; + u32 seq_failed_queueing; + u32 seq_completed; + u32 seq_restarted; + u32 mu_seq_posted; + u32 mpdus_sw_flush; + u32 mpdus_hw_filter; + u32 mpdus_truncated; + u32 mpdus_ack_failed; + u32 mpdus_expired; /* PDEV RX stats */ s32 mid_ppdu_route_change; @@ -242,6 +253,7 @@ struct ath10k_fw_stats_pdev { s32 phy_errs; s32 phy_err_drop; s32 mpdu_errs; + s32 rx_ovfl_errs; }; struct ath10k_fw_stats { @@ -250,6 +262,30 @@ struct ath10k_fw_stats { struct list_head peers; }; +#define ATH10K_TPC_TABLE_TYPE_FLAG 1 +#define ATH10K_TPC_PREAM_TABLE_END 0xFFFF + +struct ath10k_tpc_table { + u32 pream_idx[WMI_TPC_RATE_MAX]; + u8 rate_code[WMI_TPC_RATE_MAX]; + char tpc_value[WMI_TPC_RATE_MAX][WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE]; +}; + +struct ath10k_tpc_stats { + u32 reg_domain; + u32 chan_freq; + u32 phy_mode; + u32 twice_antenna_reduction; + u32 twice_max_rd_power; + s32 twice_antenna_gain; + u32 power_limit; + u32 num_tx_chain; + u32 ctl; + u32 rate_max; + u8 flag[WMI_TPC_FLAG]; + struct ath10k_tpc_table tpc_table[WMI_TPC_FLAG]; +}; + struct ath10k_dfs_stats { u32 phy_errors; u32 pulses_total; @@ -378,6 +414,11 @@ struct ath10k_debug { struct ath10k_dfs_stats dfs_stats; struct ath_dfs_pool_stats dfs_pool_stats; + /* used for tpc-dump storage, protected by data-lock */ + struct ath10k_tpc_stats *tpc_stats; + + struct completion tpc_complete; + /* protected by conf_mutex */ u32 fw_dbglog_mask; u32 fw_dbglog_level; @@ -468,6 +509,9 @@ enum ath10k_fw_features { */ ATH10K_FW_FEATURE_RAW_MODE_SUPPORT = 10, + /* Firmware Supports Adaptive CCA*/ + ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA = 11, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; @@ -612,6 +656,11 @@ struct ath10k { u32 channel_counters_freq_hz; + /* Mgmt tx descriptors threshold for limiting probe response + * frames. + */ + u32 max_probe_resp_desc_thres; + struct ath10k_hw_params_fw { const char *dir; const char *fw; @@ -642,10 +691,19 @@ struct ath10k { struct ath10k_swap_code_seg_info *firmware_swap_code_seg_info; } swap; - char spec_board_id[100]; - bool spec_board_loaded; + struct { + u32 vendor; + u32 device; + u32 subsystem_vendor; + u32 subsystem_device; + + bool bmi_ids_valid; + u8 bmi_board_id; + u8 bmi_chip_id; + } id; int fw_api; + int bd_api; enum ath10k_cal_mode cal_mode; struct { @@ -680,15 +738,13 @@ struct ath10k { bool monitor_started; unsigned int filter_flags; unsigned long dev_flags; - u32 dfs_block_radar_events; + bool dfs_block_radar_events; /* protected by conf_mutex */ bool radar_enabled; int num_started_vdevs; /* Protected by conf-mutex */ - u8 supp_tx_chainmask; - u8 supp_rx_chainmask; u8 cfg_tx_chainmask; u8 cfg_rx_chainmask; @@ -771,9 +827,12 @@ struct ath10k { struct { /* protected by conf_mutex */ const struct firmware *utf; + char utf_version[32]; + const void *utf_firmware_data; + size_t utf_firmware_len; DECLARE_BITMAP(orig_fw_features, ATH10K_FW_FEATURE_COUNT); enum ath10k_fw_wmi_op_version orig_wmi_op_version; - + enum ath10k_fw_wmi_op_version op_version; /* protected by data_lock */ bool utf_monitor; } testmode; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index bf033f46f8aa..6cc1aa3449c8 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -125,19 +125,25 @@ EXPORT_SYMBOL(ath10k_info); void ath10k_print_driver_info(struct ath10k *ar) { char fw_features[128] = {}; + char boardinfo[100]; ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features)); - ath10k_info(ar, "%s (0x%08x, 0x%08x%s%s%s) fw %s api %d htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d features %s\n", + if (ar->id.bmi_ids_valid) + scnprintf(boardinfo, sizeof(boardinfo), "bmi %d:%d", + ar->id.bmi_chip_id, ar->id.bmi_board_id); + else + scnprintf(boardinfo, sizeof(boardinfo), "sub %04x:%04x", + ar->id.subsystem_vendor, ar->id.subsystem_device); + + ath10k_info(ar, "%s (0x%08x, 0x%08x %s) fw %s fwapi %d bdapi %d htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d features %s\n", ar->hw_params.name, ar->target_version, ar->chip_id, - (strlen(ar->spec_board_id) > 0 ? ", " : ""), - ar->spec_board_id, - (strlen(ar->spec_board_id) > 0 && !ar->spec_board_loaded - ? " fallback" : ""), + boardinfo, ar->hw->wiphy->fw_version, ar->fw_api, + ar->bd_api, ar->htt.target_version_major, ar->htt.target_version_minor, ar->wmi.op_version, @@ -285,28 +291,6 @@ static void ath10k_debug_fw_stats_reset(struct ath10k *ar) spin_unlock_bh(&ar->data_lock); } -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; -} - -static size_t ath10k_debug_fw_stats_num_vdevs(struct list_head *head) -{ - struct ath10k_fw_stats_vdev *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 = {}; @@ -343,8 +327,8 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb) goto free; } - num_peers = ath10k_debug_fw_stats_num_peers(&ar->debug.fw_stats.peers); - num_vdevs = ath10k_debug_fw_stats_num_vdevs(&ar->debug.fw_stats.vdevs); + num_peers = ath10k_wmi_fw_stats_num_peers(&ar->debug.fw_stats.peers); + num_vdevs = ath10k_wmi_fw_stats_num_vdevs(&ar->debug.fw_stats.vdevs); is_start = (list_empty(&ar->debug.fw_stats.pdevs) && !list_empty(&stats.pdevs)); is_end = (!list_empty(&ar->debug.fw_stats.pdevs) && @@ -429,240 +413,6 @@ static int ath10k_debug_fw_stats_request(struct ath10k *ar) 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_vdev *vdev; - const struct ath10k_fw_stats_peer *peer; - size_t num_peers; - size_t num_vdevs; - int i; - - 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); - num_vdevs = ath10k_debug_fw_stats_num_vdevs(&fw_stats->vdevs); - - len += scnprintf(buf + len, buf_len - len, "\n"); - len += scnprintf(buf + len, buf_len - len, "%30s\n", - "ath10k PDEV stats"); - len += scnprintf(buf + len, buf_len - len, "%30s\n\n", - "================="); - - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Channel noise floor", pdev->ch_noise_floor); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "Channel TX power", pdev->chan_tx_power); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "TX frame count", pdev->tx_frame_count); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "RX frame count", pdev->rx_frame_count); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "RX clear count", pdev->rx_clear_count); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "Cycle count", pdev->cycle_count); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "PHY error count", pdev->phy_err_count); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "RTS bad count", pdev->rts_bad); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "RTS good count", pdev->rts_good); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "FCS bad count", pdev->fcs_bad); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "No beacon count", pdev->no_beacons); - len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", - "MIB int count", pdev->mib_int_count); - - len += scnprintf(buf + len, buf_len - len, "\n"); - len += scnprintf(buf + len, buf_len - len, "%30s\n", - "ath10k PDEV TX stats"); - len += scnprintf(buf + len, buf_len - len, "%30s\n\n", - "================="); - - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "HTT cookies queued", pdev->comp_queued); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "HTT cookies disp.", pdev->comp_delivered); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "MSDU queued", pdev->msdu_enqued); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "MPDU queued", pdev->mpdu_enqued); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "MSDUs dropped", pdev->wmm_drop); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Local enqued", pdev->local_enqued); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Local freed", pdev->local_freed); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "HW queued", pdev->hw_queued); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "PPDUs reaped", pdev->hw_reaped); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Num underruns", pdev->underrun); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "PPDUs cleaned", pdev->tx_abort); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "MPDUs requed", pdev->mpdus_requed); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Excessive retries", pdev->tx_ko); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "HW rate", pdev->data_rc); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Sched self tiggers", pdev->self_triggers); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Dropped due to SW retries", - pdev->sw_retry_failure); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Illegal rate phy errors", - pdev->illgl_rate_phy_err); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Pdev continous xretry", pdev->pdev_cont_xretry); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "TX timeout", pdev->pdev_tx_timeout); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "PDEV resets", pdev->pdev_resets); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "PHY underrun", pdev->phy_underrun); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "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", - "ath10k PDEV RX stats"); - len += scnprintf(buf + len, buf_len - len, "%30s\n\n", - "================="); - - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Mid PPDU route change", - pdev->mid_ppdu_route_change); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Tot. number of statuses", pdev->status_rcvd); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Extra frags on rings 0", pdev->r0_frags); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Extra frags on rings 1", pdev->r1_frags); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Extra frags on rings 2", pdev->r2_frags); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Extra frags on rings 3", pdev->r3_frags); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "MSDUs delivered to HTT", pdev->htt_msdus); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "MPDUs delivered to HTT", pdev->htt_mpdus); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "MSDUs delivered to stack", pdev->loc_msdus); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "MPDUs delivered to stack", pdev->loc_mpdus); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "Oversized AMSUs", pdev->oversize_amsdu); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "PHY errors", pdev->phy_errs); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "PHY errors drops", pdev->phy_err_drop); - len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", - "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs); - - len += scnprintf(buf + len, buf_len - len, "\n"); - len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n", - "ath10k VDEV stats", num_vdevs); - len += scnprintf(buf + len, buf_len - len, "%30s\n\n", - "================="); - - list_for_each_entry(vdev, &fw_stats->vdevs, list) { - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "vdev id", vdev->vdev_id); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "beacon snr", vdev->beacon_snr); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "data snr", vdev->data_snr); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "num rx frames", vdev->num_rx_frames); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "num rts fail", vdev->num_rts_fail); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "num rts success", vdev->num_rts_success); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "num rx err", vdev->num_rx_err); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "num rx discard", vdev->num_rx_discard); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "num tx not acked", vdev->num_tx_not_acked); - - for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++) - len += scnprintf(buf + len, buf_len - len, - "%25s [%02d] %u\n", - "num tx frames", i, - vdev->num_tx_frames[i]); - - for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++) - len += scnprintf(buf + len, buf_len - len, - "%25s [%02d] %u\n", - "num tx frames retries", i, - vdev->num_tx_frames_retries[i]); - - for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++) - len += scnprintf(buf + len, buf_len - len, - "%25s [%02d] %u\n", - "num tx frames failures", i, - vdev->num_tx_frames_failures[i]); - - for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++) - len += scnprintf(buf + len, buf_len - len, - "%25s [%02d] 0x%08x\n", - "tx rate history", i, - vdev->tx_rate_history[i]); - - for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++) - len += scnprintf(buf + len, buf_len - len, - "%25s [%02d] %u\n", - "beacon rssi history", i, - vdev->beacon_rssi_history[i]); - - len += scnprintf(buf + len, buf_len - len, "\n"); - } - - len += scnprintf(buf + len, buf_len - len, "\n"); - 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", - "================="); - - list_for_each_entry(peer, &fw_stats->peers, list) { - len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", - "Peer MAC address", peer->peer_macaddr); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "Peer RSSI", peer->peer_rssi); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "Peer TX rate", peer->peer_tx_rate); - len += scnprintf(buf + len, buf_len - len, "%30s %u\n", - "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) - buf[len - 1] = 0; - else - buf[len] = 0; -} - static int ath10k_fw_stats_open(struct inode *inode, struct file *file) { struct ath10k *ar = inode->i_private; @@ -688,7 +438,12 @@ static int ath10k_fw_stats_open(struct inode *inode, struct file *file) goto err_free; } - ath10k_fw_stats_fill(ar, &ar->debug.fw_stats, buf); + ret = ath10k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, buf); + if (ret) { + ath10k_warn(ar, "failed to fill fw stats: %d\n", ret); + goto err_free; + } + file->private_data = buf; mutex_unlock(&ar->conf_mutex); @@ -1843,6 +1598,233 @@ static const struct file_operations fops_nf_cal_period = { .llseek = default_llseek, }; +#define ATH10K_TPC_CONFIG_BUF_SIZE (1024 * 1024) + +static int ath10k_debug_tpc_stats_request(struct ath10k *ar) +{ + int ret; + unsigned long time_left; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->debug.tpc_complete); + + ret = ath10k_wmi_pdev_get_tpc_config(ar, WMI_TPC_CONFIG_PARAM); + if (ret) { + ath10k_warn(ar, "failed to request tpc config: %d\n", ret); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->debug.tpc_complete, + 1 * HZ); + if (time_left == 0) + return -ETIMEDOUT; + + return 0; +} + +void ath10k_debug_tpc_stats_process(struct ath10k *ar, + struct ath10k_tpc_stats *tpc_stats) +{ + spin_lock_bh(&ar->data_lock); + + kfree(ar->debug.tpc_stats); + ar->debug.tpc_stats = tpc_stats; + complete(&ar->debug.tpc_complete); + + spin_unlock_bh(&ar->data_lock); +} + +static void ath10k_tpc_stats_print(struct ath10k_tpc_stats *tpc_stats, + unsigned int j, char *buf, unsigned int *len) +{ + unsigned int i, buf_len; + static const char table_str[][5] = { "CDD", + "STBC", + "TXBF" }; + static const char pream_str[][6] = { "CCK", + "OFDM", + "HT20", + "HT40", + "VHT20", + "VHT40", + "VHT80", + "HTCUP" }; + + buf_len = ATH10K_TPC_CONFIG_BUF_SIZE; + *len += scnprintf(buf + *len, buf_len - *len, + "********************************\n"); + *len += scnprintf(buf + *len, buf_len - *len, + "******************* %s POWER TABLE ****************\n", + table_str[j]); + *len += scnprintf(buf + *len, buf_len - *len, + "********************************\n"); + *len += scnprintf(buf + *len, buf_len - *len, + "No. Preamble Rate_code tpc_value1 tpc_value2 tpc_value3\n"); + + for (i = 0; i < tpc_stats->rate_max; i++) { + *len += scnprintf(buf + *len, buf_len - *len, + "%8d %s 0x%2x %s\n", i, + pream_str[tpc_stats->tpc_table[j].pream_idx[i]], + tpc_stats->tpc_table[j].rate_code[i], + tpc_stats->tpc_table[j].tpc_value[i]); + } + + *len += scnprintf(buf + *len, buf_len - *len, + "***********************************\n"); +} + +static void ath10k_tpc_stats_fill(struct ath10k *ar, + struct ath10k_tpc_stats *tpc_stats, + char *buf) +{ + unsigned int len, j, buf_len; + + len = 0; + buf_len = ATH10K_TPC_CONFIG_BUF_SIZE; + + spin_lock_bh(&ar->data_lock); + + if (!tpc_stats) { + ath10k_warn(ar, "failed to get tpc stats\n"); + goto unlock; + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, + "*************************************\n"); + len += scnprintf(buf + len, buf_len - len, + "TPC config for channel %4d mode %d\n", + tpc_stats->chan_freq, + tpc_stats->phy_mode); + len += scnprintf(buf + len, buf_len - len, + "*************************************\n"); + len += scnprintf(buf + len, buf_len - len, + "CTL = 0x%2x Reg. Domain = %2d\n", + tpc_stats->ctl, + tpc_stats->reg_domain); + len += scnprintf(buf + len, buf_len - len, + "Antenna Gain = %2d Reg. Max Antenna Gain = %2d\n", + tpc_stats->twice_antenna_gain, + tpc_stats->twice_antenna_reduction); + len += scnprintf(buf + len, buf_len - len, + "Power Limit = %2d Reg. Max Power = %2d\n", + tpc_stats->power_limit, + tpc_stats->twice_max_rd_power / 2); + len += scnprintf(buf + len, buf_len - len, + "Num tx chains = %2d Num supported rates = %2d\n", + tpc_stats->num_tx_chain, + tpc_stats->rate_max); + + for (j = 0; j < tpc_stats->num_tx_chain ; j++) { + switch (j) { + case WMI_TPC_TABLE_TYPE_CDD: + if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) { + len += scnprintf(buf + len, buf_len - len, + "CDD not supported\n"); + break; + } + + ath10k_tpc_stats_print(tpc_stats, j, buf, &len); + break; + case WMI_TPC_TABLE_TYPE_STBC: + if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) { + len += scnprintf(buf + len, buf_len - len, + "STBC not supported\n"); + break; + } + + ath10k_tpc_stats_print(tpc_stats, j, buf, &len); + break; + case WMI_TPC_TABLE_TYPE_TXBF: + if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) { + len += scnprintf(buf + len, buf_len - len, + "TXBF not supported\n***************************\n"); + break; + } + + ath10k_tpc_stats_print(tpc_stats, j, buf, &len); + break; + default: + len += scnprintf(buf + len, buf_len - len, + "Invalid Type\n"); + break; + } + } + +unlock: + spin_unlock_bh(&ar->data_lock); + + if (len >= buf_len) + buf[len - 1] = 0; + else + buf[len] = 0; +} + +static int ath10k_tpc_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_TPC_CONFIG_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + ret = ath10k_debug_tpc_stats_request(ar); + if (ret) { + ath10k_warn(ar, "failed to request tpc config stats: %d\n", + ret); + goto err_free; + } + + ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf); + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_tpc_stats_release(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath10k_tpc_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_tpc_stats = { + .open = ath10k_tpc_stats_open, + .release = ath10k_tpc_stats_release, + .read = ath10k_tpc_stats_read, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_start(struct ath10k *ar) { int ret; @@ -2111,6 +2093,8 @@ void ath10k_debug_destroy(struct ath10k *ar) ar->debug.fw_crash_data = NULL; ath10k_debug_fw_stats_reset(ar); + + kfree(ar->debug.tpc_stats); } int ath10k_debug_register(struct ath10k *ar) @@ -2127,6 +2111,7 @@ int ath10k_debug_register(struct ath10k *ar) INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork, ath10k_debug_htt_stats_dwork); + init_completion(&ar->debug.tpc_complete); init_completion(&ar->debug.fw_stats_complete); debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar, @@ -2195,6 +2180,9 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("quiet_period", S_IRUGO | S_IWUSR, ar->debug.debugfs_phy, ar, &fops_quiet_period); + debugfs_create_file("tpc_stats", S_IRUSR, + ar->debug.debugfs_phy, ar, &fops_tpc_stats); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 53bd6a19eab6..7de780c4ec8d 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -55,6 +55,9 @@ enum ath10k_dbg_aggr_mode { ATH10K_DBG_AGGR_MODE_MAX, }; +/* FIXME: How to calculate the buffer size sanely? */ +#define ATH10K_FW_STATS_BUF_SIZE (1024*1024) + extern unsigned int ath10k_debug_mask; __printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...); @@ -70,6 +73,8 @@ void ath10k_debug_destroy(struct ath10k *ar); int ath10k_debug_register(struct ath10k *ar); void ath10k_debug_unregister(struct ath10k *ar); void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb); +void ath10k_debug_tpc_stats_process(struct ath10k *ar, + struct ath10k_tpc_stats *tpc_stats); struct ath10k_fw_crash_data * ath10k_debug_get_new_fw_crash_data(struct ath10k *ar); @@ -117,6 +122,12 @@ static inline void ath10k_debug_fw_stats_process(struct ath10k *ar, { } +static inline void ath10k_debug_tpc_stats_process(struct ath10k *ar, + struct ath10k_tpc_stats *tpc_stats) +{ + kfree(tpc_stats); +} + static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len) { diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h index 0c92e0251e84..89e7076c919f 100644 --- a/drivers/net/wireless/ath/ath10k/hif.h +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -30,13 +30,6 @@ struct ath10k_hif_sg_item { u16 len; }; -struct ath10k_hif_cb { - int (*tx_completion)(struct ath10k *ar, - struct sk_buff *wbuf); - int (*rx_completion)(struct ath10k *ar, - struct sk_buff *wbuf); -}; - struct ath10k_hif_ops { /* send a scatter-gather list to the target */ int (*tx_sg)(struct ath10k *ar, u8 pipe_id, @@ -65,8 +58,7 @@ struct ath10k_hif_ops { void (*stop)(struct ath10k *ar); int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id, - u8 *ul_pipe, u8 *dl_pipe, - int *ul_is_polled, int *dl_is_polled); + u8 *ul_pipe, u8 *dl_pipe); void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe); @@ -80,9 +72,6 @@ struct ath10k_hif_ops { */ void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force); - void (*set_callbacks)(struct ath10k *ar, - struct ath10k_hif_cb *callbacks); - u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id); u32 (*read32)(struct ath10k *ar, u32 address); @@ -142,13 +131,10 @@ static inline void ath10k_hif_stop(struct ath10k *ar) static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, - u8 *ul_pipe, u8 *dl_pipe, - int *ul_is_polled, - int *dl_is_polled) + u8 *ul_pipe, u8 *dl_pipe) { return ar->hif.ops->map_service_to_pipe(ar, service_id, - ul_pipe, dl_pipe, - ul_is_polled, dl_is_polled); + ul_pipe, dl_pipe); } static inline void ath10k_hif_get_default_pipe(struct ath10k *ar, @@ -163,12 +149,6 @@ static inline void ath10k_hif_send_complete_check(struct ath10k *ar, ar->hif.ops->send_complete_check(ar, pipe_id, force); } -static inline void ath10k_hif_set_callbacks(struct ath10k *ar, - struct ath10k_hif_cb *callbacks) -{ - ar->hif.ops->set_callbacks(ar, callbacks); -} - static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar, u8 pipe_id) { diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 32d9ff1b19dc..5b3c6bcf9598 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -23,16 +23,6 @@ /* Send */ /********/ -static inline void ath10k_htc_send_complete_check(struct ath10k_htc_ep *ep, - int force) -{ - /* - * Check whether HIF has any prior sends that have finished, - * have not had the post-processing done. - */ - ath10k_hif_send_complete_check(ep->htc->ar, ep->ul_pipe_id, force); -} - static void ath10k_htc_control_tx_complete(struct ath10k *ar, struct sk_buff *skb) { @@ -181,24 +171,22 @@ err_pull: return ret; } -static int ath10k_htc_tx_completion_handler(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb) { struct ath10k_htc *htc = &ar->htc; struct ath10k_skb_cb *skb_cb; struct ath10k_htc_ep *ep; if (WARN_ON_ONCE(!skb)) - return 0; + return; 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 */ - - return 0; } +EXPORT_SYMBOL(ath10k_htc_tx_completion_handler); /***********/ /* Receive */ @@ -304,8 +292,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc, return status; } -static int ath10k_htc_rx_completion_handler(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb) { int status = 0; struct ath10k_htc *htc = &ar->htc; @@ -326,21 +313,11 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, ath10k_warn(ar, "HTC Rx: invalid eid %d\n", eid); ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad header", "", hdr, sizeof(*hdr)); - status = -EINVAL; goto out; } ep = &htc->endpoint[eid]; - /* - * If this endpoint that received a message from the target has - * a to-target HIF pipe whose send completions are polled rather - * than interrupt-driven, this is a good point to ask HIF to check - * whether it has any completed sends to handle. - */ - if (ep->ul_is_polled) - ath10k_htc_send_complete_check(ep, 1); - payload_len = __le16_to_cpu(hdr->len); if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) { @@ -348,7 +325,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, payload_len + sizeof(*hdr)); ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", "", hdr, sizeof(*hdr)); - status = -EINVAL; goto out; } @@ -358,7 +334,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, skb->len, payload_len); ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", "", hdr, sizeof(*hdr)); - status = -EINVAL; goto out; } @@ -374,7 +349,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, (trailer_len > payload_len)) { ath10k_warn(ar, "Invalid trailer length: %d\n", trailer_len); - status = -EPROTO; goto out; } @@ -407,7 +381,6 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, * sending unsolicited messages on the ep 0 */ ath10k_warn(ar, "HTC rx ctrl still processing\n"); - status = -EINVAL; complete(&htc->ctl_resp); goto out; } @@ -439,9 +412,8 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, skb = NULL; out: kfree_skb(skb); - - return status; } +EXPORT_SYMBOL(ath10k_htc_rx_completion_handler); static void ath10k_htc_control_rx_complete(struct ath10k *ar, struct sk_buff *skb) @@ -767,9 +739,7 @@ setup: status = ath10k_hif_map_service_to_pipe(htc->ar, ep->service_id, &ep->ul_pipe_id, - &ep->dl_pipe_id, - &ep->ul_is_polled, - &ep->dl_is_polled); + &ep->dl_pipe_id); if (status) return status; @@ -778,10 +748,6 @@ setup: htc_service_name(ep->service_id), ep->ul_pipe_id, ep->dl_pipe_id, ep->eid); - ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot htc ep %d ul polled %d dl polled %d\n", - ep->eid, ep->ul_is_polled, ep->dl_is_polled); - if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { ep->tx_credit_flow_enabled = false; ath10k_dbg(ar, ATH10K_DBG_BOOT, @@ -841,7 +807,6 @@ int ath10k_htc_start(struct ath10k_htc *htc) /* registered target arrival callback from the HIF layer */ int ath10k_htc_init(struct ath10k *ar) { - struct ath10k_hif_cb htc_callbacks; struct ath10k_htc_ep *ep = NULL; struct ath10k_htc *htc = &ar->htc; @@ -849,15 +814,11 @@ int ath10k_htc_init(struct ath10k *ar) ath10k_htc_reset_endpoint_states(htc); - /* setup HIF layer callbacks */ - htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler; - htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler; htc->ar = ar; /* Get HIF default pipe for HTC message exchange */ ep = &htc->endpoint[ATH10K_HTC_EP_0]; - ath10k_hif_set_callbacks(ar, &htc_callbacks); ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id); init_completion(&htc->ctl_resp); diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index 527179c0edce..e70aa38e6e05 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -312,8 +312,6 @@ struct ath10k_htc_ep { int max_ep_message_len; u8 ul_pipe_id; u8 dl_pipe_id; - int ul_is_polled; /* call HIF to get tx completions */ - int dl_is_polled; /* call HIF to fetch rx (not implemented) */ u8 seq_no; /* for debugging */ int tx_credits; @@ -355,5 +353,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, struct sk_buff *packet); struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size); +void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb); +void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 573187512895..2bad50e520b5 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1485,9 +1485,9 @@ struct ath10k_htt { spinlock_t tx_lock; int max_num_pending_tx; int num_pending_tx; + int num_pending_mgmt_tx; struct idr pending_tx; wait_queue_head_t empty_tx_wq; - struct dma_pool *tx_pool; /* set if host-fw communication goes haywire * used to avoid further failures */ @@ -1508,6 +1508,11 @@ struct ath10k_htt { dma_addr_t paddr; struct htt_msdu_ext_desc *vaddr; } frag_desc; + + struct { + dma_addr_t paddr; + struct ath10k_htt_txbuf *vaddr; + } txbuf; }; #define RX_HTT_HDR_STATUS_LEN 64 @@ -1586,8 +1591,9 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt); int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, u8 max_subfrms_ampdu, u8 max_subfrms_amsdu); +void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb); -void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt); +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc); int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb); void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id); int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *); diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 1b7a04366256..6060dda4e910 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -643,6 +643,8 @@ struct amsdu_subframe_hdr { __be16 len; } __packed; +#define GROUP_ID_IS_SU_MIMO(x) ((x) == 0 || (x) == 63) + static void ath10k_htt_rx_h_rates(struct ath10k *ar, struct ieee80211_rx_status *status, struct htt_rx_desc *rxd) @@ -650,6 +652,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, struct ieee80211_supported_band *sband; u8 cck, rate, bw, sgi, mcs, nss; u8 preamble = 0; + u8 group_id; u32 info1, info2, info3; info1 = __le32_to_cpu(rxd->ppdu_start.info1); @@ -692,10 +695,50 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, case HTT_RX_VHT_WITH_TXBF: /* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3 TODO check this */ - mcs = (info3 >> 4) & 0x0F; - nss = ((info2 >> 10) & 0x07) + 1; bw = info2 & 3; sgi = info3 & 1; + group_id = (info2 >> 4) & 0x3F; + + if (GROUP_ID_IS_SU_MIMO(group_id)) { + mcs = (info3 >> 4) & 0x0F; + nss = ((info2 >> 10) & 0x07) + 1; + } else { + /* Hardware doesn't decode VHT-SIG-B into Rx descriptor + * so it's impossible to decode MCS. Also since + * firmware consumes Group Id Management frames host + * has no knowledge regarding group/user position + * mapping so it's impossible to pick the correct Nsts + * from VHT-SIG-A1. + * + * Bandwidth and SGI are valid so report the rateinfo + * on best-effort basis. + */ + mcs = 0; + nss = 1; + } + + if (mcs > 0x09) { + ath10k_warn(ar, "invalid MCS received %u\n", mcs); + ath10k_warn(ar, "rxd %08x mpdu start %08x %08x msdu start %08x %08x ppdu start %08x %08x %08x %08x %08x\n", + __le32_to_cpu(rxd->attention.flags), + __le32_to_cpu(rxd->mpdu_start.info0), + __le32_to_cpu(rxd->mpdu_start.info1), + __le32_to_cpu(rxd->msdu_start.common.info0), + __le32_to_cpu(rxd->msdu_start.common.info1), + rxd->ppdu_start.info0, + __le32_to_cpu(rxd->ppdu_start.info1), + __le32_to_cpu(rxd->ppdu_start.info2), + __le32_to_cpu(rxd->ppdu_start.info3), + __le32_to_cpu(rxd->ppdu_start.info4)); + + ath10k_warn(ar, "msdu end %08x mpdu end %08x\n", + __le32_to_cpu(rxd->msdu_end.common.info0), + __le32_to_cpu(rxd->mpdu_end.info0)); + + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, + "rx desc msdu payload: ", + rxd->msdu_payload, 50); + } status->rate_idx = mcs; status->vht_nss = nss; @@ -2082,6 +2125,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) /* Free the indication buffer */ dev_kfree_skb_any(skb); } +EXPORT_SYMBOL(ath10k_htt_t2h_msg_handler); static void ath10k_htt_txrx_compl_task(unsigned long ptr) { diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 43aa5e2d1b87..16823970dbfd 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -22,22 +22,28 @@ #include "txrx.h" #include "debug.h" -void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc) { + if (limit_mgmt_desc) + htt->num_pending_mgmt_tx--; + htt->num_pending_tx--; if (htt->num_pending_tx == htt->max_num_pending_tx - 1) ath10k_mac_tx_unlock(htt->ar, ATH10K_TX_PAUSE_Q_FULL); } -static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, + bool limit_mgmt_desc) { spin_lock_bh(&htt->tx_lock); - __ath10k_htt_tx_dec_pending(htt); + __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); spin_unlock_bh(&htt->tx_lock); } -static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt) +static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt, + bool limit_mgmt_desc, bool is_probe_resp) { + struct ath10k *ar = htt->ar; int ret = 0; spin_lock_bh(&htt->tx_lock); @@ -47,6 +53,15 @@ static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt) goto exit; } + if (limit_mgmt_desc) { + if (is_probe_resp && (htt->num_pending_mgmt_tx > + ar->hw_params.max_probe_resp_desc_thres)) { + ret = -EBUSY; + goto exit; + } + htt->num_pending_mgmt_tx++; + } + htt->num_pending_tx++; if (htt->num_pending_tx == htt->max_num_pending_tx) ath10k_mac_tx_lock(htt->ar, ATH10K_TX_PAUSE_Q_FULL); @@ -93,9 +108,12 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) spin_lock_init(&htt->tx_lock); idr_init(&htt->pending_tx); - htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev, - sizeof(struct ath10k_htt_txbuf), 4, 0); - if (!htt->tx_pool) { + size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf); + htt->txbuf.vaddr = dma_alloc_coherent(ar->dev, size, + &htt->txbuf.paddr, + GFP_DMA); + if (!htt->txbuf.vaddr) { + ath10k_err(ar, "failed to alloc tx buffer\n"); ret = -ENOMEM; goto free_idr_pending_tx; } @@ -110,14 +128,17 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) if (!htt->frag_desc.vaddr) { ath10k_warn(ar, "failed to alloc fragment desc memory\n"); ret = -ENOMEM; - goto free_tx_pool; + goto free_txbuf; } skip_frag_desc_alloc: return 0; -free_tx_pool: - dma_pool_destroy(htt->tx_pool); +free_txbuf: + size = htt->max_num_pending_tx * + sizeof(struct ath10k_htt_txbuf); + dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr, + htt->txbuf.paddr); free_idr_pending_tx: idr_destroy(&htt->pending_tx); return ret; @@ -145,7 +166,13 @@ void ath10k_htt_tx_free(struct ath10k_htt *htt) idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar); idr_destroy(&htt->pending_tx); - dma_pool_destroy(htt->tx_pool); + + if (htt->txbuf.vaddr) { + size = htt->max_num_pending_tx * + sizeof(struct ath10k_htt_txbuf); + dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr, + htt->txbuf.paddr); + } if (htt->frag_desc.vaddr) { size = htt->max_num_pending_tx * @@ -160,6 +187,12 @@ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb_any(skb); } +void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); +} +EXPORT_SYMBOL(ath10k_htt_hif_tx_complete); + int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; @@ -417,8 +450,19 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) int len = 0; int msdu_id = -1; int res; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + bool limit_mgmt_desc = false; + bool is_probe_resp = false; + + if (ar->hw_params.max_probe_resp_desc_thres) { + limit_mgmt_desc = true; + + if (ieee80211_is_probe_resp(hdr->frame_control)) + is_probe_resp = true; + } + + res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp); - res = ath10k_htt_tx_inc_pending(htt); if (res) goto err; @@ -428,9 +472,9 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) spin_lock_bh(&htt->tx_lock); res = ath10k_htt_tx_alloc_msdu_id(htt, msdu); spin_unlock_bh(&htt->tx_lock); - if (res < 0) { + if (res < 0) goto err_tx_dec; - } + msdu_id = res; txdesc = ath10k_htc_alloc_skb(ar, len); @@ -476,7 +520,7 @@ err_free_msdu_id: ath10k_htt_tx_free_msdu_id(htt, msdu_id); spin_unlock_bh(&htt->tx_lock); err_tx_dec: - ath10k_htt_tx_dec_pending(htt); + ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); err: return res; } @@ -495,32 +539,37 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) int res; u8 flags0 = 0; u16 msdu_id, flags1 = 0; - dma_addr_t paddr = 0; u32 frags_paddr = 0; struct htt_msdu_ext_desc *ext_desc = NULL; + bool limit_mgmt_desc = false; + bool is_probe_resp = false; + + if (unlikely(ieee80211_is_mgmt(hdr->frame_control)) && + ar->hw_params.max_probe_resp_desc_thres) { + limit_mgmt_desc = true; + + if (ieee80211_is_probe_resp(hdr->frame_control)) + is_probe_resp = true; + } - res = ath10k_htt_tx_inc_pending(htt); + res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp); if (res) goto err; spin_lock_bh(&htt->tx_lock); res = ath10k_htt_tx_alloc_msdu_id(htt, msdu); spin_unlock_bh(&htt->tx_lock); - if (res < 0) { + if (res < 0) goto err_tx_dec; - } + msdu_id = res; prefetch_len = min(htt->prefetch_len, msdu->len); prefetch_len = roundup(prefetch_len, 4); - skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC, - &paddr); - if (!skb_cb->htt.txbuf) { - res = -ENOMEM; - goto err_free_msdu_id; - } - skb_cb->htt.txbuf_paddr = paddr; + skb_cb->htt.txbuf = &htt->txbuf.vaddr[msdu_id]; + skb_cb->htt.txbuf_paddr = htt->txbuf.paddr + + (sizeof(struct ath10k_htt_txbuf) * msdu_id); if ((ieee80211_is_action(hdr->frame_control) || ieee80211_is_deauth(hdr->frame_control) || @@ -528,7 +577,8 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); } else if (!skb_cb->htt.nohwcrypt && - skb_cb->txmode == ATH10K_HW_TXRX_RAW) { + skb_cb->txmode == ATH10K_HW_TXRX_RAW && + ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); } @@ -537,7 +587,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) res = dma_mapping_error(dev, skb_cb->paddr); if (res) { res = -EIO; - goto err_free_txbuf; + goto err_free_msdu_id; } switch (skb_cb->txmode) { @@ -669,16 +719,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) err_unmap_msdu: dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); -err_free_txbuf: - dma_pool_free(htt->tx_pool, - skb_cb->htt.txbuf, - skb_cb->htt.txbuf_paddr); err_free_msdu_id: spin_lock_bh(&htt->tx_lock); ath10k_htt_tx_free_msdu_id(htt, msdu_id); spin_unlock_bh(&htt->tx_lock); err_tx_dec: - ath10k_htt_tx_dec_pending(htt); + ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); err: return res; } diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 23afcda2de96..39966a05c1cc 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -84,6 +84,15 @@ enum qca6174_chip_id_rev { #define QCA99X0_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA99X0_HW_2_0_PATCH_LOAD_ADDR 0x1234 +/* QCA9377 1.0 definitions */ +#define QCA9377_HW_1_0_DEV_VERSION 0x05020001 +#define QCA9377_HW_1_0_CHIP_ID_REV 0x1 +#define QCA9377_HW_1_0_FW_DIR ATH10K_FW_DIR "/QCA9377/hw1.0" +#define QCA9377_HW_1_0_FW_FILE "firmware.bin" +#define QCA9377_HW_1_0_OTP_FILE "otp.bin" +#define QCA9377_HW_1_0_BOARD_DATA_FILE "board.bin" +#define QCA9377_HW_1_0_PATCH_LOAD_ADDR 0x1234 + #define ATH10K_FW_API2_FILE "firmware-2.bin" #define ATH10K_FW_API3_FILE "firmware-3.bin" @@ -94,9 +103,13 @@ enum qca6174_chip_id_rev { #define ATH10K_FW_API5_FILE "firmware-5.bin" #define ATH10K_FW_UTF_FILE "utf.bin" +#define ATH10K_FW_UTF_API2_FILE "utf-2.bin" /* includes also the null byte */ #define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K" +#define ATH10K_BOARD_MAGIC "QCA-ATH10K-BOARD" + +#define ATH10K_BOARD_API2_FILE "board-2.bin" #define REG_DUMP_COUNT_QCA988X 60 @@ -159,10 +172,21 @@ enum ath10k_fw_htt_op_version { ATH10K_FW_HTT_OP_VERSION_MAX, }; +enum ath10k_bd_ie_type { + /* contains sub IEs of enum ath10k_bd_ie_board_type */ + ATH10K_BD_IE_BOARD = 0, +}; + +enum ath10k_bd_ie_board_type { + ATH10K_BD_IE_BOARD_NAME = 0, + ATH10K_BD_IE_BOARD_DATA = 1, +}; + enum ath10k_hw_rev { ATH10K_HW_QCA988X, ATH10K_HW_QCA6174, ATH10K_HW_QCA99X0, + ATH10K_HW_QCA9377, }; struct ath10k_hw_regs { @@ -215,6 +239,7 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, #define QCA_REV_988X(ar) ((ar)->hw_rev == ATH10K_HW_QCA988X) #define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174) #define QCA_REV_99X0(ar) ((ar)->hw_rev == ATH10K_HW_QCA99X0) +#define QCA_REV_9377(ar) ((ar)->hw_rev == ATH10K_HW_QCA9377) /* Known pecularities: * - raw appears in nwifi decap, raw and nwifi appear in ethernet decap @@ -337,7 +362,7 @@ enum ath10k_hw_rate_cck { #define TARGET_10X_MAX_FRAG_ENTRIES 0 /* 10.2 parameters */ -#define TARGET_10_2_DMA_BURST_SIZE 1 +#define TARGET_10_2_DMA_BURST_SIZE 0 /* Target specific defines for WMI-TLV firmware */ #define TARGET_TLV_NUM_VDEVS 4 @@ -391,7 +416,7 @@ enum ath10k_hw_rate_cck { #define TARGET_10_4_TX_DBG_LOG_SIZE 1024 #define TARGET_10_4_NUM_WDS_ENTRIES 32 -#define TARGET_10_4_DMA_BURST_SIZE 1 +#define TARGET_10_4_DMA_BURST_SIZE 0 #define TARGET_10_4_MAC_AGGR_DELIM 0 #define TARGET_10_4_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 #define TARGET_10_4_VOW_CONFIG 0 @@ -414,16 +439,6 @@ enum ath10k_hw_rate_cck { #define CE_COUNT ar->hw_values->ce_count /* - * Total number of PCIe MSI interrupts requested for all interrupt sources. - * PCIe standard forces this to be a power of 2. - * Some Host OS's limit MSI requests that can be granted to 8 - * so for now we abide by this limit and avoid requesting more - * than that. - */ -#define MSI_NUM_REQUEST_LOG2 3 -#define MSI_NUM_REQUEST (1<<MSI_NUM_REQUEST_LOG2) - -/* * Granted MSIs are assigned as follows: * Firmware uses the first * Remaining MSIs, if any, are used by Copy Engines diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 64674c955d44..a7411fe90cc4 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -197,9 +197,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif, return -EOPNOTSUPP; } - if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { + if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - } if (cmd == DISABLE_KEY) { arg.key_cipher = WMI_CIPHER_NONE; @@ -1070,6 +1069,7 @@ static bool ath10k_mac_monitor_vdev_is_needed(struct ath10k *ar) return false; return ar->monitor || + ar->filter_flags & FIF_OTHER_BSS || test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); } @@ -1110,7 +1110,8 @@ static int ath10k_monitor_recalc(struct ath10k *ar) ret = ath10k_monitor_stop(ar); if (ret) - ath10k_warn(ar, "failed to stop disallowed monitor: %d\n", ret); + ath10k_warn(ar, "failed to stop disallowed monitor: %d\n", + ret); /* not serious */ } @@ -2083,7 +2084,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, enum ieee80211_band band; const u8 *ht_mcs_mask; const u16 *vht_mcs_mask; - int i, n, max_nss; + int i, n; + u8 max_nss; u32 stbc; lockdep_assert_held(&ar->conf_mutex); @@ -2168,7 +2170,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, arg->peer_ht_rates.rates[i] = i; } else { arg->peer_ht_rates.num_rates = n; - arg->peer_num_spatial_streams = max_nss; + arg->peer_num_spatial_streams = min(sta->rx_nss, max_nss); } ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", @@ -3617,9 +3619,6 @@ static int ath10k_start_scan(struct ath10k *ar, } spin_unlock_bh(&ar->data_lock); - /* Add a 200ms margin to account for event/command processing */ - ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, - msecs_to_jiffies(arg->max_scan_time+200)); return 0; } @@ -3737,13 +3736,8 @@ static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) mutex_lock(&ar->conf_mutex); - if (ar->cfg_tx_chainmask) { - *tx_ant = ar->cfg_tx_chainmask; - *rx_ant = ar->cfg_rx_chainmask; - } else { - *tx_ant = ar->supp_tx_chainmask; - *rx_ant = ar->supp_rx_chainmask; - } + *tx_ant = ar->cfg_tx_chainmask; + *rx_ant = ar->cfg_rx_chainmask; mutex_unlock(&ar->conf_mutex); @@ -3763,6 +3757,169 @@ static void ath10k_check_chain_mask(struct ath10k *ar, u32 cm, const char *dbg) dbg, cm); } +static int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar) +{ + int nsts = ar->vht_cap_info; + + nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; + + /* If firmware does not deliver to host number of space-time + * streams supported, assume it support up to 4 BF STS and return + * the value for VHT CAP: nsts-1) + */ + if (nsts == 0) + return 3; + + return nsts; +} + +static int ath10k_mac_get_vht_cap_bf_sound_dim(struct ath10k *ar) +{ + int sound_dim = ar->vht_cap_info; + + sound_dim &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + + /* If the sounding dimension is not advertised by the firmware, + * let's use a default value of 1 + */ + if (sound_dim == 0) + return 1; + + return sound_dim; +} + +static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) +{ + struct ieee80211_sta_vht_cap vht_cap = {0}; + u16 mcs_map; + u32 val; + int i; + + vht_cap.vht_supported = 1; + vht_cap.cap = ar->vht_cap_info; + + if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) { + val = ath10k_mac_get_vht_cap_bf_sts(ar); + val <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; + val &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + + vht_cap.cap |= val; + } + + if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) { + val = ath10k_mac_get_vht_cap_bf_sound_dim(ar); + val <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + val &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + + vht_cap.cap |= val; + } + + mcs_map = 0; + for (i = 0; i < 8; i++) { + if ((i < ar->num_rf_chains) && (ar->cfg_tx_chainmask & BIT(i))) + mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2); + else + mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2); + } + + vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map); + vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map); + + return vht_cap; +} + +static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar) +{ + int i; + struct ieee80211_sta_ht_cap ht_cap = {0}; + + if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED)) + return ht_cap; + + ht_cap.ht_supported = 1; + ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + + if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + + if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) { + u32 smps; + + smps = WLAN_HT_CAP_SM_PS_DYNAMIC; + smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; + + ht_cap.cap |= smps; + } + + if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC) + ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + + if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) { + u32 stbc; + + stbc = ar->ht_cap_info; + stbc &= WMI_HT_CAP_RX_STBC; + stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT; + stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT; + stbc &= IEEE80211_HT_CAP_RX_STBC; + + ht_cap.cap |= stbc; + } + + if (ar->ht_cap_info & WMI_HT_CAP_LDPC) + ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; + + if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT) + ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT; + + /* max AMSDU is implicitly taken from vht_cap_info */ + if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK) + ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + for (i = 0; i < ar->num_rf_chains; i++) { + if (ar->cfg_rx_chainmask & BIT(i)) + ht_cap.mcs.rx_mask[i] = 0xFF; + } + + ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; + + return ht_cap; +} + +static void ath10k_mac_setup_ht_vht_cap(struct ath10k *ar) +{ + struct ieee80211_supported_band *band; + struct ieee80211_sta_vht_cap vht_cap; + struct ieee80211_sta_ht_cap ht_cap; + + ht_cap = ath10k_get_ht_cap(ar); + vht_cap = ath10k_create_vht_cap(ar); + + if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) { + band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; + band->ht_cap = ht_cap; + + /* Enable the VHT support at 2.4 GHz */ + band->vht_cap = vht_cap; + } + if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) { + band = &ar->mac.sbands[IEEE80211_BAND_5GHZ]; + band->ht_cap = ht_cap; + band->vht_cap = vht_cap; + } +} + static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant) { int ret; @@ -3795,6 +3952,9 @@ static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant) return ret; } + /* Reload HT/VHT capability */ + ath10k_mac_setup_ht_vht_cap(ar); + return 0; } @@ -3885,9 +4045,7 @@ static int ath10k_start(struct ieee80211_hw *hw) } } - if (ar->cfg_tx_chainmask) - __ath10k_set_antenna(ar, ar->cfg_tx_chainmask, - ar->cfg_rx_chainmask); + __ath10k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask); /* * By default FW set ARP frames ac to voice (6). In that case ARP @@ -3906,6 +4064,18 @@ static int ath10k_start(struct ieee80211_hw *hw) goto err_core_stop; } + if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA, + ar->fw_features)) { + ret = ath10k_wmi_pdev_enable_adaptive_cca(ar, 1, + WMI_CCA_DETECT_LEVEL_AUTO, + WMI_CCA_DETECT_MARGIN_AUTO); + if (ret) { + ath10k_warn(ar, "failed to enable adaptive cca: %d\n", + ret); + goto err_core_stop; + } + } + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->ani_enable, 1); if (ret) { @@ -4068,17 +4238,21 @@ static int ath10k_mac_set_txbf_conf(struct ath10k_vif *arvif) { u32 value = 0; struct ath10k *ar = arvif->ar; + int nsts; + int sound_dim; if (ath10k_wmi_get_txbf_conf_scheme(ar) != WMI_TXBF_CONF_BEFORE_ASSOC) return 0; + nsts = ath10k_mac_get_vht_cap_bf_sts(ar); if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) - value |= SM((ar->num_rf_chains - 1), WMI_TXBF_STS_CAP_OFFSET); + value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); + sound_dim = ath10k_mac_get_vht_cap_bf_sound_dim(ar); if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) - value |= SM((ar->num_rf_chains - 1), WMI_BF_SOUND_DIM_OFFSET); + value |= SM(sound_dim, WMI_BF_SOUND_DIM_OFFSET); if (!value) return 0; @@ -4175,6 +4349,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_ADHOC: arvif->vdev_type = WMI_VDEV_TYPE_IBSS; break; + case NL80211_IFTYPE_MESH_POINT: + if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { + ret = -EINVAL; + ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n"); + goto err; + } + arvif->vdev_type = WMI_VDEV_TYPE_AP; + break; case NL80211_IFTYPE_AP: arvif->vdev_type = WMI_VDEV_TYPE_AP; @@ -4215,6 +4397,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, * become corrupted, e.g. have garbled IEs or out-of-date TIM bitmap. */ if (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_AP) { arvif->beacon_buf = dma_zalloc_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN, @@ -4554,6 +4737,13 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (ret) ath10k_warn(ar, "failed to update beacon template: %d\n", ret); + + if (ieee80211_vif_is_mesh(vif)) { + /* mesh doesn't use SSID but firmware needs it */ + strncpy(arvif->u.ap.ssid, "mesh", + sizeof(arvif->u.ap.ssid)); + arvif->u.ap.ssid_len = 4; + } } if (changed & BSS_CHANGED_AP_PROBE_RESP) { @@ -4607,7 +4797,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, info->use_cts_prot ? 1 : 0); if (ret) ath10k_warn(ar, "failed to set protection mode %d on vdev %i: %d\n", - info->use_cts_prot, arvif->vdev_id, ret); + info->use_cts_prot, arvif->vdev_id, ret); } if (changed & BSS_CHANGED_ERP_SLOT) { @@ -4751,6 +4941,11 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, spin_unlock_bh(&ar->data_lock); } + /* Add a 200ms margin to account for event/command processing */ + ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, + msecs_to_jiffies(arg.max_scan_time + + 200)); + exit: mutex_unlock(&ar->conf_mutex); return ret; @@ -5293,6 +5488,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC && (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_ADHOC)) { /* * New association. @@ -5328,6 +5524,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH && (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_ADHOC)) { /* * Disassociation. @@ -5901,7 +6098,7 @@ ath10k_mac_bitrate_mask_get_single_rate(struct ath10k *ar, } static int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif, - u8 rate, u8 nss, u8 sgi) + u8 rate, u8 nss, u8 sgi, u8 ldpc) { struct ath10k *ar = arvif->ar; u32 vdev_param; @@ -5934,6 +6131,13 @@ static int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif, return ret; } + vdev_param = ar->wmi.vdev_param->ldpc; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ldpc); + if (ret) { + ath10k_warn(ar, "failed to set ldpc param %d: %d\n", ldpc, ret); + return ret; + } + return 0; } @@ -5997,6 +6201,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, u8 rate; u8 nss; u8 sgi; + u8 ldpc; int single_nss; int ret; @@ -6006,6 +6211,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, band = def.chan->band; ht_mcs_mask = mask->control[band].ht_mcs; vht_mcs_mask = mask->control[band].vht_mcs; + ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC); sgi = mask->control[band].gi; if (sgi == NL80211_TXRATE_FORCE_LGI) @@ -6044,7 +6250,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); - ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi); + ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc); if (ret) { ath10k_warn(ar, "failed to set fixed rate params on vdev %i: %d\n", arvif->vdev_id, ret); @@ -6144,7 +6350,7 @@ static int ath10k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); @@ -6203,8 +6409,8 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, rcu_read_lock(); if (!ctx && ath10k_mac_num_chanctxs(ar) == 1) { ieee80211_iter_chan_contexts_atomic(ar->hw, - ath10k_mac_get_any_chandef_iter, - &def); + ath10k_mac_get_any_chandef_iter, + &def); if (vifs) def = &vifs[0].new_ctx->def; @@ -6218,6 +6424,94 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, rcu_read_unlock(); } +static void +ath10k_mac_update_vif_chan(struct ath10k *ar, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs) +{ + struct ath10k_vif *arvif; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + /* First stop monitor interface. Some FW versions crash if there's a + * lone monitor interface. + */ + if (ar->monitor_started) + ath10k_monitor_stop(ar); + + for (i = 0; i < n_vifs; i++) { + arvif = ath10k_vif_to_arvif(vifs[i].vif); + + ath10k_dbg(ar, ATH10K_DBG_MAC, + "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n", + arvif->vdev_id, + vifs[i].old_ctx->def.chan->center_freq, + vifs[i].new_ctx->def.chan->center_freq, + vifs[i].old_ctx->def.width, + vifs[i].new_ctx->def.width); + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) { + ath10k_warn(ar, "failed to down vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + /* All relevant vdevs are downed and associated channel resources + * should be available for the channel switch now. + */ + + spin_lock_bh(&ar->data_lock); + ath10k_mac_update_rx_channel(ar, NULL, vifs, n_vifs); + spin_unlock_bh(&ar->data_lock); + + for (i = 0; i < n_vifs; i++) { + arvif = ath10k_vif_to_arvif(vifs[i].vif); + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath10k_mac_setup_bcn_tmpl(arvif); + if (ret) + ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n", + ret); + + ret = ath10k_mac_setup_prb_tmpl(arvif); + if (ret) + ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n", + ret); + + ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def); + if (ret) { + ath10k_warn(ar, "failed to restart vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + + ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); + if (ret) { + ath10k_warn(ar, "failed to bring vdev up %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + ath10k_monitor_recalc(ar); +} + static int ath10k_mac_op_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) @@ -6264,12 +6558,52 @@ ath10k_mac_op_remove_chanctx(struct ieee80211_hw *hw, mutex_unlock(&ar->conf_mutex); } +struct ath10k_mac_change_chanctx_arg { + struct ieee80211_chanctx_conf *ctx; + struct ieee80211_vif_chanctx_switch *vifs; + int n_vifs; + int next_vif; +}; + +static void +ath10k_mac_change_chanctx_cnt_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_mac_change_chanctx_arg *arg = data; + + if (rcu_access_pointer(vif->chanctx_conf) != arg->ctx) + return; + + arg->n_vifs++; +} + +static void +ath10k_mac_change_chanctx_fill_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_mac_change_chanctx_arg *arg = data; + struct ieee80211_chanctx_conf *ctx; + + ctx = rcu_access_pointer(vif->chanctx_conf); + if (ctx != arg->ctx) + return; + + if (WARN_ON(arg->next_vif == arg->n_vifs)) + return; + + arg->vifs[arg->next_vif].vif = vif; + arg->vifs[arg->next_vif].old_ctx = ctx; + arg->vifs[arg->next_vif].new_ctx = ctx; + arg->next_vif++; +} + static void ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) { struct ath10k *ar = hw->priv; + struct ath10k_mac_change_chanctx_arg arg = { .ctx = ctx }; mutex_lock(&ar->conf_mutex); @@ -6283,6 +6617,30 @@ ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw, if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) goto unlock; + if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) { + ieee80211_iterate_active_interfaces_atomic( + hw, + IEEE80211_IFACE_ITER_NORMAL, + ath10k_mac_change_chanctx_cnt_iter, + &arg); + if (arg.n_vifs == 0) + goto radar; + + arg.vifs = kcalloc(arg.n_vifs, sizeof(arg.vifs[0]), + GFP_KERNEL); + if (!arg.vifs) + goto radar; + + ieee80211_iterate_active_interfaces_atomic( + hw, + IEEE80211_IFACE_ITER_NORMAL, + ath10k_mac_change_chanctx_fill_iter, + &arg); + ath10k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs); + kfree(arg.vifs); + } + +radar: ath10k_recalc_radar_detection(ar); /* FIXME: How to configure Rx chains properly? */ @@ -6402,91 +6760,13 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, enum ieee80211_chanctx_switch_mode mode) { struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif; - int ret; - int i; mutex_lock(&ar->conf_mutex); ath10k_dbg(ar, ATH10K_DBG_MAC, "mac chanctx switch n_vifs %d mode %d\n", n_vifs, mode); - - /* First stop monitor interface. Some FW versions crash if there's a - * lone monitor interface. - */ - if (ar->monitor_started) - ath10k_monitor_stop(ar); - - for (i = 0; i < n_vifs; i++) { - arvif = ath10k_vif_to_arvif(vifs[i].vif); - - ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n", - arvif->vdev_id, - vifs[i].old_ctx->def.chan->center_freq, - vifs[i].new_ctx->def.chan->center_freq, - vifs[i].old_ctx->def.width, - vifs[i].new_ctx->def.width); - - if (WARN_ON(!arvif->is_started)) - continue; - - if (WARN_ON(!arvif->is_up)) - continue; - - ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); - if (ret) { - ath10k_warn(ar, "failed to down vdev %d: %d\n", - arvif->vdev_id, ret); - continue; - } - } - - /* All relevant vdevs are downed and associated channel resources - * should be available for the channel switch now. - */ - - spin_lock_bh(&ar->data_lock); - ath10k_mac_update_rx_channel(ar, NULL, vifs, n_vifs); - spin_unlock_bh(&ar->data_lock); - - for (i = 0; i < n_vifs; i++) { - arvif = ath10k_vif_to_arvif(vifs[i].vif); - - if (WARN_ON(!arvif->is_started)) - continue; - - if (WARN_ON(!arvif->is_up)) - continue; - - ret = ath10k_mac_setup_bcn_tmpl(arvif); - if (ret) - ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n", - ret); - - ret = ath10k_mac_setup_prb_tmpl(arvif); - if (ret) - ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n", - ret); - - ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def); - if (ret) { - ath10k_warn(ar, "failed to restart vdev %d: %d\n", - arvif->vdev_id, ret); - continue; - } - - ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, - arvif->bssid); - if (ret) { - ath10k_warn(ar, "failed to bring vdev up %d: %d\n", - arvif->vdev_id, ret); - continue; - } - } - - ath10k_monitor_recalc(ar); + ath10k_mac_update_vif_chan(ar, vifs, n_vifs); mutex_unlock(&ar->conf_mutex); return 0; @@ -6642,6 +6922,9 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { { .max = 7, .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif }, }; @@ -6649,6 +6932,9 @@ static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { { .max = 8, .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif }, }; @@ -6686,6 +6972,9 @@ static const struct ieee80211_iface_limit ath10k_tlv_if_limit[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), }, @@ -6707,6 +6996,9 @@ static const struct ieee80211_iface_limit ath10k_tlv_qcs_if_limit[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif BIT(NL80211_IFTYPE_P2P_GO), }, { @@ -6773,6 +7065,9 @@ static const struct ieee80211_iface_limit ath10k_10_4_if_limits[] = { { .max = 16, .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif }, }; @@ -6792,111 +7087,6 @@ static const struct ieee80211_iface_combination ath10k_10_4_if_comb[] = { }, }; -static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) -{ - struct ieee80211_sta_vht_cap vht_cap = {0}; - u16 mcs_map; - u32 val; - int i; - - vht_cap.vht_supported = 1; - vht_cap.cap = ar->vht_cap_info; - - if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | - IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) { - val = ar->num_rf_chains - 1; - val <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; - val &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; - - vht_cap.cap |= val; - } - - if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | - IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) { - val = ar->num_rf_chains - 1; - val <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; - val &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; - - vht_cap.cap |= val; - } - - mcs_map = 0; - for (i = 0; i < 8; i++) { - if (i < ar->num_rf_chains) - mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i*2); - else - mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2); - } - - vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map); - vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map); - - return vht_cap; -} - -static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar) -{ - int i; - struct ieee80211_sta_ht_cap ht_cap = {0}; - - if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED)) - return ht_cap; - - ht_cap.ht_supported = 1; - ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; - ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; - ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; - - if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI) - ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; - - if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI) - ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - - if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) { - u32 smps; - - smps = WLAN_HT_CAP_SM_PS_DYNAMIC; - smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; - - ht_cap.cap |= smps; - } - - if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC) - ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; - - if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) { - u32 stbc; - - stbc = ar->ht_cap_info; - stbc &= WMI_HT_CAP_RX_STBC; - stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT; - stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT; - stbc &= IEEE80211_HT_CAP_RX_STBC; - - ht_cap.cap |= stbc; - } - - if (ar->ht_cap_info & WMI_HT_CAP_LDPC) - ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; - - if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT) - ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT; - - /* max AMSDU is implicitly taken from vht_cap_info */ - if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK) - ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; - - for (i = 0; i < ar->num_rf_chains; i++) - ht_cap.mcs.rx_mask[i] = 0xFF; - - ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; - - return ht_cap; -} - static void ath10k_get_arvif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { @@ -6938,8 +7128,6 @@ int ath10k_mac_register(struct ath10k *ar) WLAN_CIPHER_SUITE_AES_CMAC, }; struct ieee80211_supported_band *band; - struct ieee80211_sta_vht_cap vht_cap; - struct ieee80211_sta_ht_cap ht_cap; void *channels; int ret; @@ -6947,9 +7135,6 @@ int ath10k_mac_register(struct ath10k *ar) SET_IEEE80211_DEV(ar->hw, ar->dev); - ht_cap = ath10k_get_ht_cap(ar); - vht_cap = ath10k_create_vht_cap(ar); - BUILD_BUG_ON((ARRAY_SIZE(ath10k_2ghz_channels) + ARRAY_SIZE(ath10k_5ghz_channels)) != ATH10K_NUM_CHANS); @@ -6968,10 +7153,6 @@ int ath10k_mac_register(struct ath10k *ar) band->channels = channels; band->n_bitrates = ath10k_g_rates_size; band->bitrates = ath10k_g_rates; - band->ht_cap = ht_cap; - - /* Enable the VHT support at 2.4 GHz */ - band->vht_cap = vht_cap; ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band; } @@ -6990,17 +7171,18 @@ int ath10k_mac_register(struct ath10k *ar) band->channels = channels; band->n_bitrates = ath10k_a_rates_size; band->bitrates = ath10k_a_rates; - band->ht_cap = ht_cap; - band->vht_cap = vht_cap; ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band; } + ath10k_mac_setup_ht_vht_cap(ar); + ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP); + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT); - ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask; - ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask; + ar->hw->wiphy->available_antennas_rx = ar->cfg_rx_chainmask; + ar->hw->wiphy->available_antennas_tx = ar->cfg_tx_chainmask; if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features)) ar->hw->wiphy->interface_modes |= @@ -7146,7 +7328,7 @@ int ath10k_mac_register(struct ath10k *ar) ath10k_reg_notifier); if (ret) { ath10k_err(ar, "failed to initialise regulatory: %i\n", ret); - goto err_free; + goto err_dfs_detector_exit; } ar->hw->wiphy->cipher_suites = cipher_suites; @@ -7155,7 +7337,7 @@ int ath10k_mac_register(struct ath10k *ar) ret = ieee80211_register_hw(ar->hw); if (ret) { ath10k_err(ar, "failed to register ieee80211: %d\n", ret); - goto err_free; + goto err_dfs_detector_exit; } if (!ath_is_world_regd(&ar->ath_common.regulatory)) { @@ -7169,10 +7351,16 @@ int ath10k_mac_register(struct ath10k *ar) err_unregister: ieee80211_unregister_hw(ar->hw); + +err_dfs_detector_exit: + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) + ar->dfs_detector->exit(ar->dfs_detector); + err_free: kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels); kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels); + SET_IEEE80211_DEV(ar->hw, NULL); return ret; } diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 1046ab65b9ab..3fca200b986c 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -61,12 +61,14 @@ MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)"); #define QCA6164_2_1_DEVICE_ID (0x0041) #define QCA6174_2_1_DEVICE_ID (0x003e) #define QCA99X0_2_0_DEVICE_ID (0x0040) +#define QCA9377_1_0_DEVICE_ID (0x0042) static const struct pci_device_id ath10k_pci_id_table[] = { { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */ { PCI_VDEVICE(ATHEROS, QCA6164_2_1_DEVICE_ID) }, /* PCI-E QCA6164 V2.1 */ { PCI_VDEVICE(ATHEROS, QCA6174_2_1_DEVICE_ID) }, /* PCI-E QCA6174 V2.1 */ { PCI_VDEVICE(ATHEROS, QCA99X0_2_0_DEVICE_ID) }, /* PCI-E QCA99X0 V2 */ + { PCI_VDEVICE(ATHEROS, QCA9377_1_0_DEVICE_ID) }, /* PCI-E QCA9377 V1 */ {0} }; @@ -90,6 +92,7 @@ static const struct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = { { QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_2_CHIP_ID_REV }, { QCA99X0_2_0_DEVICE_ID, QCA99X0_HW_2_0_CHIP_ID_REV }, + { QCA9377_1_0_DEVICE_ID, QCA9377_HW_1_0_CHIP_ID_REV }, }; static void ath10k_pci_buffer_cleanup(struct ath10k *ar); @@ -104,6 +107,10 @@ static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, struct ath10k_ce_pipe *rx_pipe, struct bmi_xfer *xfer); static int ath10k_pci_qca99x0_chip_reset(struct ath10k *ar); +static void ath10k_pci_htc_tx_cb(struct ath10k_ce_pipe *ce_state); +static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state); +static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state); +static void ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state); static const struct ce_attr host_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ @@ -112,6 +119,7 @@ static const struct ce_attr host_ce_config_wlan[] = { .src_nentries = 16, .src_sz_max = 256, .dest_nentries = 0, + .send_cb = ath10k_pci_htc_tx_cb, }, /* CE1: target->host HTT + HTC control */ @@ -120,6 +128,7 @@ static const struct ce_attr host_ce_config_wlan[] = { .src_nentries = 0, .src_sz_max = 2048, .dest_nentries = 512, + .recv_cb = ath10k_pci_htc_rx_cb, }, /* CE2: target->host WMI */ @@ -128,6 +137,7 @@ static const struct ce_attr host_ce_config_wlan[] = { .src_nentries = 0, .src_sz_max = 2048, .dest_nentries = 128, + .recv_cb = ath10k_pci_htc_rx_cb, }, /* CE3: host->target WMI */ @@ -136,6 +146,7 @@ static const struct ce_attr host_ce_config_wlan[] = { .src_nentries = 32, .src_sz_max = 2048, .dest_nentries = 0, + .send_cb = ath10k_pci_htc_tx_cb, }, /* CE4: host->target HTT */ @@ -144,14 +155,16 @@ static const struct ce_attr host_ce_config_wlan[] = { .src_nentries = CE_HTT_H2T_MSG_SRC_NENTRIES, .src_sz_max = 256, .dest_nentries = 0, + .send_cb = ath10k_pci_htt_tx_cb, }, - /* CE5: unused */ + /* CE5: target->host HTT (HIF->HTT) */ { .flags = CE_ATTR_FLAGS, .src_nentries = 0, - .src_sz_max = 0, - .dest_nentries = 0, + .src_sz_max = 512, + .dest_nentries = 512, + .recv_cb = ath10k_pci_htt_rx_cb, }, /* CE6: target autonomous hif_memcpy */ @@ -257,12 +270,12 @@ static const struct ce_pipe_config target_ce_config_wlan[] = { /* NB: 50% of src nentries, since tx has 2 frags */ - /* CE5: unused */ + /* CE5: target->host HTT (HIF->HTT) */ { .pipenum = __cpu_to_le32(5), - .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .pipedir = __cpu_to_le32(PIPEDIR_IN), .nentries = __cpu_to_le32(32), - .nbytes_max = __cpu_to_le32(2048), + .nbytes_max = __cpu_to_le32(512), .flags = __cpu_to_le32(CE_ATTR_FLAGS), .reserved = __cpu_to_le32(0), }, @@ -396,7 +409,7 @@ static const struct service_to_pipe target_service_to_ce_map_wlan[] = { { __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG), __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ - __cpu_to_le32(1), + __cpu_to_le32(5), }, /* (Additions here) */ @@ -452,8 +465,12 @@ static int ath10k_pci_wake_wait(struct ath10k *ar) int curr_delay = 5; while (tot_delay < PCIE_WAKE_TIMEOUT) { - if (ath10k_pci_is_awake(ar)) + if (ath10k_pci_is_awake(ar)) { + if (tot_delay > PCIE_WAKE_LATE_US) + ath10k_warn(ar, "device wakeup took %d ms which is unusally long, otherwise it works normally.\n", + tot_delay / 1000); return 0; + } udelay(curr_delay); tot_delay += curr_delay; @@ -465,12 +482,53 @@ static int ath10k_pci_wake_wait(struct ath10k *ar) return -ETIMEDOUT; } +static int ath10k_pci_force_wake(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ar_pci->ps_lock, flags); + + if (!ar_pci->ps_awake) { + iowrite32(PCIE_SOC_WAKE_V_MASK, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + + ret = ath10k_pci_wake_wait(ar); + if (ret == 0) + ar_pci->ps_awake = true; + } + + spin_unlock_irqrestore(&ar_pci->ps_lock, flags); + + return ret; +} + +static void ath10k_pci_force_sleep(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned long flags; + + spin_lock_irqsave(&ar_pci->ps_lock, flags); + + iowrite32(PCIE_SOC_WAKE_RESET, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + ar_pci->ps_awake = false; + + spin_unlock_irqrestore(&ar_pci->ps_lock, flags); +} + static int ath10k_pci_wake(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); unsigned long flags; int ret = 0; + if (ar_pci->pci_ps == 0) + return ret; + spin_lock_irqsave(&ar_pci->ps_lock, flags); ath10k_dbg(ar, ATH10K_DBG_PCI_PS, "pci ps wake refcount %lu awake %d\n", @@ -502,6 +560,9 @@ static void ath10k_pci_sleep(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); unsigned long flags; + if (ar_pci->pci_ps == 0) + return; + spin_lock_irqsave(&ar_pci->ps_lock, flags); ath10k_dbg(ar, ATH10K_DBG_PCI_PS, "pci ps sleep refcount %lu awake %d\n", @@ -544,6 +605,11 @@ static void ath10k_pci_sleep_sync(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); unsigned long flags; + if (ar_pci->pci_ps == 0) { + ath10k_pci_force_sleep(ar); + return; + } + del_timer_sync(&ar_pci->ps_timer); spin_lock_irqsave(&ar_pci->ps_lock, flags); @@ -682,8 +748,6 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe) dma_addr_t paddr; int ret; - lockdep_assert_held(&ar_pci->ce_lock); - skb = dev_alloc_skb(pipe->buf_sz); if (!skb) return -ENOMEM; @@ -701,9 +765,10 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe) ATH10K_SKB_RXCB(skb)->paddr = paddr; + spin_lock_bh(&ar_pci->ce_lock); ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr); + spin_unlock_bh(&ar_pci->ce_lock); if (ret) { - ath10k_warn(ar, "failed to post pci rx buf: %d\n", ret); dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); dev_kfree_skb_any(skb); @@ -713,25 +778,27 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe) return 0; } -static void __ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) +static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) { struct ath10k *ar = pipe->hif_ce_state; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl; int ret, num; - lockdep_assert_held(&ar_pci->ce_lock); - if (pipe->buf_sz == 0) return; if (!ce_pipe->dest_ring) return; + spin_lock_bh(&ar_pci->ce_lock); num = __ath10k_ce_rx_num_free_bufs(ce_pipe); + spin_unlock_bh(&ar_pci->ce_lock); while (num--) { ret = __ath10k_pci_rx_post_buf(pipe); if (ret) { + if (ret == -ENOSPC) + break; ath10k_warn(ar, "failed to post pci rx buf: %d\n", ret); mod_timer(&ar_pci->rx_post_retry, jiffies + ATH10K_PCI_RX_POST_RETRY_MS); @@ -740,25 +807,13 @@ static void __ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) } } -static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) -{ - struct ath10k *ar = pipe->hif_ce_state; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - spin_lock_bh(&ar_pci->ce_lock); - __ath10k_pci_rx_post_pipe(pipe); - spin_unlock_bh(&ar_pci->ce_lock); -} - static void ath10k_pci_rx_post(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int i; - spin_lock_bh(&ar_pci->ce_lock); for (i = 0; i < CE_COUNT; i++) - __ath10k_pci_rx_post_pipe(&ar_pci->pipe_info[i]); - spin_unlock_bh(&ar_pci->ce_lock); + ath10k_pci_rx_post_pipe(&ar_pci->pipe_info[i]); } static void ath10k_pci_rx_replenish_retry(unsigned long ptr) @@ -775,6 +830,7 @@ static u32 ath10k_pci_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr) switch (ar->hw_rev) { case ATH10K_HW_QCA988X: case ATH10K_HW_QCA6174: + case ATH10K_HW_QCA9377: val = (ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS) & 0x7ff) << 21; @@ -858,9 +914,8 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, goto done; i = 0; - while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf, - &completed_nbytes, - &id) != 0) { + while (ath10k_ce_completed_send_next_nolock(ce_diag, + NULL) != 0) { mdelay(1); if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { ret = -EBUSY; @@ -868,16 +923,6 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, } } - if (nbytes != completed_nbytes) { - ret = -EIO; - goto done; - } - - if (buf != (u32)address) { - ret = -EIO; - goto done; - } - i = 0; while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf, &completed_nbytes, @@ -1031,9 +1076,8 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, goto done; i = 0; - while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf, - &completed_nbytes, - &id) != 0) { + while (ath10k_ce_completed_send_next_nolock(ce_diag, + NULL) != 0) { mdelay(1); if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { @@ -1042,16 +1086,6 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, } } - if (nbytes != completed_nbytes) { - ret = -EIO; - goto done; - } - - if (buf != ce_data) { - ret = -EIO; - goto done; - } - i = 0; while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf, &completed_nbytes, @@ -1102,20 +1136,14 @@ static int ath10k_pci_diag_write32(struct ath10k *ar, u32 address, u32 value) } /* Called by lower (CE) layer when a send to Target completes. */ -static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state) +static void ath10k_pci_htc_tx_cb(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; struct sk_buff_head list; struct sk_buff *skb; - u32 ce_data; - unsigned int nbytes; - unsigned int transfer_id; __skb_queue_head_init(&list); - while (ath10k_ce_completed_send_next(ce_state, (void **)&skb, &ce_data, - &nbytes, &transfer_id) == 0) { + while (ath10k_ce_completed_send_next(ce_state, (void **)&skb) == 0) { /* no need to call tx completion for NULL pointers */ if (skb == NULL) continue; @@ -1124,16 +1152,16 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state) } while ((skb = __skb_dequeue(&list))) - cb->tx_completion(ar, skb); + ath10k_htc_tx_completion_handler(ar, skb); } -/* Called by lower (CE) layer when data is received from the Target. */ -static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state) +static void ath10k_pci_process_rx_cb(struct ath10k_ce_pipe *ce_state, + void (*callback)(struct ath10k *ar, + struct sk_buff *skb)) { struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); 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; @@ -1168,12 +1196,52 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state) ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci rx: ", skb->data, skb->len); - cb->rx_completion(ar, skb); + callback(ar, skb); } ath10k_pci_rx_post_pipe(pipe_info); } +/* Called by lower (CE) layer when data is received from the Target. */ +static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state) +{ + ath10k_pci_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler); +} + +/* Called by lower (CE) layer when a send to HTT Target completes. */ +static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state) +{ + struct ath10k *ar = ce_state->ar; + struct sk_buff *skb; + + while (ath10k_ce_completed_send_next(ce_state, (void **)&skb) == 0) { + /* no need to call tx completion for NULL pointers */ + if (!skb) + continue; + + dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr, + skb->len, DMA_TO_DEVICE); + ath10k_htt_hif_tx_complete(ar, skb); + } +} + +static void ath10k_pci_htt_rx_deliver(struct ath10k *ar, struct sk_buff *skb) +{ + skb_pull(skb, sizeof(struct ath10k_htc_hdr)); + ath10k_htt_t2h_msg_handler(ar, skb); +} + +/* Called by lower (CE) layer when HTT data is received from the Target. */ +static void ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state) +{ + /* CE4 polling needs to be done whenever CE pipe which transports + * HTT Rx (target->host) is processed. + */ + ath10k_ce_per_engine_service(ce_state->ar, 4); + + ath10k_pci_process_rx_cb(ce_state, ath10k_pci_htt_rx_deliver); +} + static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, struct ath10k_hif_sg_item *items, int n_items) { @@ -1343,17 +1411,6 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, ath10k_ce_per_engine_service(ar, pipe); } -static void ath10k_pci_hif_set_callbacks(struct ath10k *ar, - struct ath10k_hif_cb *callbacks) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif set callbacks\n"); - - memcpy(&ar_pci->msg_callbacks_current, callbacks, - sizeof(ar_pci->msg_callbacks_current)); -} - static void ath10k_pci_kill_tasklet(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -1368,10 +1425,8 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar) del_timer_sync(&ar_pci->rx_post_retry); } -static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, - u16 service_id, u8 *ul_pipe, - u8 *dl_pipe, int *ul_is_polled, - int *dl_is_polled) +static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe) { const struct service_to_pipe *entry; bool ul_set = false, dl_set = false; @@ -1379,9 +1434,6 @@ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif map service\n"); - /* polling for received messages not supported */ - *dl_is_polled = 0; - for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) { entry = &target_service_to_ce_map_wlan[i]; @@ -1415,25 +1467,17 @@ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, if (WARN_ON(!ul_set || !dl_set)) return -ENOENT; - *ul_is_polled = - (host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0; - return 0; } static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe) { - int ul_is_polled, dl_is_polled; - ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif get default pipe\n"); (void)ath10k_pci_hif_map_service_to_pipe(ar, ATH10K_HTC_SVC_ID_RSVD_CTRL, - ul_pipe, - dl_pipe, - &ul_is_polled, - &dl_is_polled); + ul_pipe, dl_pipe); } static void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar) @@ -1443,6 +1487,7 @@ static void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar) switch (ar->hw_rev) { case ATH10K_HW_QCA988X: case ATH10K_HW_QCA6174: + case ATH10K_HW_QCA9377: val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS); val &= ~CORE_CTRL_PCIE_REG_31_MASK; @@ -1464,6 +1509,7 @@ static void ath10k_pci_irq_msi_fw_unmask(struct ath10k *ar) switch (ar->hw_rev) { case ATH10K_HW_QCA988X: case ATH10K_HW_QCA6174: + case ATH10K_HW_QCA9377: val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS); val |= CORE_CTRL_PCIE_REG_31_MASK; @@ -1504,6 +1550,7 @@ static void ath10k_pci_irq_enable(struct ath10k *ar) static int ath10k_pci_hif_start(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n"); ath10k_pci_irq_enable(ar); @@ -1553,7 +1600,6 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe) struct ath10k_pci *ar_pci; struct ath10k_ce_pipe *ce_pipe; struct ath10k_ce_ring *ce_ring; - struct ce_desc *ce_desc; struct sk_buff *skb; int i; @@ -1568,10 +1614,6 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe) if (!pci_pipe->buf_sz) return; - 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) @@ -1579,7 +1621,7 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe) ce_ring->per_transfer_context[i] = NULL; - ar_pci->msg_callbacks_current.tx_completion(ar, skb); + ath10k_htc_tx_completion_handler(ar, skb); } } @@ -1745,12 +1787,8 @@ err_dma: static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state) { struct bmi_xfer *xfer; - u32 ce_data; - unsigned int nbytes; - unsigned int transfer_id; - if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer, &ce_data, - &nbytes, &transfer_id)) + if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer)) return; xfer->tx_done = true; @@ -1840,6 +1878,8 @@ static int ath10k_pci_get_num_banks(struct ath10k *ar) return 9; } break; + case QCA9377_1_0_DEVICE_ID: + return 2; } ath10k_warn(ar, "unknown number of banks, assuming 1\n"); @@ -1999,9 +2039,7 @@ static int ath10k_pci_alloc_pipes(struct ath10k *ar) 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); + ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]); if (ret) { ath10k_err(ar, "failed to allocate copy engine pipe %d: %d\n", i, ret); @@ -2257,7 +2295,7 @@ static int ath10k_pci_qca6174_chip_reset(struct ath10k *ar) ret = ath10k_pci_wait_for_target_init(ar); if (ret) { ath10k_warn(ar, "failed to wait for target after cold reset: %d\n", - ret); + ret); return ret; } @@ -2302,6 +2340,8 @@ static int ath10k_pci_chip_reset(struct ath10k *ar) return ath10k_pci_qca988x_chip_reset(ar); else if (QCA_REV_6174(ar)) return ath10k_pci_qca6174_chip_reset(ar); + else if (QCA_REV_9377(ar)) + return ath10k_pci_qca6174_chip_reset(ar); else if (QCA_REV_99X0(ar)) return ath10k_pci_qca99x0_chip_reset(ar); else @@ -2397,6 +2437,15 @@ static int ath10k_pci_hif_resume(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct pci_dev *pdev = ar_pci->pdev; u32 val; + int ret = 0; + + if (ar_pci->pci_ps == 0) { + ret = ath10k_pci_force_wake(ar); + if (ret) { + ath10k_err(ar, "failed to wake up target: %d\n", ret); + return ret; + } + } /* Suspend/Resume resets the PCI configuration space, so we have to * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries @@ -2407,7 +2456,7 @@ static int ath10k_pci_hif_resume(struct ath10k *ar) if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); - return 0; + return ret; } #endif @@ -2421,7 +2470,6 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = { .map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe, .get_default_pipe = ath10k_pci_hif_get_default_pipe, .send_complete_check = ath10k_pci_hif_send_complete_check, - .set_callbacks = ath10k_pci_hif_set_callbacks, .get_free_queue_number = ath10k_pci_hif_get_free_queue_number, .power_up = ath10k_pci_hif_power_up, .power_down = ath10k_pci_hif_power_down, @@ -2501,6 +2549,16 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) { struct ath10k *ar = arg; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + if (ar_pci->pci_ps == 0) { + ret = ath10k_pci_force_wake(ar); + if (ret) { + ath10k_warn(ar, "failed to wake device up on irq: %d\n", + ret); + return IRQ_NONE; + } + } if (ar_pci->num_msi_intrs == 0) { if (!ath10k_pci_irq_pending(ar)) @@ -2609,12 +2667,9 @@ static int ath10k_pci_request_irq(struct ath10k *ar) return ath10k_pci_request_irq_legacy(ar); case 1: return ath10k_pci_request_irq_msi(ar); - case MSI_NUM_REQUEST: + default: return ath10k_pci_request_irq_msix(ar); } - - ath10k_warn(ar, "unknown irq configuration upon request\n"); - return -EINVAL; } static void ath10k_pci_free_irq(struct ath10k *ar) @@ -2657,7 +2712,7 @@ static int ath10k_pci_init_irq(struct ath10k *ar) /* Try MSI-X */ if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO) { - ar_pci->num_msi_intrs = MSI_NUM_REQUEST; + ar_pci->num_msi_intrs = MSI_ASSIGN_CE_MAX + 1; ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs, ar_pci->num_msi_intrs); if (ret > 0) @@ -2705,18 +2760,13 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar) switch (ar_pci->num_msi_intrs) { case 0: ath10k_pci_deinit_irq_legacy(ar); - return 0; - case 1: - /* fall-through */ - case MSI_NUM_REQUEST: - pci_disable_msi(ar_pci->pdev); - return 0; + break; default: pci_disable_msi(ar_pci->pdev); + break; } - ath10k_warn(ar, "unknown irq configuration upon deinit\n"); - return -EINVAL; + return 0; } static int ath10k_pci_wait_for_target_init(struct ath10k *ar) @@ -2908,17 +2958,25 @@ static int ath10k_pci_probe(struct pci_dev *pdev, struct ath10k_pci *ar_pci; enum ath10k_hw_rev hw_rev; u32 chip_id; + bool pci_ps; switch (pci_dev->device) { case QCA988X_2_0_DEVICE_ID: hw_rev = ATH10K_HW_QCA988X; + pci_ps = false; break; case QCA6164_2_1_DEVICE_ID: case QCA6174_2_1_DEVICE_ID: hw_rev = ATH10K_HW_QCA6174; + pci_ps = true; break; case QCA99X0_2_0_DEVICE_ID: hw_rev = ATH10K_HW_QCA99X0; + pci_ps = false; + break; + case QCA9377_1_0_DEVICE_ID: + hw_rev = ATH10K_HW_QCA9377; + pci_ps = true; break; default: WARN_ON(1); @@ -2932,19 +2990,21 @@ static int ath10k_pci_probe(struct pci_dev *pdev, return -ENOMEM; } - ath10k_dbg(ar, ATH10K_DBG_PCI, "pci probe\n"); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "pci probe %04x:%04x %04x:%04x\n", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); ar_pci = ath10k_pci_priv(ar); ar_pci->pdev = pdev; ar_pci->dev = &pdev->dev; ar_pci->ar = ar; ar->dev_id = pci_dev->device; + ar_pci->pci_ps = pci_ps; - if (pdev->subsystem_vendor || pdev->subsystem_device) - scnprintf(ar->spec_board_id, sizeof(ar->spec_board_id), - "%04x:%04x:%04x:%04x", - pdev->vendor, pdev->device, - pdev->subsystem_vendor, pdev->subsystem_device); + ar->id.vendor = pdev->vendor; + ar->id.device = pdev->device; + ar->id.subsystem_vendor = pdev->subsystem_vendor; + ar->id.subsystem_device = pdev->subsystem_device; spin_lock_init(&ar_pci->ce_lock); spin_lock_init(&ar_pci->ps_lock); @@ -2970,6 +3030,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ath10k_pci_ce_deinit(ar); ath10k_pci_irq_disable(ar); + if (ar_pci->pci_ps == 0) { + ret = ath10k_pci_force_wake(ar); + if (ret) { + ath10k_warn(ar, "failed to wake up device : %d\n", ret); + goto err_free_pipes; + } + } + ret = ath10k_pci_init_irq(ar); if (ret) { ath10k_err(ar, "failed to init irqs: %d\n", ret); @@ -3098,13 +3166,20 @@ MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API3_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API4_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API5_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_BOARD_API2_FILE); /* QCA6174 2.1 firmware files */ MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_FW_API4_FILE); MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_FW_API5_FILE); MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" QCA6174_HW_2_1_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_BOARD_API2_FILE); /* QCA6174 3.1 firmware files */ MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API4_FILE); MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API5_FILE); MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" QCA6174_HW_3_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_BOARD_API2_FILE); + +/* QCA9377 1.0 firmware files */ +MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" ATH10K_FW_API5_FILE); +MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" QCA9377_HW_1_0_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 8d364fb8f743..f91bf333cb75 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -175,8 +175,6 @@ struct ath10k_pci { struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX]; - struct ath10k_hif_cb msg_callbacks_current; - /* Copy Engine used for Diagnostic Accesses */ struct ath10k_ce_pipe *ce_diag; @@ -221,6 +219,12 @@ struct ath10k_pci { * powersave register state changes. */ bool ps_awake; + + /* pci power save, disable for QCA988X and QCA99X0. + * Writing 'false' to this variable avoids frequent locking + * on MMIO read/write. + */ + bool pci_ps; }; static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) @@ -230,7 +234,8 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) #define ATH10K_PCI_RX_POST_RETRY_MS 50 #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */ -#define PCIE_WAKE_TIMEOUT 10000 /* 10ms */ +#define PCIE_WAKE_TIMEOUT 30000 /* 30ms */ +#define PCIE_WAKE_LATE_US 10000 /* 10ms */ #define BAR_NUM 0 diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h index 768bef629099..05a421bc322a 100644 --- a/drivers/net/wireless/ath/ath10k/targaddrs.h +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h @@ -450,6 +450,9 @@ Fw Mode/SubMode Mask #define QCA6174_BOARD_DATA_SZ 8192 #define QCA6174_BOARD_EXT_DATA_SZ 0 +#define QCA9377_BOARD_DATA_SZ QCA6174_BOARD_DATA_SZ +#define QCA9377_BOARD_EXT_DATA_SZ 0 + #define QCA99X0_BOARD_DATA_SZ 12288 #define QCA99X0_BOARD_EXT_DATA_SZ 0 diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index b084f88da102..1d5a2fdcbf56 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -139,11 +139,181 @@ static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[]) return cfg80211_testmode_reply(skb); } -static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[]) +static int ath10k_tm_fetch_utf_firmware_api_2(struct ath10k *ar) +{ + size_t len, magic_len, ie_len; + struct ath10k_fw_ie *hdr; + char filename[100]; + __le32 *version; + const u8 *data; + int ie_id, ret; + + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw_params.fw.dir, ATH10K_FW_UTF_API2_FILE); + + /* load utf firmware image */ + ret = request_firmware(&ar->testmode.utf, filename, ar->dev); + if (ret) { + ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n", + filename, ret); + return ret; + } + + data = ar->testmode.utf->data; + len = ar->testmode.utf->size; + + /* FIXME: call release_firmware() in error cases */ + + /* magic also includes the null byte, check that as well */ + magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1; + + if (len < magic_len) { + ath10k_err(ar, "utf firmware file is too small to contain magic\n"); + ret = -EINVAL; + goto err; + } + + if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) { + ath10k_err(ar, "invalid firmware magic\n"); + ret = -EINVAL; + goto err; + } + + /* jump over the padding */ + magic_len = ALIGN(magic_len, 4); + + len -= magic_len; + data += magic_len; + + /* loop elements */ + while (len > sizeof(struct ath10k_fw_ie)) { + hdr = (struct ath10k_fw_ie *)data; + + ie_id = le32_to_cpu(hdr->id); + ie_len = le32_to_cpu(hdr->len); + + len -= sizeof(*hdr); + data += sizeof(*hdr); + + if (len < ie_len) { + ath10k_err(ar, "invalid length for FW IE %d (%zu < %zu)\n", + ie_id, len, ie_len); + ret = -EINVAL; + goto err; + } + + switch (ie_id) { + case ATH10K_FW_IE_FW_VERSION: + if (ie_len > sizeof(ar->testmode.utf_version) - 1) + break; + + memcpy(ar->testmode.utf_version, data, ie_len); + ar->testmode.utf_version[ie_len] = '\0'; + + ath10k_dbg(ar, ATH10K_DBG_TESTMODE, + "testmode found fw utf version %s\n", + ar->testmode.utf_version); + break; + case ATH10K_FW_IE_TIMESTAMP: + /* ignore timestamp, but don't warn about it either */ + break; + case ATH10K_FW_IE_FW_IMAGE: + ath10k_dbg(ar, ATH10K_DBG_TESTMODE, + "testmode found fw image ie (%zd B)\n", + ie_len); + + ar->testmode.utf_firmware_data = data; + ar->testmode.utf_firmware_len = ie_len; + break; + case ATH10K_FW_IE_WMI_OP_VERSION: + if (ie_len != sizeof(u32)) + break; + version = (__le32 *)data; + ar->testmode.op_version = le32_to_cpup(version); + ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode found fw ie wmi op version %d\n", + ar->testmode.op_version); + break; + default: + ath10k_warn(ar, "Unknown testmode FW IE: %u\n", + le32_to_cpu(hdr->id)); + break; + } + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + + len -= ie_len; + data += ie_len; + } + + if (!ar->testmode.utf_firmware_data || !ar->testmode.utf_firmware_len) { + ath10k_err(ar, "No ATH10K_FW_IE_FW_IMAGE found\n"); + ret = -EINVAL; + goto err; + } + + return 0; + +err: + release_firmware(ar->testmode.utf); + + return ret; +} + +static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar) { char filename[100]; int ret; + snprintf(filename, sizeof(filename), "%s/%s", + ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE); + + /* load utf firmware image */ + ret = request_firmware(&ar->testmode.utf, filename, ar->dev); + if (ret) { + ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n", + filename, ret); + return ret; + } + + /* We didn't find FW UTF API 1 ("utf.bin") does not advertise + * firmware features. Do an ugly hack where we force the firmware + * features to match with 10.1 branch so that wmi.c will use the + * correct WMI interface. + */ + + ar->testmode.op_version = ATH10K_FW_WMI_OP_VERSION_10_1; + ar->testmode.utf_firmware_data = ar->testmode.utf->data; + ar->testmode.utf_firmware_len = ar->testmode.utf->size; + + return 0; +} + +static int ath10k_tm_fetch_firmware(struct ath10k *ar) +{ + int ret; + + ret = ath10k_tm_fetch_utf_firmware_api_2(ar); + if (ret == 0) { + ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2"); + return 0; + } + + ret = ath10k_tm_fetch_utf_firmware_api_1(ar); + if (ret) { + ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret); + return ret; + } + + ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1"); + + return 0; +} + +static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[]) +{ + const char *ver; + int ret; + ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n"); mutex_lock(&ar->conf_mutex); @@ -165,36 +335,27 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[]) goto err; } - snprintf(filename, sizeof(filename), "%s/%s", - ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE); - - /* load utf firmware image */ - ret = request_firmware(&ar->testmode.utf, filename, ar->dev); + ret = ath10k_tm_fetch_firmware(ar); if (ret) { - ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n", - filename, ret); + ath10k_err(ar, "failed to fetch UTF firmware: %d", ret); goto err; } spin_lock_bh(&ar->data_lock); - ar->testmode.utf_monitor = true; - spin_unlock_bh(&ar->data_lock); - BUILD_BUG_ON(sizeof(ar->fw_features) != sizeof(ar->testmode.orig_fw_features)); memcpy(ar->testmode.orig_fw_features, ar->fw_features, sizeof(ar->fw_features)); ar->testmode.orig_wmi_op_version = ar->wmi.op_version; - - /* utf.bin firmware image does not advertise firmware features. Do - * an ugly hack where we force the firmware features so that wmi.c - * will use the correct WMI interface. - */ memset(ar->fw_features, 0, sizeof(ar->fw_features)); - ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1; + + ar->wmi.op_version = ar->testmode.op_version; + + ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n", + ar->wmi.op_version); ret = ath10k_hif_power_up(ar); if (ret) { @@ -212,7 +373,12 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[]) ar->state = ATH10K_STATE_UTF; - ath10k_info(ar, "UTF firmware started\n"); + if (strlen(ar->testmode.utf_version) > 0) + ver = ar->testmode.utf_version; + else + ver = "API 1"; + + ath10k_info(ar, "UTF firmware %s started\n", ver); mutex_unlock(&ar->conf_mutex); diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c index 1a899d70dc5d..60fe562e3041 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.c +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -215,6 +215,6 @@ err_cooling_destroy: void ath10k_thermal_unregister(struct ath10k *ar) { - thermal_cooling_device_unregister(ar->thermal.cdev); sysfs_remove_link(&ar->dev->kobj, "cooling_device"); + thermal_cooling_device_unregister(ar->thermal.cdev); } diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index e4a9c4c8d0cb..6d1105ab4592 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -52,6 +52,9 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct ieee80211_tx_info *info; struct ath10k_skb_cb *skb_cb; struct sk_buff *msdu; + struct ieee80211_hdr *hdr; + __le16 fc; + bool limit_mgmt_desc = false; ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d success %d\n", @@ -72,21 +75,23 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, spin_unlock_bh(&htt->tx_lock); return; } + + hdr = (struct ieee80211_hdr *)msdu->data; + fc = hdr->frame_control; + + if (unlikely(ieee80211_is_mgmt(fc)) && + ar->hw_params.max_probe_resp_desc_thres) + limit_mgmt_desc = true; + ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id); - __ath10k_htt_tx_dec_pending(htt); + __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); if (htt->num_pending_tx == 0) wake_up(&htt->empty_tx_wq); spin_unlock_bh(&htt->tx_lock); skb_cb = ATH10K_SKB_CB(msdu); - dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->htt.txbuf) - dma_pool_free(htt->tx_pool, - skb_cb->htt.txbuf, - skb_cb->htt.txbuf_paddr); - ath10k_report_offchan_tx(htt->ar, msdu); info = IEEE80211_SKB_CB(msdu); diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index 248ffc3d6620..8f4f6a892581 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -177,6 +177,15 @@ struct wmi_ops { const struct wmi_tdls_peer_capab_arg *cap, const struct wmi_channel_arg *chan); struct sk_buff *(*gen_adaptive_qcs)(struct ath10k *ar, bool enable); + struct sk_buff *(*gen_pdev_get_tpc_config)(struct ath10k *ar, + u32 param); + void (*fw_stats_fill)(struct ath10k *ar, + struct ath10k_fw_stats *fw_stats, + char *buf); + struct sk_buff *(*gen_pdev_enable_adaptive_cca)(struct ath10k *ar, + u8 enable, + u32 detect_level, + u32 detect_margin); }; int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); @@ -1270,4 +1279,52 @@ ath10k_wmi_adaptive_qcs(struct ath10k *ar, bool enable) return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->adaptive_qcs_cmdid); } +static inline int +ath10k_wmi_pdev_get_tpc_config(struct ath10k *ar, u32 param) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_get_tpc_config) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_get_tpc_config(ar, param); + + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_get_tpc_config_cmdid); +} + +static inline int +ath10k_wmi_fw_stats_fill(struct ath10k *ar, struct ath10k_fw_stats *fw_stats, + char *buf) +{ + if (!ar->wmi.ops->fw_stats_fill) + return -EOPNOTSUPP; + + ar->wmi.ops->fw_stats_fill(ar, fw_stats, buf); + return 0; +} + +static inline int +ath10k_wmi_pdev_enable_adaptive_cca(struct ath10k *ar, u8 enable, + u32 detect_level, u32 detect_margin) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_enable_adaptive_cca) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_enable_adaptive_cca(ar, enable, + detect_level, + detect_margin); + + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_enable_adaptive_cca_cmdid); +} + #endif diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index b5849b3fd2f0..6fbd17b69469 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -23,6 +23,7 @@ #include "wmi-ops.h" #include "wmi-tlv.h" #include "p2p.h" +#include "testmode.h" /***************/ /* TLV helpers */ @@ -419,6 +420,7 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_tlv_event_id id; + bool consumed; cmd_hdr = (struct wmi_cmd_hdr *)skb->data; id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); @@ -428,6 +430,18 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) trace_ath10k_wmi_event(ar, id, skb->data, skb->len); + consumed = ath10k_tm_event_wmi(ar, id, skb); + + /* Ready event must be handled normally also in UTF mode so that we + * know the UTF firmware has booted, others we are just bypass WMI + * events to testmode. + */ + if (consumed && id != WMI_TLV_READY_EVENTID) { + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv testmode consumed 0x%x\n", id); + goto out; + } + switch (id) { case WMI_TLV_MGMT_RX_EVENTID: ath10k_wmi_event_mgmt_rx(ar, skb); @@ -3468,6 +3482,7 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_update_fw_tdls_state = ath10k_wmi_tlv_op_gen_update_fw_tdls_state, .gen_tdls_peer_update = ath10k_wmi_tlv_op_gen_tdls_peer_update, .gen_adaptive_qcs = ath10k_wmi_tlv_op_gen_adaptive_qcs, + .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill, }; /************/ diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ce01107ef37a..7569db0f69b5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -148,6 +148,7 @@ static struct wmi_cmd_map wmi_cmd_map = { .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID, .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_enable_adaptive_cca_cmdid = WMI_CMD_UNSUPPORTED, .scan_update_request_cmdid = WMI_CMD_UNSUPPORTED, .vdev_standby_response_cmdid = WMI_CMD_UNSUPPORTED, .vdev_resume_response_cmdid = WMI_CMD_UNSUPPORTED, @@ -313,6 +314,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID, .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_enable_adaptive_cca_cmdid = WMI_CMD_UNSUPPORTED, .scan_update_request_cmdid = WMI_CMD_UNSUPPORTED, .vdev_standby_response_cmdid = WMI_CMD_UNSUPPORTED, .vdev_resume_response_cmdid = WMI_CMD_UNSUPPORTED, @@ -477,6 +479,7 @@ static struct wmi_cmd_map wmi_10_2_4_cmd_map = { .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID, .pdev_get_temperature_cmdid = WMI_10_2_PDEV_GET_TEMPERATURE_CMDID, + .pdev_enable_adaptive_cca_cmdid = WMI_10_2_SET_CCA_PARAMS, .scan_update_request_cmdid = WMI_CMD_UNSUPPORTED, .vdev_standby_response_cmdid = WMI_CMD_UNSUPPORTED, .vdev_resume_response_cmdid = WMI_CMD_UNSUPPORTED, @@ -1407,6 +1410,7 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = { .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID, .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_enable_adaptive_cca_cmdid = WMI_CMD_UNSUPPORTED, .scan_update_request_cmdid = WMI_CMD_UNSUPPORTED, .vdev_standby_response_cmdid = WMI_CMD_UNSUPPORTED, .vdev_resume_response_cmdid = WMI_CMD_UNSUPPORTED, @@ -2475,6 +2479,47 @@ void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src, dst->txop_ovf = __le32_to_cpu(src->txop_ovf); } +static void +ath10k_wmi_10_4_pull_pdev_stats_tx(const struct wmi_10_4_pdev_stats_tx *src, + struct ath10k_fw_stats_pdev *dst) +{ + dst->comp_queued = __le32_to_cpu(src->comp_queued); + dst->comp_delivered = __le32_to_cpu(src->comp_delivered); + dst->msdu_enqued = __le32_to_cpu(src->msdu_enqued); + dst->mpdu_enqued = __le32_to_cpu(src->mpdu_enqued); + dst->wmm_drop = __le32_to_cpu(src->wmm_drop); + dst->local_enqued = __le32_to_cpu(src->local_enqued); + dst->local_freed = __le32_to_cpu(src->local_freed); + dst->hw_queued = __le32_to_cpu(src->hw_queued); + dst->hw_reaped = __le32_to_cpu(src->hw_reaped); + dst->underrun = __le32_to_cpu(src->underrun); + dst->tx_abort = __le32_to_cpu(src->tx_abort); + dst->mpdus_requed = __le32_to_cpu(src->mpdus_requed); + dst->tx_ko = __le32_to_cpu(src->tx_ko); + dst->data_rc = __le32_to_cpu(src->data_rc); + dst->self_triggers = __le32_to_cpu(src->self_triggers); + dst->sw_retry_failure = __le32_to_cpu(src->sw_retry_failure); + dst->illgl_rate_phy_err = __le32_to_cpu(src->illgl_rate_phy_err); + dst->pdev_cont_xretry = __le32_to_cpu(src->pdev_cont_xretry); + dst->pdev_tx_timeout = __le32_to_cpu(src->pdev_tx_timeout); + dst->pdev_resets = __le32_to_cpu(src->pdev_resets); + dst->phy_underrun = __le32_to_cpu(src->phy_underrun); + dst->txop_ovf = __le32_to_cpu(src->txop_ovf); + dst->hw_paused = __le32_to_cpu(src->hw_paused); + dst->seq_posted = __le32_to_cpu(src->seq_posted); + dst->seq_failed_queueing = + __le32_to_cpu(src->seq_failed_queueing); + dst->seq_completed = __le32_to_cpu(src->seq_completed); + dst->seq_restarted = __le32_to_cpu(src->seq_restarted); + dst->mu_seq_posted = __le32_to_cpu(src->mu_seq_posted); + dst->mpdus_sw_flush = __le32_to_cpu(src->mpdus_sw_flush); + dst->mpdus_hw_filter = __le32_to_cpu(src->mpdus_hw_filter); + dst->mpdus_truncated = __le32_to_cpu(src->mpdus_truncated); + dst->mpdus_ack_failed = __le32_to_cpu(src->mpdus_ack_failed); + dst->mpdus_hw_filter = __le32_to_cpu(src->mpdus_hw_filter); + dst->mpdus_expired = __le32_to_cpu(src->mpdus_expired); +} + void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src, struct ath10k_fw_stats_pdev *dst) { @@ -2785,6 +2830,86 @@ static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar, return 0; } +static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar, + struct sk_buff *skb, + struct ath10k_fw_stats *stats) +{ + const struct wmi_10_2_stats_event *ev = (void *)skb->data; + u32 num_pdev_stats; + u32 num_pdev_ext_stats; + u32 num_vdev_stats; + u32 num_peer_stats; + int i; + + if (!skb_pull(skb, sizeof(*ev))) + return -EPROTO; + + num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); + num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_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_10_4_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_base(&src->base, dst); + ath10k_wmi_10_4_pull_pdev_stats_tx(&src->tx, dst); + ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst); + dst->rx_ovfl_errs = __le32_to_cpu(src->rx_ovfl_errs); + ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst); + + list_add_tail(&dst->list, &stats->pdevs); + } + + for (i = 0; i < num_pdev_ext_stats; i++) { + const struct wmi_10_2_pdev_ext_stats *src; + + src = (void *)skb->data; + if (!skb_pull(skb, sizeof(*src))) + return -EPROTO; + + /* FIXME: expose values to userspace + * + * Note: Even though this loop seems to do nothing it is + * required to parse following sub-structures properly. + */ + } + + /* fw doesn't implement vdev stats */ + + for (i = 0; i < num_peer_stats; i++) { + const struct wmi_10_4_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; + + 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); + dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate); + /* FIXME: expose 10.4 specific values */ + + list_add_tail(&dst->list, &stats->peers); + } + + return 0; +} + void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n"); @@ -3018,8 +3143,6 @@ static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif, memcpy(skb_put(bcn, arvif->u.ap.noa_len), arvif->u.ap.noa_data, arvif->u.ap.noa_len); - - return; } static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb, @@ -3507,7 +3630,7 @@ void ath10k_wmi_event_spectral_scan(struct ath10k *ar, tsf); if (res < 0) { ath10k_dbg(ar, ATH10K_DBG_WMI, "failed to process fft report: %d\n", - res); + res); return; } break; @@ -3835,9 +3958,258 @@ void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n"); } +static u8 ath10k_tpc_config_get_rate(struct ath10k *ar, + struct wmi_pdev_tpc_config_event *ev, + u32 rate_idx, u32 num_chains, + u32 rate_code, u8 type) +{ + u8 tpc, num_streams, preamble, ch, stm_idx; + + num_streams = ATH10K_HW_NSS(rate_code); + preamble = ATH10K_HW_PREAMBLE(rate_code); + ch = num_chains - 1; + + tpc = min_t(u8, ev->rates_array[rate_idx], ev->max_reg_allow_pow[ch]); + + if (__le32_to_cpu(ev->num_tx_chain) <= 1) + goto out; + + if (preamble == WMI_RATE_PREAMBLE_CCK) + goto out; + + stm_idx = num_streams - 1; + if (num_chains <= num_streams) + goto out; + + switch (type) { + case WMI_TPC_TABLE_TYPE_STBC: + tpc = min_t(u8, tpc, + ev->max_reg_allow_pow_agstbc[ch - 1][stm_idx]); + break; + case WMI_TPC_TABLE_TYPE_TXBF: + tpc = min_t(u8, tpc, + ev->max_reg_allow_pow_agtxbf[ch - 1][stm_idx]); + break; + case WMI_TPC_TABLE_TYPE_CDD: + tpc = min_t(u8, tpc, + ev->max_reg_allow_pow_agcdd[ch - 1][stm_idx]); + break; + default: + ath10k_warn(ar, "unknown wmi tpc table type: %d\n", type); + tpc = 0; + break; + } + +out: + return tpc; +} + +static void ath10k_tpc_config_disp_tables(struct ath10k *ar, + struct wmi_pdev_tpc_config_event *ev, + struct ath10k_tpc_stats *tpc_stats, + u8 *rate_code, u16 *pream_table, u8 type) +{ + u32 i, j, pream_idx, flags; + u8 tpc[WMI_TPC_TX_N_CHAIN]; + char tpc_value[WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE]; + char buff[WMI_TPC_BUF_SIZE]; + + flags = __le32_to_cpu(ev->flags); + + switch (type) { + case WMI_TPC_TABLE_TYPE_CDD: + if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD)) { + ath10k_dbg(ar, ATH10K_DBG_WMI, "CDD not supported\n"); + tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG; + return; + } + break; + case WMI_TPC_TABLE_TYPE_STBC: + if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC)) { + ath10k_dbg(ar, ATH10K_DBG_WMI, "STBC not supported\n"); + tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG; + return; + } + break; + case WMI_TPC_TABLE_TYPE_TXBF: + if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF)) { + ath10k_dbg(ar, ATH10K_DBG_WMI, "TXBF not supported\n"); + tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG; + return; + } + break; + default: + ath10k_dbg(ar, ATH10K_DBG_WMI, + "invalid table type in wmi tpc event: %d\n", type); + return; + } + + pream_idx = 0; + for (i = 0; i < __le32_to_cpu(ev->rate_max); i++) { + memset(tpc_value, 0, sizeof(tpc_value)); + memset(buff, 0, sizeof(buff)); + if (i == pream_table[pream_idx]) + pream_idx++; + + for (j = 0; j < WMI_TPC_TX_N_CHAIN; j++) { + if (j >= __le32_to_cpu(ev->num_tx_chain)) + break; + + tpc[j] = ath10k_tpc_config_get_rate(ar, ev, i, j + 1, + rate_code[i], + type); + snprintf(buff, sizeof(buff), "%8d ", tpc[j]); + strncat(tpc_value, buff, strlen(buff)); + } + tpc_stats->tpc_table[type].pream_idx[i] = pream_idx; + tpc_stats->tpc_table[type].rate_code[i] = rate_code[i]; + memcpy(tpc_stats->tpc_table[type].tpc_value[i], + tpc_value, sizeof(tpc_value)); + } +} + void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n"); + u32 i, j, pream_idx, num_tx_chain; + u8 rate_code[WMI_TPC_RATE_MAX], rate_idx; + u16 pream_table[WMI_TPC_PREAM_TABLE_MAX]; + struct wmi_pdev_tpc_config_event *ev; + struct ath10k_tpc_stats *tpc_stats; + + ev = (struct wmi_pdev_tpc_config_event *)skb->data; + + tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC); + if (!tpc_stats) + return; + + /* Create the rate code table based on the chains supported */ + rate_idx = 0; + pream_idx = 0; + + /* Fill CCK rate code */ + for (i = 0; i < 4; i++) { + rate_code[rate_idx] = + ATH10K_HW_RATECODE(i, 0, WMI_RATE_PREAMBLE_CCK); + rate_idx++; + } + pream_table[pream_idx] = rate_idx; + pream_idx++; + + /* Fill OFDM rate code */ + for (i = 0; i < 8; i++) { + rate_code[rate_idx] = + ATH10K_HW_RATECODE(i, 0, WMI_RATE_PREAMBLE_OFDM); + rate_idx++; + } + pream_table[pream_idx] = rate_idx; + pream_idx++; + + num_tx_chain = __le32_to_cpu(ev->num_tx_chain); + + /* Fill HT20 rate code */ + for (i = 0; i < num_tx_chain; i++) { + for (j = 0; j < 8; j++) { + rate_code[rate_idx] = + ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_HT); + rate_idx++; + } + } + pream_table[pream_idx] = rate_idx; + pream_idx++; + + /* Fill HT40 rate code */ + for (i = 0; i < num_tx_chain; i++) { + for (j = 0; j < 8; j++) { + rate_code[rate_idx] = + ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_HT); + rate_idx++; + } + } + pream_table[pream_idx] = rate_idx; + pream_idx++; + + /* Fill VHT20 rate code */ + for (i = 0; i < __le32_to_cpu(ev->num_tx_chain); i++) { + for (j = 0; j < 10; j++) { + rate_code[rate_idx] = + ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_VHT); + rate_idx++; + } + } + pream_table[pream_idx] = rate_idx; + pream_idx++; + + /* Fill VHT40 rate code */ + for (i = 0; i < num_tx_chain; i++) { + for (j = 0; j < 10; j++) { + rate_code[rate_idx] = + ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_VHT); + rate_idx++; + } + } + pream_table[pream_idx] = rate_idx; + pream_idx++; + + /* Fill VHT80 rate code */ + for (i = 0; i < num_tx_chain; i++) { + for (j = 0; j < 10; j++) { + rate_code[rate_idx] = + ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_VHT); + rate_idx++; + } + } + pream_table[pream_idx] = rate_idx; + pream_idx++; + + rate_code[rate_idx++] = + ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_CCK); + rate_code[rate_idx++] = + ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_OFDM); + rate_code[rate_idx++] = + ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_CCK); + rate_code[rate_idx++] = + ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_OFDM); + rate_code[rate_idx++] = + ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_OFDM); + + pream_table[pream_idx] = ATH10K_TPC_PREAM_TABLE_END; + + tpc_stats->chan_freq = __le32_to_cpu(ev->chan_freq); + tpc_stats->phy_mode = __le32_to_cpu(ev->phy_mode); + tpc_stats->ctl = __le32_to_cpu(ev->ctl); + tpc_stats->reg_domain = __le32_to_cpu(ev->reg_domain); + tpc_stats->twice_antenna_gain = a_sle32_to_cpu(ev->twice_antenna_gain); + tpc_stats->twice_antenna_reduction = + __le32_to_cpu(ev->twice_antenna_reduction); + tpc_stats->power_limit = __le32_to_cpu(ev->power_limit); + tpc_stats->twice_max_rd_power = __le32_to_cpu(ev->twice_max_rd_power); + tpc_stats->num_tx_chain = __le32_to_cpu(ev->num_tx_chain); + tpc_stats->rate_max = __le32_to_cpu(ev->rate_max); + + ath10k_tpc_config_disp_tables(ar, ev, tpc_stats, + rate_code, pream_table, + WMI_TPC_TABLE_TYPE_CDD); + ath10k_tpc_config_disp_tables(ar, ev, tpc_stats, + rate_code, pream_table, + WMI_TPC_TABLE_TYPE_STBC); + ath10k_tpc_config_disp_tables(ar, ev, tpc_stats, + rate_code, pream_table, + WMI_TPC_TABLE_TYPE_TXBF); + + ath10k_debug_tpc_stats_process(ar, tpc_stats); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi event tpc config channel %d mode %d ctl %d regd %d gain %d %d limit %d max_power %d tx_chanins %d rates %d\n", + __le32_to_cpu(ev->chan_freq), + __le32_to_cpu(ev->phy_mode), + __le32_to_cpu(ev->ctl), + __le32_to_cpu(ev->reg_domain), + a_sle32_to_cpu(ev->twice_antenna_gain), + __le32_to_cpu(ev->twice_antenna_reduction), + __le32_to_cpu(ev->power_limit), + __le32_to_cpu(ev->twice_max_rd_power) / 2, + __le32_to_cpu(ev->num_tx_chain), + __le32_to_cpu(ev->rate_max)); } void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb) @@ -3917,6 +4289,53 @@ static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, return 0; } +static bool +ath10k_wmi_is_host_mem_allocated(struct ath10k *ar, + const struct wlan_host_mem_req **mem_reqs, + u32 num_mem_reqs) +{ + u32 req_id, num_units, unit_size, num_unit_info; + u32 pool_size; + int i, j; + bool found; + + if (ar->wmi.num_mem_chunks != num_mem_reqs) + return false; + + for (i = 0; i < num_mem_reqs; ++i) { + req_id = __le32_to_cpu(mem_reqs[i]->req_id); + num_units = __le32_to_cpu(mem_reqs[i]->num_units); + unit_size = __le32_to_cpu(mem_reqs[i]->unit_size); + num_unit_info = __le32_to_cpu(mem_reqs[i]->num_unit_info); + + if (num_unit_info & NUM_UNITS_IS_NUM_ACTIVE_PEERS) { + if (ar->num_active_peers) + num_units = ar->num_active_peers + 1; + else + num_units = ar->max_num_peers + 1; + } else if (num_unit_info & NUM_UNITS_IS_NUM_PEERS) { + num_units = ar->max_num_peers + 1; + } else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) { + num_units = ar->max_num_vdevs + 1; + } + + found = false; + for (j = 0; j < ar->wmi.num_mem_chunks; j++) { + if (ar->wmi.mem_chunks[j].req_id == req_id) { + pool_size = num_units * round_up(unit_size, 4); + if (ar->wmi.mem_chunks[j].len == pool_size) { + found = true; + break; + } + } + } + if (!found) + return false; + } + + return true; +} + static int ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, struct wmi_svc_rdy_ev_arg *arg) @@ -3997,6 +4416,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) struct wmi_svc_rdy_ev_arg arg = {}; u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; int ret; + bool allocated; if (!skb) { ath10k_warn(ar, "invalid service ready event skb\n"); @@ -4040,8 +4460,10 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) ar->num_rf_chains = ar->max_spatial_stream; } - ar->supp_tx_chainmask = (1 << ar->num_rf_chains) - 1; - ar->supp_rx_chainmask = (1 << ar->num_rf_chains) - 1; + if (!ar->cfg_tx_chainmask) { + ar->cfg_tx_chainmask = (1 << ar->num_rf_chains) - 1; + ar->cfg_rx_chainmask = (1 << ar->num_rf_chains) - 1; + } if (strlen(ar->hw->wiphy->fw_version) == 0) { snprintf(ar->hw->wiphy->fw_version, @@ -4073,6 +4495,18 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) * and WMI_SERVICE_IRAM_TIDS, etc. */ + allocated = ath10k_wmi_is_host_mem_allocated(ar, arg.mem_reqs, + num_mem_reqs); + if (allocated) + goto skip_mem_alloc; + + /* Either this event is received during boot time or there is a change + * in memory requirement from firmware when compared to last request. + * Free any old memory and do a fresh allocation based on the current + * memory requirement. + */ + ath10k_wmi_free_host_mem(ar); + for (i = 0; i < num_mem_reqs; ++i) { req_id = __le32_to_cpu(arg.mem_reqs[i]->req_id); num_units = __le32_to_cpu(arg.mem_reqs[i]->num_units); @@ -4108,6 +4542,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) return; } +skip_mem_alloc: ath10k_dbg(ar, ATH10K_DBG_WMI, "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 fw_build 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), @@ -4623,6 +5058,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg(ar, ATH10K_DBG_WMI, "received event id %d not implemented\n", id); break; + case WMI_10_4_UPDATE_STATS_EVENTID: + ath10k_wmi_event_update_stats(ar, skb); + break; default: ath10k_warn(ar, "Unknown eventid: %d\n", id); break; @@ -5029,7 +5467,7 @@ static struct sk_buff *ath10k_wmi_10_4_op_gen_init(struct ath10k *ar) config.rx_timeout_pri[2] = __cpu_to_le32(TARGET_10_4_RX_TIMEOUT_LO_PRI); config.rx_timeout_pri[3] = __cpu_to_le32(TARGET_10_4_RX_TIMEOUT_HI_PRI); - config.rx_decap_mode = __cpu_to_le32(TARGET_10_4_RX_DECAP_MODE); + config.rx_decap_mode = __cpu_to_le32(ar->wmi.rx_decap_mode); config.scan_max_pending_req = __cpu_to_le32(TARGET_10_4_SCAN_MAX_REQS); config.bmiss_offload_max_vdev = __cpu_to_le32(TARGET_10_4_BMISS_OFFLOAD_MAX_VDEV); @@ -6295,6 +6733,505 @@ ath10k_wmi_op_gen_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac, return skb; } +static struct sk_buff * +ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config(struct ath10k *ar, u32 param) +{ + struct wmi_pdev_get_tpc_config_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_pdev_get_tpc_config_cmd *)skb->data; + cmd->param = __cpu_to_le32(param); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi pdev get tcp config param:%d\n", param); + return skb; +} + +size_t ath10k_wmi_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; +} + +size_t ath10k_wmi_fw_stats_num_vdevs(struct list_head *head) +{ + struct ath10k_fw_stats_vdev *i; + size_t num = 0; + + list_for_each_entry(i, head, list) + ++num; + + return num; +} + +static void +ath10k_wmi_fw_pdev_base_stats_fill(const struct ath10k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath10k PDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Channel noise floor", pdev->ch_noise_floor); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Channel TX power", pdev->chan_tx_power); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX frame count", pdev->tx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX frame count", pdev->rx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX clear count", pdev->rx_clear_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Cycle count", pdev->cycle_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY error count", pdev->phy_err_count); + + *length = len; +} + +static void +ath10k_wmi_fw_pdev_extra_stats_fill(const struct ath10k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RTS bad count", pdev->rts_bad); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RTS good count", pdev->rts_good); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "FCS bad count", pdev->fcs_bad); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "No beacon count", pdev->no_beacons); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "MIB int count", pdev->mib_int_count); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; +} + +static void +ath10k_wmi_fw_pdev_tx_stats_fill(const struct ath10k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath10k PDEV TX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies queued", pdev->comp_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies disp.", pdev->comp_delivered); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDU queued", pdev->msdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU queued", pdev->mpdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs dropped", pdev->wmm_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local enqued", pdev->local_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local freed", pdev->local_freed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW queued", pdev->hw_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs reaped", pdev->hw_reaped); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num underruns", pdev->underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs cleaned", pdev->tx_abort); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs requed", pdev->mpdus_requed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Excessive retries", pdev->tx_ko); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW rate", pdev->data_rc); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Sched self tiggers", pdev->self_triggers); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Dropped due to SW retries", + pdev->sw_retry_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Illegal rate phy errors", + pdev->illgl_rate_phy_err); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Pdev continuous xretry", pdev->pdev_cont_xretry); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "TX timeout", pdev->pdev_tx_timeout); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PDEV resets", pdev->pdev_resets); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY underrun", pdev->phy_underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU is more than txop limit", pdev->txop_ovf); + *length = len; +} + +static void +ath10k_wmi_fw_pdev_rx_stats_fill(const struct ath10k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath10k PDEV RX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Mid PPDU route change", + pdev->mid_ppdu_route_change); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Tot. number of statuses", pdev->status_rcvd); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 0", pdev->r0_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 1", pdev->r1_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 2", pdev->r2_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 3", pdev->r3_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to HTT", pdev->htt_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to HTT", pdev->htt_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to stack", pdev->loc_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to stack", pdev->loc_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Oversized AMSUs", pdev->oversize_amsdu); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors", pdev->phy_errs); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors drops", pdev->phy_err_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs); + *length = len; +} + +static void +ath10k_wmi_fw_vdev_stats_fill(const struct ath10k_fw_stats_vdev *vdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; + int i; + + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "vdev id", vdev->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "beacon snr", vdev->beacon_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "data snr", vdev->data_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx frames", vdev->num_rx_frames); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts fail", vdev->num_rts_fail); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts success", vdev->num_rts_success); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx err", vdev->num_rx_err); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx discard", vdev->num_rx_discard); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num tx not acked", vdev->num_tx_not_acked); + + for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames", i, + vdev->num_tx_frames[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames retries", i, + vdev->num_tx_frames_retries[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames failures", i, + vdev->num_tx_frames_failures[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] 0x%08x\n", + "tx rate history", i, + vdev->tx_rate_history[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "beacon rssi history", i, + vdev->beacon_rssi_history[i]); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; +} + +static void +ath10k_wmi_fw_peer_stats_fill(const struct ath10k_fw_stats_peer *peer, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "Peer MAC address", peer->peer_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Peer RSSI", peer->peer_rssi); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Peer TX rate", peer->peer_tx_rate); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Peer RX rate", peer->peer_rx_rate); + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; +} + +void ath10k_wmi_main_op_fw_stats_fill(struct ath10k *ar, + struct ath10k_fw_stats *fw_stats, + char *buf) +{ + u32 len = 0; + u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; + const struct ath10k_fw_stats_pdev *pdev; + const struct ath10k_fw_stats_vdev *vdev; + const struct ath10k_fw_stats_peer *peer; + size_t num_peers; + size_t num_vdevs; + + 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_wmi_fw_stats_num_peers(&fw_stats->peers); + num_vdevs = ath10k_wmi_fw_stats_num_vdevs(&fw_stats->vdevs); + + ath10k_wmi_fw_pdev_base_stats_fill(pdev, buf, &len); + ath10k_wmi_fw_pdev_tx_stats_fill(pdev, buf, &len); + ath10k_wmi_fw_pdev_rx_stats_fill(pdev, buf, &len); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n", + "ath10k VDEV stats", num_vdevs); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + list_for_each_entry(vdev, &fw_stats->vdevs, list) { + ath10k_wmi_fw_vdev_stats_fill(vdev, buf, &len); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + 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", + "================="); + + list_for_each_entry(peer, &fw_stats->peers, list) { + ath10k_wmi_fw_peer_stats_fill(peer, buf, &len); + } + +unlock: + spin_unlock_bh(&ar->data_lock); + + if (len >= buf_len) + buf[len - 1] = 0; + else + buf[len] = 0; +} + +void ath10k_wmi_10x_op_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_vdev *vdev; + const struct ath10k_fw_stats_peer *peer; + size_t num_peers; + size_t num_vdevs; + + 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_wmi_fw_stats_num_peers(&fw_stats->peers); + num_vdevs = ath10k_wmi_fw_stats_num_vdevs(&fw_stats->vdevs); + + ath10k_wmi_fw_pdev_base_stats_fill(pdev, buf, &len); + ath10k_wmi_fw_pdev_extra_stats_fill(pdev, buf, &len); + ath10k_wmi_fw_pdev_tx_stats_fill(pdev, buf, &len); + ath10k_wmi_fw_pdev_rx_stats_fill(pdev, buf, &len); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n", + "ath10k VDEV stats", num_vdevs); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + list_for_each_entry(vdev, &fw_stats->vdevs, list) { + ath10k_wmi_fw_vdev_stats_fill(vdev, buf, &len); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + 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", + "================="); + + list_for_each_entry(peer, &fw_stats->peers, list) { + ath10k_wmi_fw_peer_stats_fill(peer, buf, &len); + } + +unlock: + spin_unlock_bh(&ar->data_lock); + + if (len >= buf_len) + buf[len - 1] = 0; + else + buf[len] = 0; +} + +static struct sk_buff * +ath10k_wmi_op_gen_pdev_enable_adaptive_cca(struct ath10k *ar, u8 enable, + u32 detect_level, u32 detect_margin) +{ + struct wmi_pdev_set_adaptive_cca_params *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_pdev_set_adaptive_cca_params *)skb->data; + cmd->enable = __cpu_to_le32(enable); + cmd->cca_detect_level = __cpu_to_le32(detect_level); + cmd->cca_detect_margin = __cpu_to_le32(detect_margin); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi pdev set adaptive cca params enable:%d detection level:%d detection margin:%d\n", + enable, detect_level, detect_margin); + return skb; +} + +void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar, + struct ath10k_fw_stats *fw_stats, + char *buf) +{ + u32 len = 0; + u32 buf_len = ATH10K_FW_STATS_BUF_SIZE; + const struct ath10k_fw_stats_pdev *pdev; + const struct ath10k_fw_stats_vdev *vdev; + const struct ath10k_fw_stats_peer *peer; + size_t num_peers; + size_t num_vdevs; + + 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_wmi_fw_stats_num_peers(&fw_stats->peers); + num_vdevs = ath10k_wmi_fw_stats_num_vdevs(&fw_stats->vdevs); + + ath10k_wmi_fw_pdev_base_stats_fill(pdev, buf, &len); + ath10k_wmi_fw_pdev_extra_stats_fill(pdev, buf, &len); + ath10k_wmi_fw_pdev_tx_stats_fill(pdev, buf, &len); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW paused", pdev->hw_paused); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Seqs posted", pdev->seq_posted); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Seqs failed queueing", pdev->seq_failed_queueing); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Seqs completed", pdev->seq_completed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Seqs restarted", pdev->seq_restarted); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MU Seqs posted", pdev->mu_seq_posted); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs SW flushed", pdev->mpdus_sw_flush); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs HW filtered", pdev->mpdus_hw_filter); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs truncated", pdev->mpdus_truncated); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs receive no ACK", pdev->mpdus_ack_failed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs expired", pdev->mpdus_expired); + + ath10k_wmi_fw_pdev_rx_stats_fill(pdev, buf, &len); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num Rx Overflow errors", pdev->rx_ovfl_errs); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n", + "ath10k VDEV stats", num_vdevs); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + list_for_each_entry(vdev, &fw_stats->vdevs, list) { + ath10k_wmi_fw_vdev_stats_fill(vdev, buf, &len); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + 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", + "================="); + + list_for_each_entry(peer, &fw_stats->peers, list) { + ath10k_wmi_fw_peer_stats_fill(peer, buf, &len); + } + +unlock: + spin_unlock_bh(&ar->data_lock); + + if (len >= buf_len) + buf[len - 1] = 0; + else + buf[len] = 0; +} + static const struct wmi_ops wmi_ops = { .rx = ath10k_wmi_op_rx, .map_svc = wmi_main_svc_map, @@ -6353,10 +7290,12 @@ static const struct wmi_ops wmi_ops = { .gen_addba_send = ath10k_wmi_op_gen_addba_send, .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, .gen_delba_send = ath10k_wmi_op_gen_delba_send, + .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill, /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ /* .gen_adaptive_qcs not implemented */ + /* .gen_pdev_enable_adaptive_cca not implemented */ }; static const struct wmi_ops wmi_10_1_ops = { @@ -6418,10 +7357,12 @@ static const struct wmi_ops wmi_10_1_ops = { .gen_addba_send = ath10k_wmi_op_gen_addba_send, .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, .gen_delba_send = ath10k_wmi_op_gen_delba_send, + .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ /* .gen_adaptive_qcs not implemented */ + /* .gen_pdev_enable_adaptive_cca not implemented */ }; static const struct wmi_ops wmi_10_2_ops = { @@ -6484,6 +7425,8 @@ static const struct wmi_ops wmi_10_2_ops = { .gen_addba_send = ath10k_wmi_op_gen_addba_send, .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, .gen_delba_send = ath10k_wmi_op_gen_delba_send, + .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, + /* .gen_pdev_enable_adaptive_cca not implemented */ }; static const struct wmi_ops wmi_10_2_4_ops = { @@ -6545,6 +7488,10 @@ static const struct wmi_ops wmi_10_2_4_ops = { .gen_addba_send = ath10k_wmi_op_gen_addba_send, .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, .gen_delba_send = ath10k_wmi_op_gen_delba_send, + .gen_pdev_get_tpc_config = ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config, + .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, + .gen_pdev_enable_adaptive_cca = + ath10k_wmi_op_gen_pdev_enable_adaptive_cca, /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ @@ -6555,6 +7502,7 @@ static const struct wmi_ops wmi_10_4_ops = { .rx = ath10k_wmi_10_4_op_rx, .map_svc = wmi_10_4_svc_map, + .pull_fw_stats = ath10k_wmi_10_4_op_pull_fw_stats, .pull_scan = ath10k_wmi_op_pull_scan_ev, .pull_mgmt_rx = ath10k_wmi_10_4_op_pull_mgmt_rx_ev, .pull_ch_info = ath10k_wmi_10_4_op_pull_ch_info_ev, @@ -6604,9 +7552,11 @@ static const struct wmi_ops wmi_10_4_ops = { .gen_addba_send = ath10k_wmi_op_gen_addba_send, .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, .gen_delba_send = ath10k_wmi_op_gen_delba_send, + .fw_stats_fill = ath10k_wmi_10_4_op_fw_stats_fill, /* shared with 10.2 */ .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc, + .gen_request_stats = ath10k_wmi_op_gen_request_stats, }; int ath10k_wmi_attach(struct ath10k *ar) @@ -6660,15 +7610,10 @@ int ath10k_wmi_attach(struct ath10k *ar) return 0; } -void ath10k_wmi_detach(struct ath10k *ar) +void ath10k_wmi_free_host_mem(struct ath10k *ar) { int i; - cancel_work_sync(&ar->svc_rdy_work); - - if (ar->svc_rdy_skb) - dev_kfree_skb(ar->svc_rdy_skb); - /* free the host memory chunks requested by firmware */ for (i = 0; i < ar->wmi.num_mem_chunks; i++) { dma_free_coherent(ar->dev, @@ -6679,3 +7624,11 @@ void ath10k_wmi_detach(struct ath10k *ar) ar->wmi.num_mem_chunks = 0; } + +void ath10k_wmi_detach(struct ath10k *ar) +{ + cancel_work_sync(&ar->svc_rdy_work); + + if (ar->svc_rdy_skb) + dev_kfree_skb(ar->svc_rdy_skb); +} diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 52d35032d53e..72a4ef709577 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -73,6 +73,25 @@ struct wmi_cmd_hdr { #define HTC_PROTOCOL_VERSION 0x0002 #define WMI_PROTOCOL_VERSION 0x0002 +/* + * There is no signed version of __le32, so for a temporary solution come + * up with our own version. The idea is from fs/ntfs/types.h. + * + * Use a_ prefix so that it doesn't conflict if we get proper support to + * linux/types.h. + */ +typedef __s32 __bitwise a_sle32; + +static inline a_sle32 a_cpu_to_sle32(s32 val) +{ + return (__force a_sle32)cpu_to_le32(val); +} + +static inline s32 a_sle32_to_cpu(a_sle32 val) +{ + return le32_to_cpu((__force __le32)val); +} + enum wmi_service { WMI_SERVICE_BEACON_OFFLOAD = 0, WMI_SERVICE_SCAN_OFFLOAD, @@ -753,6 +772,7 @@ struct wmi_cmd_map { u32 mu_cal_start_cmdid; u32 set_cca_params_cmdid; u32 pdev_bss_chan_info_request_cmdid; + u32 pdev_enable_adaptive_cca_cmdid; }; /* @@ -1362,6 +1382,9 @@ enum wmi_10_2_cmd_id { WMI_10_2_VDEV_ATF_REQUEST_CMDID, WMI_10_2_PEER_ATF_REQUEST_CMDID, WMI_10_2_PDEV_GET_TEMPERATURE_CMDID, + WMI_10_2_MU_CAL_START_CMDID, + WMI_10_2_SET_LTEU_CONFIG_CMDID, + WMI_10_2_SET_CCA_PARAMS, WMI_10_2_PDEV_UTF_CMDID = WMI_10_2_END_CMDID - 1, }; @@ -3642,8 +3665,18 @@ struct wmi_pdev_get_tpc_config_cmd { __le32 param; } __packed; +#define WMI_TPC_CONFIG_PARAM 1 #define WMI_TPC_RATE_MAX 160 #define WMI_TPC_TX_N_CHAIN 4 +#define WMI_TPC_PREAM_TABLE_MAX 10 +#define WMI_TPC_FLAG 3 +#define WMI_TPC_BUF_SIZE 10 + +enum wmi_tpc_table_type { + WMI_TPC_TABLE_TYPE_CDD = 0, + WMI_TPC_TABLE_TYPE_STBC = 1, + WMI_TPC_TABLE_TYPE_TXBF = 2, +}; enum wmi_tpc_config_event_flag { WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD = 0x1, @@ -3657,7 +3690,7 @@ struct wmi_pdev_tpc_config_event { __le32 phy_mode; __le32 twice_antenna_reduction; __le32 twice_max_rd_power; - s32 twice_antenna_gain; + a_sle32 twice_antenna_gain; __le32 power_limit; __le32 rate_max; __le32 num_tx_chain; @@ -3833,6 +3866,111 @@ struct wmi_pdev_stats_tx { __le32 txop_ovf; } __packed; +struct wmi_10_4_pdev_stats_tx { + /* Num HTT cookies queued to dispatch list */ + __le32 comp_queued; + + /* Num HTT cookies dispatched */ + __le32 comp_delivered; + + /* Num MSDU queued to WAL */ + __le32 msdu_enqued; + + /* Num MPDU queue to WAL */ + __le32 mpdu_enqued; + + /* Num MSDUs dropped by WMM limit */ + __le32 wmm_drop; + + /* Num Local frames queued */ + __le32 local_enqued; + + /* Num Local frames done */ + __le32 local_freed; + + /* Num queued to HW */ + __le32 hw_queued; + + /* Num PPDU reaped from HW */ + __le32 hw_reaped; + + /* Num underruns */ + __le32 underrun; + + /* HW Paused. */ + __le32 hw_paused; + + /* Num PPDUs cleaned up in TX abort */ + __le32 tx_abort; + + /* Num MPDUs requed by SW */ + __le32 mpdus_requed; + + /* excessive retries */ + __le32 tx_ko; + + /* data hw rate code */ + __le32 data_rc; + + /* Scheduler self triggers */ + __le32 self_triggers; + + /* frames dropped due to excessive sw retries */ + __le32 sw_retry_failure; + + /* illegal rate phy errors */ + __le32 illgl_rate_phy_err; + + /* wal pdev continuous xretry */ + __le32 pdev_cont_xretry; + + /* wal pdev tx timeouts */ + __le32 pdev_tx_timeout; + + /* wal pdev resets */ + __le32 pdev_resets; + + /* frames dropped due to non-availability of stateless TIDs */ + __le32 stateless_tid_alloc_failure; + + __le32 phy_underrun; + + /* MPDU is more than txop limit */ + __le32 txop_ovf; + + /* Number of Sequences posted */ + __le32 seq_posted; + + /* Number of Sequences failed queueing */ + __le32 seq_failed_queueing; + + /* Number of Sequences completed */ + __le32 seq_completed; + + /* Number of Sequences restarted */ + __le32 seq_restarted; + + /* Number of MU Sequences posted */ + __le32 mu_seq_posted; + + /* Num MPDUs flushed by SW, HWPAUSED,SW TXABORT(Reset,channel change) */ + __le32 mpdus_sw_flush; + + /* Num MPDUs filtered by HW, all filter condition (TTL expired) */ + __le32 mpdus_hw_filter; + + /* Num MPDUs truncated by PDG + * (TXOP, TBTT, PPDU_duration based on rate, dyn_bw) + */ + __le32 mpdus_truncated; + + /* Num MPDUs that was tried but didn't receive ACK or BA */ + __le32 mpdus_ack_failed; + + /* Num MPDUs that was dropped due to expiry. */ + __le32 mpdus_expired; +} __packed; + struct wmi_pdev_stats_rx { /* Cnts any change in ring routing mid-ppdu */ __le32 mid_ppdu_route_change; @@ -4006,6 +4144,16 @@ struct wmi_10_2_pdev_stats { struct wmi_pdev_stats_extra extra; } __packed; +struct wmi_10_4_pdev_stats { + struct wmi_pdev_stats_base base; + struct wmi_10_4_pdev_stats_tx tx; + struct wmi_pdev_stats_rx rx; + __le32 rx_ovfl_errs; + struct wmi_pdev_stats_mem mem; + __le32 sram_free_size; + struct wmi_pdev_stats_extra extra; +} __packed; + /* * VDEV statistics * TODO: add all VDEV stats here @@ -4047,6 +4195,23 @@ struct wmi_10_2_4_peer_stats { __le32 unknown_value; /* FIXME: what is this word? */ } __packed; +struct wmi_10_4_peer_stats { + struct wmi_mac_addr peer_macaddr; + __le32 peer_rssi; + __le32 peer_rssi_seq_num; + __le32 peer_tx_rate; + __le32 peer_rx_rate; + __le32 current_per; + __le32 retries; + __le32 tx_rate_count; + __le32 max_4ms_frame_len; + __le32 total_sub_frames; + __le32 tx_bytes; + __le32 num_pkt_loss_overflow[4]; + __le32 num_pkt_loss_excess_retry[4]; + __le32 peer_rssi_changed; +} __packed; + struct wmi_10_2_pdev_ext_stats { __le32 rx_rssi_comb; __le32 rx_rssi[4]; @@ -4253,6 +4418,11 @@ enum wmi_rate_preamble { WMI_RATE_PREAMBLE_VHT, }; +#define ATH10K_HW_NSS(rate) (1 + (((rate) >> 4) & 0x3)) +#define ATH10K_HW_PREAMBLE(rate) (((rate) >> 6) & 0x3) +#define ATH10K_HW_RATECODE(rate, nss, preamble) \ + (((preamble) << 6) | ((nss) << 4) | (rate)) + /* Value to disable fixed rate setting */ #define WMI_FIXED_RATE_NONE (0xff) @@ -6060,13 +6230,24 @@ enum wmi_txbf_conf { WMI_TXBF_CONF_AFTER_ASSOC, }; +#define WMI_CCA_DETECT_LEVEL_AUTO 0 +#define WMI_CCA_DETECT_MARGIN_AUTO 0 + +struct wmi_pdev_set_adaptive_cca_params { + __le32 enable; + __le32 cca_detect_level; + __le32 cca_detect_margin; +} __packed; + struct ath10k; struct ath10k_vif; struct ath10k_fw_stats_pdev; struct ath10k_fw_stats_peer; +struct ath10k_fw_stats; int ath10k_wmi_attach(struct ath10k *ar); void ath10k_wmi_detach(struct ath10k *ar); +void ath10k_wmi_free_host_mem(struct ath10k *ar); int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); @@ -6144,4 +6325,16 @@ void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb); int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb); int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, const void *phyerr_buf, int left_len, struct wmi_phyerr_ev_arg *arg); +void ath10k_wmi_main_op_fw_stats_fill(struct ath10k *ar, + struct ath10k_fw_stats *fw_stats, + char *buf); +void ath10k_wmi_10x_op_fw_stats_fill(struct ath10k *ar, + struct ath10k_fw_stats *fw_stats, + char *buf); +size_t ath10k_wmi_fw_stats_num_peers(struct list_head *head); +size_t ath10k_wmi_fw_stats_num_vdevs(struct list_head *head); +void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar, + struct ath10k_fw_stats *fw_stats, + char *buf); + #endif /* _WMI_H_ */ diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index fa6e89e5c421..ba12f7f4061d 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1367,7 +1367,7 @@ struct ath5k_hw { u8 ah_retry_long; u8 ah_retry_short; - u32 ah_use_32khz_clock; + bool ah_use_32khz_clock; u8 ah_coverage_class; bool ah_ack_bitrate_high; diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index a511ef3614b9..81ac8c59f0ec 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2217,7 +2217,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) /* enter / leave wow suspend on first vif always */ first_vif = ath6kl_vif_first(ar); - if (WARN_ON(unlikely(!first_vif)) || + if (WARN_ON(!first_vif) || !ath6kl_cfg80211_ready(first_vif)) return -EIO; @@ -2297,7 +2297,7 @@ static int ath6kl_wow_resume(struct ath6kl *ar) int ret; vif = ath6kl_vif_first(ar); - if (WARN_ON(unlikely(!vif)) || + if (WARN_ON(!vif) || !ath6kl_cfg80211_ready(vif)) return -EIO; @@ -3231,6 +3231,15 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, wait, buf, len, no_cck); } +static int ath6kl_get_antenna(struct wiphy *wiphy, + u32 *tx_ant, u32 *rx_ant) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + *tx_ant = ar->hw.tx_ant; + *rx_ant = ar->hw.rx_ant; + return 0; +} + static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, u16 frame_type, bool reg) @@ -3312,7 +3321,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, } /* fw uses seconds, also make sure that it's >0 */ - interval = max_t(u16, 1, request->interval / 1000); + interval = max_t(u16, 1, request->scan_plans[0].interval); ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, interval, interval, @@ -3447,6 +3456,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, .mgmt_tx = ath6kl_mgmt_tx, .mgmt_frame_register = ath6kl_mgmt_frame_register, + .get_antenna = ath6kl_get_antenna, .sched_scan_start = ath6kl_cfg80211_sscan_start, .sched_scan_stop = ath6kl_cfg80211_sscan_stop, .set_bitrate_mask = ath6kl_cfg80211_set_bitrate, @@ -3634,6 +3644,127 @@ void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif) ar->num_vif--; } +static const char ath6kl_gstrings_sta_stats[][ETH_GSTRING_LEN] = { + /* Common stats names used by many drivers. */ + "tx_pkts_nic", "tx_bytes_nic", "rx_pkts_nic", "rx_bytes_nic", + + /* TX stats. */ + "d_tx_ucast_pkts", "d_tx_bcast_pkts", + "d_tx_ucast_bytes", "d_tx_bcast_bytes", + "d_tx_rts_ok", "d_tx_error", "d_tx_fail", + "d_tx_retry", "d_tx_multi_retry", "d_tx_rts_fail", + "d_tx_tkip_counter_measures", + + /* RX Stats. */ + "d_rx_ucast_pkts", "d_rx_ucast_rate", "d_rx_bcast_pkts", + "d_rx_ucast_bytes", "d_rx_bcast_bytes", "d_rx_frag_pkt", + "d_rx_error", "d_rx_crc_err", "d_rx_keycache_miss", + "d_rx_decrypt_crc_err", "d_rx_duplicate_frames", + "d_rx_mic_err", "d_rx_tkip_format_err", "d_rx_ccmp_format_err", + "d_rx_ccmp_replay_err", + + /* Misc stats. */ + "d_beacon_miss", "d_num_connects", "d_num_disconnects", + "d_beacon_avg_rssi", "d_arp_received", "d_arp_matched", + "d_arp_replied" +}; + +#define ATH6KL_STATS_LEN ARRAY_SIZE(ath6kl_gstrings_sta_stats) + +static int ath6kl_get_sset_count(struct net_device *dev, int sset) +{ + int rv = 0; + + if (sset == ETH_SS_STATS) + rv += ATH6KL_STATS_LEN; + + if (rv == 0) + return -EOPNOTSUPP; + return rv; +} + +static void ath6kl_get_stats(struct net_device *dev, + struct ethtool_stats *stats, + u64 *data) +{ + struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl *ar = vif->ar; + int i = 0; + struct target_stats *tgt_stats; + + memset(data, 0, sizeof(u64) * ATH6KL_STATS_LEN); + + ath6kl_read_tgt_stats(ar, vif); + + tgt_stats = &vif->target_stats; + + data[i++] = tgt_stats->tx_ucast_pkt + tgt_stats->tx_bcast_pkt; + data[i++] = tgt_stats->tx_ucast_byte + tgt_stats->tx_bcast_byte; + data[i++] = tgt_stats->rx_ucast_pkt + tgt_stats->rx_bcast_pkt; + data[i++] = tgt_stats->rx_ucast_byte + tgt_stats->rx_bcast_byte; + + data[i++] = tgt_stats->tx_ucast_pkt; + data[i++] = tgt_stats->tx_bcast_pkt; + data[i++] = tgt_stats->tx_ucast_byte; + data[i++] = tgt_stats->tx_bcast_byte; + data[i++] = tgt_stats->tx_rts_success_cnt; + data[i++] = tgt_stats->tx_err; + data[i++] = tgt_stats->tx_fail_cnt; + data[i++] = tgt_stats->tx_retry_cnt; + data[i++] = tgt_stats->tx_mult_retry_cnt; + data[i++] = tgt_stats->tx_rts_fail_cnt; + data[i++] = tgt_stats->tkip_cnter_measures_invoked; + + data[i++] = tgt_stats->rx_ucast_pkt; + data[i++] = tgt_stats->rx_ucast_rate; + data[i++] = tgt_stats->rx_bcast_pkt; + data[i++] = tgt_stats->rx_ucast_byte; + data[i++] = tgt_stats->rx_bcast_byte; + data[i++] = tgt_stats->rx_frgment_pkt; + data[i++] = tgt_stats->rx_err; + data[i++] = tgt_stats->rx_crc_err; + data[i++] = tgt_stats->rx_key_cache_miss; + data[i++] = tgt_stats->rx_decrypt_err; + data[i++] = tgt_stats->rx_dupl_frame; + data[i++] = tgt_stats->tkip_local_mic_fail; + data[i++] = tgt_stats->tkip_fmt_err; + data[i++] = tgt_stats->ccmp_fmt_err; + data[i++] = tgt_stats->ccmp_replays; + + data[i++] = tgt_stats->cs_bmiss_cnt; + data[i++] = tgt_stats->cs_connect_cnt; + data[i++] = tgt_stats->cs_discon_cnt; + data[i++] = tgt_stats->cs_ave_beacon_rssi; + data[i++] = tgt_stats->arp_received; + data[i++] = tgt_stats->arp_matched; + data[i++] = tgt_stats->arp_replied; + + if (i != ATH6KL_STATS_LEN) { + WARN_ON_ONCE(1); + ath6kl_err("ethtool stats error, i: %d STATS_LEN: %d\n", + i, (int)ATH6KL_STATS_LEN); + } +} + +/* These stats are per NIC, not really per vdev, so we just ignore dev. */ +static void ath6kl_get_strings(struct net_device *dev, u32 sset, u8 *data) +{ + int sz_sta_stats = 0; + + if (sset == ETH_SS_STATS) { + sz_sta_stats = sizeof(ath6kl_gstrings_sta_stats); + memcpy(data, ath6kl_gstrings_sta_stats, sz_sta_stats); + } +} + +static const struct ethtool_ops ath6kl_ethtool_ops = { + .get_drvinfo = cfg80211_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_strings = ath6kl_get_strings, + .get_ethtool_stats = ath6kl_get_stats, + .get_sset_count = ath6kl_get_sset_count, +}; + struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, @@ -3679,6 +3810,8 @@ struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, if (ath6kl_cfg80211_vif_init(vif)) goto err; + netdev_set_default_ethtool_ops(ndev, &ath6kl_ethtool_ops); + if (register_netdevice(ndev)) goto err; @@ -3786,6 +3919,9 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) ath6kl_band_2ghz.ht_cap.ht_supported = false; ath6kl_band_5ghz.ht_cap.cap = 0; ath6kl_band_5ghz.ht_cap.ht_supported = false; + + if (ht) + ath6kl_err("Firmware lacks RSN-CAP-OVERRIDE, so HT (802.11n) is disabled."); } if (test_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES, @@ -3794,11 +3930,18 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff; + ar->hw.tx_ant = 2; + ar->hw.rx_ant = 2; } else { ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; + ar->hw.tx_ant = 1; + ar->hw.rx_ant = 1; } + wiphy->available_antennas_tx = ar->hw.tx_ant; + wiphy->available_antennas_rx = ar->hw.rx_ant; + if (band_2gig) wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; if (band_5gig) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 2b78c863d030..5f3acfe6015e 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -782,6 +782,8 @@ struct ath6kl { u32 refclk_hz; u32 uarttx_pin; u32 testscript_addr; + u8 tx_ant; + u8 rx_ant; enum wmi_phy_cap cap; u32 flags; diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 81ba48d2938b..e2b7809d7886 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -98,6 +98,33 @@ void ath6kl_warn(const char *fmt, ...) } EXPORT_SYMBOL(ath6kl_warn); +int ath6kl_read_tgt_stats(struct ath6kl *ar, struct ath6kl_vif *vif) +{ + long left; + + if (down_interruptible(&ar->sem)) + return -EBUSY; + + set_bit(STATS_UPDATE_PEND, &vif->flags); + + if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) { + up(&ar->sem); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(STATS_UPDATE_PEND, + &vif->flags), WMI_TIMEOUT); + + up(&ar->sem); + + if (left <= 0) + return -ETIMEDOUT; + + return 0; +} +EXPORT_SYMBOL(ath6kl_read_tgt_stats); + #ifdef CONFIG_ATH6KL_DEBUG void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...) @@ -544,42 +571,24 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, char *buf; unsigned int len = 0, buf_len = 1500; int i; - long left; ssize_t ret_cnt; + int rv; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; - tgt_stats = &vif->target_stats; - buf = kzalloc(buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; - if (down_interruptible(&ar->sem)) { + rv = ath6kl_read_tgt_stats(ar, vif); + if (rv < 0) { kfree(buf); - return -EBUSY; + return rv; } - set_bit(STATS_UPDATE_PEND, &vif->flags); - - if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) { - up(&ar->sem); - kfree(buf); - return -EIO; - } - - left = wait_event_interruptible_timeout(ar->event_wq, - !test_bit(STATS_UPDATE_PEND, - &vif->flags), WMI_TIMEOUT); - - up(&ar->sem); - - if (left <= 0) { - kfree(buf); - return -ETIMEDOUT; - } + tgt_stats = &vif->target_stats; len += scnprintf(buf + len, buf_len - len, "\n"); len += scnprintf(buf + len, buf_len - len, "%25s\n", diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 19106ed28961..0614393dd7ae 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -59,6 +59,8 @@ enum ath6kl_war { ATH6KL_WAR_INVALID_RATE, }; +int ath6kl_read_tgt_stats(struct ath6kl *ar, struct ath6kl_vif *vif); + #ifdef CONFIG_ATH6KL_DEBUG void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...); diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index e481f14b9878..fffb65b3e652 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -1085,9 +1085,7 @@ static int htc_setup_tx_complete(struct htc_target *target) send_pkt->completion = NULL; ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0); status = ath6kl_htc_tx_issue(target, send_pkt); - - if (send_pkt != NULL) - htc_reclaim_txctrl_buf(target, send_pkt); + htc_reclaim_txctrl_buf(target, send_pkt); return status; } diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 6e473fa4b13c..6ae0734f86e0 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -715,6 +715,7 @@ static bool check_device_tree(struct ath6kl *ar) board_filename, ret); continue; } + of_node_put(node); return true; } return false; @@ -994,7 +995,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) switch (ie_id) { case ATH6KL_FW_IE_FW_VERSION: strlcpy(ar->wiphy->fw_version, data, - sizeof(ar->wiphy->fw_version)); + min(sizeof(ar->wiphy->fw_version), ie_len+1)); ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw version %s\n", diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.h b/drivers/net/wireless/ath/ath9k/ar9002_phy.h index 6314ae2e93e3..9d17a5375f64 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h @@ -610,8 +610,8 @@ #define AR_PHY_CCA_MIN_GOOD_VAL_9271_2GHZ -127 #define AR_PHY_CCA_MAX_GOOD_VAL_9271_2GHZ -116 -#define AR_PHY_CCA_NOM_VAL_9287_2GHZ -120 +#define AR_PHY_CCA_NOM_VAL_9287_2GHZ -112 #define AR_PHY_CCA_MIN_GOOD_VAL_9287_2GHZ -127 -#define AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ -110 +#define AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ -97 #endif diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 174442beb952..0c391997a2f7 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -1249,7 +1249,8 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0); - if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) || + AR_SREV_9561(ah)) { if (is_2g) REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, @@ -1640,7 +1641,8 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah, skip_tx_iqcal: if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { - if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) { + if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah) || + AR_SREV_9561(ah)) { for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->rxchainmask & (1 << i))) continue; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 79fd3b2dcbde..8b238c15916d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -857,7 +857,7 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) qca956x_1p0_common_rx_gain_table); INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, qca956x_1p0_common_rx_gain_bounds); - INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna, qca956x_1p0_xlna_only); } else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, @@ -942,7 +942,7 @@ static void ar9003_rx_gain_table_mode2(struct ath_hw *ah) ar9462_2p1_baseband_core_mix_rxgain); INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble, ar9462_2p1_baseband_postamble_mix_rxgain); - INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna, ar9462_2p1_baseband_postamble_5g_xlna); } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, @@ -951,7 +951,7 @@ static void ar9003_rx_gain_table_mode2(struct ath_hw *ah) ar9462_2p0_baseband_core_mix_rxgain); INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble, ar9462_2p0_baseband_postamble_mix_rxgain); - INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna, ar9462_2p0_baseband_postamble_5g_xlna); } } @@ -961,12 +961,12 @@ static void ar9003_rx_gain_table_mode3(struct ath_hw *ah) if (AR_SREV_9462_21(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_2p1_common_5g_xlna_only_rxgain); - INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna, ar9462_2p1_baseband_postamble_5g_xlna); } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_2p0_common_5g_xlna_only_rxgain); - INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + INIT_INI_ARRAY(&ah->ini_modes_rxgain_xlna, ar9462_2p0_baseband_postamble_5g_xlna); } } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 1ad66b76749b..201425e7f9cb 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -926,19 +926,18 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, */ if ((ar9003_hw_get_rx_gain_idx(ah) == 2) || (ar9003_hw_get_rx_gain_idx(ah) == 3)) { - REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + REG_WRITE_ARRAY(&ah->ini_modes_rxgain_xlna, modesIndex, regWrites); } - - if (AR_SREV_9561(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0)) - REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna, - modesIndex, regWrites); } if (AR_SREV_9550(ah) || AR_SREV_9561(ah)) REG_WRITE_ARRAY(&ah->ini_modes_rx_gain_bounds, modesIndex, regWrites); + if (AR_SREV_9561(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0)) + REG_WRITE_ARRAY(&ah->ini_modes_rxgain_xlna, + modesIndex, regWrites); /* * TXGAIN initvals. */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index c85c47978e1e..b42f4a963ef4 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -635,6 +635,7 @@ struct ath9k_vif_iter_data { int nstations; /* number of station vifs */ int nwds; /* number of WDS vifs */ int nadhocs; /* number of adhoc vifs */ + int nocbs; /* number of OCB vifs */ struct ieee80211_vif *primary_sta; }; diff --git a/drivers/net/wireless/ath/ath9k/common-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c index 3b289f933405..84afcf78151f 100644 --- a/drivers/net/wireless/ath/ath9k/common-debug.c +++ b/drivers/net/wireless/ath/ath9k/common-debug.c @@ -207,6 +207,7 @@ static ssize_t read_file_phy_err(struct file *file, char __user *user_buf, PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR); PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE); PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR); + PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING); PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); @@ -214,17 +215,24 @@ static ssize_t read_file_phy_err(struct file *file, char __user *user_buf, PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP); PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE); PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART); - PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); + + PHY_ERR("CCK-BLOCKER ERR", ATH9K_PHYERR_CCK_BLOCKER); PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING); PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC); PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL); - PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); - PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP); + PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); + PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); + PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR); PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); + PHY_ERR("HT-ZLF ERR", ATH9K_PHYERR_HT_ZLF); + + PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); + PHY_ERR("GREEN-FIELD ERR", ATH9K_PHYERR_GREEN_FIELD); + PHY_ERR("SPECTRAL ERR", ATH9K_PHYERR_SPECTRAL); if (len > size) len = size; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index da32c8faad94..6de64cface3c 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -741,8 +741,8 @@ static int read_file_misc(struct seq_file *file, void *data) i++, (int)(ctx->assigned), iter_data.naps, iter_data.nstations, iter_data.nmeshes, iter_data.nwds); - seq_printf(file, " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", - iter_data.nadhocs, sc->cur_chan->nvifs, + seq_printf(file, " ADHOC: %i OCB: %i TOTAL: %hi BEACON-VIF: %hi\n", + iter_data.nadhocs, iter_data.nocbs, sc->cur_chan->nvifs, sc->nbcnvifs); } diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 10c02f5cbc5e..165dd202c365 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -17,12 +17,8 @@ #include <asm/unaligned.h> #include "htc.h" -/* identify firmware images */ -#define FIRMWARE_AR7010_1_1 "htc_7010.fw" -#define FIRMWARE_AR9271 "htc_9271.fw" - -MODULE_FIRMWARE(FIRMWARE_AR7010_1_1); -MODULE_FIRMWARE(FIRMWARE_AR9271); +MODULE_FIRMWARE(HTC_7010_MODULE_FW); +MODULE_FIRMWARE(HTC_9271_MODULE_FW); static struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x0cf3, 0x9271) }, /* Atheros */ @@ -1080,12 +1076,88 @@ static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev) device_unlock(parent); } +static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context); + +/* taken from iwlwifi */ +static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev, + bool first) +{ + char index[8], *chip; + int ret; + + if (first) { + if (htc_use_dev_fw) { + hif_dev->fw_minor_index = FIRMWARE_MINOR_IDX_MAX + 1; + sprintf(index, "%s", "dev"); + } else { + hif_dev->fw_minor_index = FIRMWARE_MINOR_IDX_MAX; + sprintf(index, "%d", hif_dev->fw_minor_index); + } + } else { + hif_dev->fw_minor_index--; + sprintf(index, "%d", hif_dev->fw_minor_index); + } + + /* test for FW 1.3 */ + if (MAJOR_VERSION_REQ == 1 && hif_dev->fw_minor_index == 3) { + const char *filename; + + if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info)) + filename = FIRMWARE_AR7010_1_1; + else + filename = FIRMWARE_AR9271; + + /* expected fw locations: + * - htc_9271.fw (stable version 1.3, depricated) + */ + snprintf(hif_dev->fw_name, sizeof(hif_dev->fw_name), + "%s", filename); + + } else if (hif_dev->fw_minor_index < FIRMWARE_MINOR_IDX_MIN) { + dev_err(&hif_dev->udev->dev, "no suitable firmware found!\n"); + + return -ENOENT; + } else { + if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info)) + chip = "7010"; + else + chip = "9271"; + + /* expected fw locations: + * - ath9k_htc/htc_9271-1.dev.0.fw (development version) + * - ath9k_htc/htc_9271-1.4.0.fw (stable version) + */ + snprintf(hif_dev->fw_name, sizeof(hif_dev->fw_name), + "%s/htc_%s-%d.%s.0.fw", HTC_FW_PATH, + chip, MAJOR_VERSION_REQ, index); + } + + ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, + &hif_dev->udev->dev, GFP_KERNEL, + hif_dev, ath9k_hif_usb_firmware_cb); + if (ret) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: Async request for firmware %s failed\n", + hif_dev->fw_name); + return ret; + } + + dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", + hif_dev->fw_name); + + return ret; +} + static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context) { struct hif_device_usb *hif_dev = context; int ret; if (!fw) { + ret = ath9k_hif_request_firmware(hif_dev, false); + if (!ret) + return; + dev_err(&hif_dev->udev->dev, "ath9k_htc: Failed to get firmware %s\n", hif_dev->fw_name); @@ -1215,27 +1287,11 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, init_completion(&hif_dev->fw_done); - /* Find out which firmware to load */ - - if (IS_AR7010_DEVICE(id->driver_info)) - hif_dev->fw_name = FIRMWARE_AR7010_1_1; - else - hif_dev->fw_name = FIRMWARE_AR9271; - - ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, - &hif_dev->udev->dev, GFP_KERNEL, - hif_dev, ath9k_hif_usb_firmware_cb); - if (ret) { - dev_err(&hif_dev->udev->dev, - "ath9k_htc: Async request for firmware %s failed\n", - hif_dev->fw_name); + ret = ath9k_hif_request_firmware(hif_dev, true); + if (ret) goto err_fw_req; - } - dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", - hif_dev->fw_name); - - return 0; + return ret; err_fw_req: usb_set_intfdata(interface, NULL); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index 51496e74b83e..7c2ef7ecd98b 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -17,8 +17,26 @@ #ifndef HTC_USB_H #define HTC_USB_H +/* old firmware images */ +#define FIRMWARE_AR7010_1_1 "htc_7010.fw" +#define FIRMWARE_AR9271 "htc_9271.fw" + +/* supported Major FW version */ #define MAJOR_VERSION_REQ 1 #define MINOR_VERSION_REQ 3 +/* minimal and maximal supported Minor FW version. */ +#define FIRMWARE_MINOR_IDX_MAX 4 +#define FIRMWARE_MINOR_IDX_MIN 3 +#define HTC_FW_PATH "ath9k_htc" + +#define HTC_9271_MODULE_FW HTC_FW_PATH "/htc_9271-" \ + __stringify(MAJOR_VERSION_REQ) \ + "." __stringify(FIRMWARE_MINOR_IDX_MAX) ".0.fw" +#define HTC_7010_MODULE_FW HTC_FW_PATH "/htc_7010-" \ + __stringify(MAJOR_VERSION_REQ) \ + "." __stringify(FIRMWARE_MINOR_IDX_MAX) ".0.fw" + +extern int htc_use_dev_fw; #define IS_AR7010_DEVICE(_v) (((_v) == AR9280_USB) || ((_v) == AR9287_USB)) @@ -101,7 +119,8 @@ struct hif_device_usb { struct usb_anchor reg_in_submitted; struct usb_anchor mgmt_submitted; struct sk_buff *remain_skb; - const char *fw_name; + char fw_name[32]; + int fw_minor_index; int rx_remain_len; int rx_pkt_len; int rx_transfer_len; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 1e84882f8c5b..8647ab77c019 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -38,6 +38,10 @@ static int ath9k_ps_enable; module_param_named(ps_enable, ath9k_ps_enable, int, 0444); MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); +int htc_use_dev_fw = 0; +module_param_named(use_dev_fw, htc_use_dev_fw, int, 0444); +MODULE_PARM_DESC(use_dev_fw, "Use development FW version"); + #ifdef CONFIG_MAC80211_LEDS int ath9k_htc_led_blink = 1; module_param_named(blink, ath9k_htc_led_blink, int, 0444); @@ -736,7 +740,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_MESH_POINT); + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_OCB); hw->wiphy->iface_combinations = &if_comb; hw->wiphy->n_iface_combinations = 1; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 172a9ff4aaab..a680a970b7f7 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1659,7 +1659,7 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size) + u16 tid, u16 *ssn, u8 buf_size, bool amsdu) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1dd0339de372..41382f89abe1 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -385,7 +385,7 @@ static void ath9k_hw_init_config(struct ath_hw *ah) ah->config.dma_beacon_response_time = 1; ah->config.sw_beacon_response_time = 6; - ah->config.cwm_ignore_extcca = 0; + ah->config.cwm_ignore_extcca = false; ah->config.analog_shiftreg = 1; ah->config.rx_intr_mitigation = true; @@ -1241,6 +1241,7 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) break; } /* fall through */ + case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: set |= AR_STA_ID1_STA_AP; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index e8454db17634..831a54415a25 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -332,14 +332,14 @@ enum ath9k_hw_hang_checks { struct ath9k_ops_config { int dma_beacon_response_time; int sw_beacon_response_time; - u32 cwm_ignore_extcca; + bool cwm_ignore_extcca; u32 pcie_waen; u8 analog_shiftreg; u32 ofdm_trig_low; u32 ofdm_trig_high; u32 cck_trig_high; u32 cck_trig_low; - u32 enable_paprd; + bool enable_paprd; int serialize_regmode; bool rx_intr_mitigation; bool tx_intr_mitigation; @@ -919,7 +919,7 @@ struct ath_hw { struct ar5416IniArray iniCckfirJapan2484; struct ar5416IniArray iniModes_9271_ANI_reg; struct ar5416IniArray ini_radio_post_sys2ant; - struct ar5416IniArray ini_modes_rxgain_5g_xlna; + struct ar5416IniArray ini_modes_rxgain_xlna; struct ar5416IniArray ini_modes_rxgain_bb_core; struct ar5416IniArray ini_modes_rxgain_bb_postamble; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 57f95f2dca5b..2e2b92ba96b8 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -855,7 +855,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_WDS); + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_OCB); if (ath9k_is_chanctx_enabled()) hw->wiphy->interface_modes |= @@ -880,6 +881,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); + hw->extra_tx_headroom = 4; hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index e55fa11894b6..7fbf7f965f61 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -209,21 +209,25 @@ enum ath9k_phyerr { ATH9K_PHYERR_OFDM_POWER_DROP = 21, ATH9K_PHYERR_OFDM_SERVICE = 22, ATH9K_PHYERR_OFDM_RESTART = 23, - ATH9K_PHYERR_FALSE_RADAR_EXT = 24, + ATH9K_PHYERR_CCK_BLOCKER = 24, ATH9K_PHYERR_CCK_TIMING = 25, ATH9K_PHYERR_CCK_HEADER_CRC = 26, ATH9K_PHYERR_CCK_RATE_ILLEGAL = 27, + ATH9K_PHYERR_CCK_LENGTH_ILLEGAL = 28, + ATH9K_PHYERR_CCK_POWER_DROP = 29, ATH9K_PHYERR_CCK_SERVICE = 30, ATH9K_PHYERR_CCK_RESTART = 31, - ATH9K_PHYERR_CCK_LENGTH_ILLEGAL = 32, - ATH9K_PHYERR_CCK_POWER_DROP = 33, - ATH9K_PHYERR_HT_CRC_ERROR = 34, - ATH9K_PHYERR_HT_LENGTH_ILLEGAL = 35, - ATH9K_PHYERR_HT_RATE_ILLEGAL = 36, + ATH9K_PHYERR_HT_CRC_ERROR = 32, + ATH9K_PHYERR_HT_LENGTH_ILLEGAL = 33, + ATH9K_PHYERR_HT_RATE_ILLEGAL = 34, + ATH9K_PHYERR_HT_ZLF = 35, + + ATH9K_PHYERR_FALSE_RADAR_EXT = 36, + ATH9K_PHYERR_GREEN_FIELD = 37, + ATH9K_PHYERR_SPECTRAL = 38, - ATH9K_PHYERR_SPECTRAL = 38, ATH9K_PHYERR_MAX = 39, }; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c27143ba9ffb..d184e682e636 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -938,6 +938,9 @@ static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data, if (avp->assoc && !iter_data->primary_sta) iter_data->primary_sta = vif; break; + case NL80211_IFTYPE_OCB: + iter_data->nocbs++; + break; case NL80211_IFTYPE_ADHOC: iter_data->nadhocs++; if (vif->bss_conf.enable_beacon) @@ -1111,6 +1114,8 @@ void ath9k_calculate_summary_state(struct ath_softc *sc, if (iter_data.nmeshes) ah->opmode = NL80211_IFTYPE_MESH_POINT; + else if (iter_data.nocbs) + ah->opmode = NL80211_IFTYPE_OCB; else if (iter_data.nwds) ah->opmode = NL80211_IFTYPE_AP; else if (iter_data.nadhocs) @@ -1760,7 +1765,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath9k_calculate_summary_state(sc, avp->chanctx); } - if (changed & BSS_CHANGED_IBSS) { + if ((changed & BSS_CHANGED_IBSS) || + (changed & BSS_CHANGED_OCB)) { memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); common->curaid = bss_conf->aid; ath9k_hw_write_associd(sc->sc_ah); @@ -1856,7 +1862,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size) + u16 tid, u16 *ssn, u8 buf_size, bool amsdu) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index d3189daf9996..994daf6c6297 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -403,7 +403,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) (sc->cur_chan->nvifs <= 1) && !(sc->cur_chan->rxfilter & FIF_BCN_PRBRESP_PROMISC)) rfilt |= ATH9K_RX_FILTER_MYBEACON; - else + else if (sc->sc_ah->opmode != NL80211_IFTYPE_OCB) rfilt |= ATH9K_RX_FILTER_BEACON; if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 170c209f99b8..19d3d64416bf 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1415,7 +1415,7 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size) + u16 tid, u16 *ssn, u8 buf_size, bool amsdu) { struct ar9170 *ar = hw->priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 924135b8e575..d66533cbc38a 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -453,7 +453,7 @@ static void carl9170_rx_phy_status(struct ar9170 *ar, /* post-process RSSI */ for (i = 0; i < 7; i++) if (phy->rssi[i] & 0x80) - phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f; + phy->rssi[i] = ((~phy->rssi[i] & 0x7f) + 1) & 0x7f; /* TODO: we could do something with phy_errors */ status->signal = ar->noise[0] + phy->rssi_combined; diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 656ce42b339a..2303ef96299d 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -21,12 +21,6 @@ #include "dfs_pri_detector.h" #include "ath.h" -/* - * tolerated deviation of radar time stamp in usecs on both sides - * TODO: this might need to be HW-dependent - */ -#define PRI_TOLERANCE 16 - /** * struct radar_types - contains array of patterns defined for one DFS domain * @domain: DFS regulatory domain @@ -121,7 +115,7 @@ static const struct radar_detector_specs jp_radar_ref_types[] = { JP_PATTERN(4, 0, 5, 150, 230, 1, 23, 50, false), JP_PATTERN(5, 6, 10, 200, 500, 1, 16, 50, false), JP_PATTERN(6, 11, 20, 200, 500, 1, 12, 50, false), - JP_PATTERN(7, 50, 100, 1000, 2000, 1, 20, 50, false), + JP_PATTERN(7, 50, 100, 1000, 2000, 1, 3, 50, false), JP_PATTERN(5, 0, 1, 333, 333, 1, 9, 50, false), }; @@ -290,10 +284,10 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) if (cd == NULL) return false; - dpd->last_pulse_ts = event->ts; /* reset detector on time stamp wraparound, caused by TSF reset */ if (event->ts < dpd->last_pulse_ts) dpd_reset(dpd); + dpd->last_pulse_ts = event->ts; /* do type individual pattern matching */ for (i = 0; i < dpd->num_radar_types; i++) { diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.h b/drivers/net/wireless/ath/dfs_pattern_detector.h index 25a43d632f90..92be3530e9b5 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/dfs_pattern_detector.h @@ -21,6 +21,11 @@ #include <linux/list.h> #include <linux/nl80211.h> +/* tolerated deviation of radar time stamp in usecs on both sides + * TODO: this might need to be HW-dependent + */ +#define PRI_TOLERANCE 16 + /** * struct ath_dfs_pool_stats - DFS Statistics for global pools */ diff --git a/drivers/net/wireless/ath/dfs_pri_detector.c b/drivers/net/wireless/ath/dfs_pri_detector.c index cc5c592fc4c0..05b0464c6b92 100644 --- a/drivers/net/wireless/ath/dfs_pri_detector.c +++ b/drivers/net/wireless/ath/dfs_pri_detector.c @@ -25,6 +25,9 @@ struct ath_dfs_pool_stats global_dfs_pool_stats = {}; #define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) #define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) +#define GET_PRI_TO_USE(MIN, MAX, RUNTIME) \ + (MIN + PRI_TOLERANCE == MAX - PRI_TOLERANCE ? \ + MIN + PRI_TOLERANCE : RUNTIME) /** * struct pulse_elem - elements in pulse queue @@ -243,7 +246,8 @@ static bool pseq_handler_create_sequences(struct pri_detector *pde, ps.count_falses = 0; ps.first_ts = p->ts; ps.last_ts = ts; - ps.pri = ts - p->ts; + ps.pri = GET_PRI_TO_USE(pde->rs->pri_min, + pde->rs->pri_max, ts - p->ts); ps.dur = ps.pri * (pde->rs->ppb - 1) + 2 * pde->rs->max_pri_tolerance; diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index 086549b732b9..f8dfa05b290a 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -79,6 +79,7 @@ static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch) struct wcn36xx_dxe_ctl *cur_ctl = NULL; int i; + spin_lock_init(&ch->lock); for (i = 0; i < ch->desc_num; i++) { cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL); if (!cur_ctl) @@ -169,7 +170,7 @@ void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn) wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch); } -static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch) +static int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn_ch) { struct wcn36xx_dxe_desc *cur_dxe = NULL; struct wcn36xx_dxe_desc *prev_dxe = NULL; @@ -178,7 +179,7 @@ static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch) int i; size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc); - wcn_ch->cpu_addr = dma_alloc_coherent(NULL, size, &wcn_ch->dma_addr, + wcn_ch->cpu_addr = dma_alloc_coherent(dev, size, &wcn_ch->dma_addr, GFP_KERNEL); if (!wcn_ch->cpu_addr) return -ENOMEM; @@ -270,7 +271,7 @@ static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch) return 0; } -static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl) +static int wcn36xx_dxe_fill_skb(struct device *dev, struct wcn36xx_dxe_ctl *ctl) { struct wcn36xx_dxe_desc *dxe = ctl->desc; struct sk_buff *skb; @@ -279,7 +280,7 @@ static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl) if (skb == NULL) return -ENOMEM; - dxe->dst_addr_l = dma_map_single(NULL, + dxe->dst_addr_l = dma_map_single(dev, skb_tail_pointer(skb), WCN36XX_PKT_SIZE, DMA_FROM_DEVICE); @@ -297,7 +298,7 @@ static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn, cur_ctl = wcn_ch->head_blk_ctl; for (i = 0; i < wcn_ch->desc_num; i++) { - wcn36xx_dxe_fill_skb(cur_ctl); + wcn36xx_dxe_fill_skb(wcn->dev, cur_ctl); cur_ctl = cur_ctl->next; } @@ -345,7 +346,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) { - struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl; + struct wcn36xx_dxe_ctl *ctl; struct ieee80211_tx_info *info; unsigned long flags; @@ -354,23 +355,25 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) * completely full head and tail are pointing to the same element * and while-do will not make any cycles. */ + spin_lock_irqsave(&ch->lock, flags); + ctl = ch->tail_blk_ctl; do { if (ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK) break; if (ctl->skb) { - dma_unmap_single(NULL, ctl->desc->src_addr_l, + dma_unmap_single(wcn->dev, ctl->desc->src_addr_l, ctl->skb->len, DMA_TO_DEVICE); info = IEEE80211_SKB_CB(ctl->skb); if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) { /* Keep frame until TX status comes */ ieee80211_free_txskb(wcn->hw, ctl->skb); } - spin_lock_irqsave(&ctl->skb_lock, flags); + spin_lock(&ctl->skb_lock); if (wcn->queues_stopped) { wcn->queues_stopped = false; ieee80211_wake_queues(wcn->hw); } - spin_unlock_irqrestore(&ctl->skb_lock, flags); + spin_unlock(&ctl->skb_lock); ctl->skb = NULL; } @@ -379,6 +382,7 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) !(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)); ch->tail_blk_ctl = ctl; + spin_unlock_irqrestore(&ch->lock, flags); } static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) @@ -474,7 +478,7 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) { skb = ctl->skb; dma_addr = dxe->dst_addr_l; - wcn36xx_dxe_fill_skb(ctl); + wcn36xx_dxe_fill_skb(wcn->dev, ctl); switch (ch->ch_type) { case WCN36XX_DXE_CH_RX_L: @@ -491,7 +495,7 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, wcn36xx_warn("Unknown channel\n"); } - dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE, + dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE, DMA_FROM_DEVICE); wcn36xx_rx_skb(wcn, skb); ctl = ctl->next; @@ -540,7 +544,7 @@ int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn) 16 - (WCN36XX_BD_CHUNK_SIZE % 8); s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H; - cpu_addr = dma_alloc_coherent(NULL, s, &wcn->mgmt_mem_pool.phy_addr, + cpu_addr = dma_alloc_coherent(wcn->dev, s, &wcn->mgmt_mem_pool.phy_addr, GFP_KERNEL); if (!cpu_addr) goto out_err; @@ -555,7 +559,7 @@ int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn) 16 - (WCN36XX_BD_CHUNK_SIZE % 8); s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L; - cpu_addr = dma_alloc_coherent(NULL, s, &wcn->data_mem_pool.phy_addr, + cpu_addr = dma_alloc_coherent(wcn->dev, s, &wcn->data_mem_pool.phy_addr, GFP_KERNEL); if (!cpu_addr) goto out_err; @@ -574,13 +578,13 @@ out_err: void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn) { if (wcn->mgmt_mem_pool.virt_addr) - dma_free_coherent(NULL, wcn->mgmt_mem_pool.chunk_size * + dma_free_coherent(wcn->dev, wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H, wcn->mgmt_mem_pool.virt_addr, wcn->mgmt_mem_pool.phy_addr); if (wcn->data_mem_pool.virt_addr) { - dma_free_coherent(NULL, wcn->data_mem_pool.chunk_size * + dma_free_coherent(wcn->dev, wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L, wcn->data_mem_pool.virt_addr, wcn->data_mem_pool.phy_addr); @@ -596,12 +600,14 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, struct wcn36xx_dxe_desc *desc = NULL; struct wcn36xx_dxe_ch *ch = NULL; unsigned long flags; + int ret; ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch; + spin_lock_irqsave(&ch->lock, flags); ctl = ch->head_blk_ctl; - spin_lock_irqsave(&ctl->next->skb_lock, flags); + spin_lock(&ctl->next->skb_lock); /* * If skb is not null that means that we reached the tail of the ring @@ -611,10 +617,11 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, if (NULL != ctl->next->skb) { ieee80211_stop_queues(wcn->hw); wcn->queues_stopped = true; - spin_unlock_irqrestore(&ctl->next->skb_lock, flags); + spin_unlock(&ctl->next->skb_lock); + spin_unlock_irqrestore(&ch->lock, flags); return -EBUSY; } - spin_unlock_irqrestore(&ctl->next->skb_lock, flags); + spin_unlock(&ctl->next->skb_lock); ctl->skb = NULL; desc = ctl->desc; @@ -640,10 +647,11 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, desc = ctl->desc; if (ctl->bd_cpu_addr) { wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } - desc->src_addr_l = dma_map_single(NULL, + desc->src_addr_l = dma_map_single(wcn->dev, ctl->skb->data, ctl->skb->len, DMA_TO_DEVICE); @@ -679,7 +687,10 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, ch->reg_ctrl, ch->def_ctrl); } - return 0; + ret = 0; +unlock: + spin_unlock_irqrestore(&ch->lock, flags); + return ret; } int wcn36xx_dxe_init(struct wcn36xx *wcn) @@ -696,7 +707,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /***************************************/ /* Init descriptors for TX LOW channel */ /***************************************/ - wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch); + wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_l_ch); wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool); /* Write channel head to a NEXT register */ @@ -714,7 +725,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /***************************************/ /* Init descriptors for TX HIGH channel */ /***************************************/ - wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch); + wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_h_ch); wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool); /* Write channel head to a NEXT register */ @@ -734,7 +745,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /***************************************/ /* Init descriptors for RX LOW channel */ /***************************************/ - wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch); + wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_l_ch); /* For RX we need to preallocated buffers */ wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch); @@ -764,7 +775,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /***************************************/ /* Init descriptors for RX HIGH channel */ /***************************************/ - wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch); + wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_h_ch); /* For RX we need to prealocat buffers */ wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch); diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h index 35ee7e966bd2..3eca4f9594f2 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.h +++ b/drivers/net/wireless/ath/wcn36xx/dxe.h @@ -243,6 +243,7 @@ struct wcn36xx_dxe_ctl { }; struct wcn36xx_dxe_ch { + spinlock_t lock; /* protects head/tail ptrs */ enum wcn36xx_dxe_ch_type ch_type; void *cpu_addr; dma_addr_t dma_addr; diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 900e72a089d8..7c169abdbafe 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -859,7 +859,7 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct wcn36xx *wcn = hw->priv; struct wcn36xx_sta *sta_priv = NULL; diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig index ce8c0381825e..6dfedc8bd6a3 100644 --- a/drivers/net/wireless/ath/wil6210/Kconfig +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -1,5 +1,6 @@ config WIL6210 tristate "Wilocity 60g WiFi card wil6210 support" + select WANT_DEV_COREDUMP depends on CFG80211 depends on PCI default n diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index 64b432625fbb..fdf63d5fe82b 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -17,6 +17,7 @@ wil6210-y += pmc.o wil6210-$(CONFIG_WIL6210_TRACING) += trace.o wil6210-y += wil_platform.o wil6210-y += ethtool.o +wil6210-y += wil_crash_dump.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index d1a1e160ef31..97bc186f9728 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1373,6 +1373,12 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) } } spin_unlock_bh(&p->tid_rx_lock); + seq_printf(s, + "Rx invalid frame: non-data %lu, short %lu, large %lu\n", + p->stats.rx_non_data_frame, + p->stats.rx_short_frame, + p->stats.rx_large_frame); + seq_puts(s, "Rx/MCS:"); for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs); mcs++) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index a371f036d054..50c136e843c4 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -236,7 +236,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH); - if (likely(test_bit(wil_status_reset_done, wil->status))) { + if (likely(test_bit(wil_status_fwready, wil->status))) { if (likely(test_bit(wil_status_napi_en, wil->status))) { wil_dbg_txrx(wil, "NAPI(Rx) schedule\n"); need_unmask = false; @@ -286,7 +286,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; /* clear also all VRING interrupts */ isr &= ~(BIT(25) - 1UL); - if (likely(test_bit(wil_status_reset_done, wil->status))) { + if (likely(test_bit(wil_status_fwready, wil->status))) { wil_dbg_txrx(wil, "NAPI(Tx) schedule\n"); need_unmask = false; napi_schedule(&wil->napi_tx); @@ -347,7 +347,12 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) wil6210_mask_irq_misc(wil); if (isr & ISR_MISC_FW_ERROR) { - wil_err(wil, "Firmware error detected\n"); + u32 fw_assert_code = wil_r(wil, RGF_FW_ASSERT_CODE); + u32 ucode_assert_code = wil_r(wil, RGF_UCODE_ASSERT_CODE); + + wil_err(wil, + "Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n", + fw_assert_code, ucode_assert_code); clear_bit(wil_status_fwready, wil->status); /* * do not clear @isr here - we do 2-nd part in thread @@ -359,7 +364,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) if (isr & ISR_MISC_FW_READY) { wil_dbg_irq(wil, "IRQ: FW ready\n"); wil_cache_mbox_regs(wil); - set_bit(wil_status_reset_done, wil->status); + set_bit(wil_status_mbox_ready, wil->status); /** * Actual FW ready indicated by the * WMI_FW_READY_EVENTID @@ -386,6 +391,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr); if (isr & ISR_MISC_FW_ERROR) { + wil_fw_core_dump(wil); wil_notify_fw_error(wil); isr &= ~ISR_MISC_FW_ERROR; wil_fw_error_recovery(wil); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 2fb04c51da53..bb69a5949aea 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -203,11 +203,13 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, * - disconnect single STA, already disconnected * - disconnect all * - * For "disconnect all", there are 2 options: + * For "disconnect all", there are 3 options: * - bssid == NULL + * - bssid is broadcast address (ff:ff:ff:ff:ff:ff) * - bssid is our MAC address */ - if (bssid && memcmp(ndev->dev_addr, bssid, ETH_ALEN)) { + if (bssid && !is_broadcast_ether_addr(bssid) && + !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) { cid = wil_find_cid(wil, bssid); wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n", bssid, cid, reason_code); @@ -420,7 +422,7 @@ static void wil_connect_worker(struct work_struct *work) wil->sta[cid].status = wil_sta_connected; netif_tx_wake_all_queues(ndev); } else { - wil->sta[cid].status = wil_sta_unused; + wil_disconnect_cid(wil, cid, WLAN_REASON_UNSPECIFIED, true); } } @@ -765,6 +767,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) if (wil->hw_version == HW_VER_UNKNOWN) return -ENODEV; + set_bit(wil_status_resetting, wil->status); + cancel_work_sync(&wil->disconnect_worker); wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); wil_bcast_fini(wil); @@ -851,6 +855,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) void wil_fw_error_recovery(struct wil6210_priv *wil) { wil_dbg_misc(wil, "starting fw error recovery\n"); + + if (test_bit(wil_status_resetting, wil->status)) { + wil_info(wil, "Reset already in progress\n"); + return; + } + wil->recovery_state = fw_recovery_pending; schedule_work(&wil->fw_error_worker); } diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index feff1ef10fb3..1a3142c332e1 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -260,6 +260,7 @@ static const struct pci_device_id wil6210_pcie_ids[] = { MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); #ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int wil6210_suspend(struct device *dev, bool is_runtime) { @@ -307,7 +308,6 @@ static int wil6210_resume(struct device *dev, bool is_runtime) return rc; } -#ifdef CONFIG_PM_SLEEP static int wil6210_pm_suspend(struct device *dev) { return wil6210_suspend(dev, false); diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c index 8a8cdc61b25b..5ca0307a3274 100644 --- a/drivers/net/wireless/ath/wil6210/pmc.c +++ b/drivers/net/wireless/ath/wil6210/pmc.c @@ -110,7 +110,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil, */ for (i = 0; i < num_descriptors; i++) { struct vring_tx_desc *_d = &pmc->pring_va[i]; - struct vring_tx_desc dd, *d = ⅆ + struct vring_tx_desc dd = {}, *d = ⅆ int j = 0; pmc->descriptors[i].va = dma_alloc_coherent(dev, diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 9238c1ac23dd..e3d1be82f314 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -205,6 +205,32 @@ out: spin_unlock(&sta->tid_rx_lock); } +/* process BAR frame, called in NAPI context */ +void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq) +{ + struct wil_sta_info *sta = &wil->sta[cid]; + struct wil_tid_ampdu_rx *r; + + spin_lock(&sta->tid_rx_lock); + + r = sta->tid_rx[tid]; + if (!r) { + wil_err(wil, "BAR for non-existing CID %d TID %d\n", cid, tid); + goto out; + } + if (seq_less(seq, r->head_seq_num)) { + wil_err(wil, "BAR Seq 0x%03x preceding head 0x%03x\n", + seq, r->head_seq_num); + goto out; + } + wil_dbg_txrx(wil, "BAR: CID %d TID %d Seq 0x%03x head 0x%03x\n", + cid, tid, seq, r->head_seq_num); + wil_release_reorder_frames(wil, r, seq); + +out: + spin_unlock(&sta->tid_rx_lock); +} + struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, int size, u16 ssn) { diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 6229110d558a..3bc9bc0efbac 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -358,6 +358,13 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, } } +/* similar to ieee80211_ version, but FC contain only 1-st byte */ +static inline int wil_is_back_req(u8 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); +} + /** * reap 1 frame from @swhead * @@ -379,14 +386,16 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, u16 dmalen; u8 ftype; int cid; - int i = (int)vring->swhead; + int i; struct wil_net_stats *stats; BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb)); +again: if (unlikely(wil_vring_is_empty(vring))) return NULL; + i = (int)vring->swhead; _d = &vring->va[i].rx; if (unlikely(!(_d->dma.status & RX_DMA_STATUS_DU))) { /* it is not error, we just reached end of Rx done area */ @@ -398,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, wil_vring_advance_head(vring, 1); if (!skb) { wil_err(wil, "No Rx skb at [%d]\n", i); - return NULL; + goto again; } d = wil_skb_rxdesc(skb); *d = *_d; @@ -409,13 +418,17 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, trace_wil6210_rx(i, d); wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", i, dmalen); - wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, + wil_hex_dump_txrx("RxD ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); + cid = wil_rxdesc_cid(d); + stats = &wil->sta[cid].stats; + if (unlikely(dmalen > sz)) { wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); + stats->rx_large_frame++; kfree_skb(skb); - return NULL; + goto again; } skb_trim(skb, dmalen); @@ -424,8 +437,6 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb_headlen(skb), false); - cid = wil_rxdesc_cid(d); - stats = &wil->sta[cid].stats; stats->last_mcs_rx = wil_rxdesc_mcs(d); if (stats->last_mcs_rx < ARRAY_SIZE(stats->rx_per_mcs)) stats->rx_per_mcs[stats->last_mcs_rx]++; @@ -437,24 +448,47 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, /* no extra checks if in sniffer mode */ if (ndev->type != ARPHRD_ETHER) return skb; - /* - * Non-data frames may be delivered through Rx DMA channel (ex: BAR) + /* Non-data frames may be delivered through Rx DMA channel (ex: BAR) * Driver should recognize it by frame type, that is found * in Rx descriptor. If type is not data, it is 802.11 frame as is */ ftype = wil_rxdesc_ftype(d) << 2; if (unlikely(ftype != IEEE80211_FTYPE_DATA)) { - wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype); - /* TODO: process it */ + u8 fc1 = wil_rxdesc_fc1(d); + int mid = wil_rxdesc_mid(d); + int tid = wil_rxdesc_tid(d); + u16 seq = wil_rxdesc_seq(d); + + wil_dbg_txrx(wil, + "Non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n", + fc1, mid, cid, tid, seq); + stats->rx_non_data_frame++; + if (wil_is_back_req(fc1)) { + wil_dbg_txrx(wil, + "BAR: MID %d CID %d TID %d Seq 0x%03x\n", + mid, cid, tid, seq); + wil_rx_bar(wil, cid, tid, seq); + } else { + /* print again all info. One can enable only this + * without overhead for printing every Rx frame + */ + wil_dbg_txrx(wil, + "Unhandled non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n", + fc1, mid, cid, tid, seq); + wil_hex_dump_txrx("RxD ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + } kfree_skb(skb); - return NULL; + goto again; } if (unlikely(skb->len < ETH_HLEN + snaplen)) { wil_err(wil, "Short frame, len = %d\n", skb->len); - /* TODO: process it (i.e. BAR) */ + stats->rx_short_frame++; kfree_skb(skb); - return NULL; + goto again; } /* L4 IDENT is on when HW calculated checksum, check status @@ -1208,6 +1242,7 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring, int tcp_hdr_len; int skb_net_hdr_len; int gso_type; + int rc = -EINVAL; wil_dbg_txrx(wil, "%s() %d bytes to vring %d\n", __func__, skb->len, vring_index); @@ -1299,8 +1334,9 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring, len, rem_data, descs_used); if (descs_used == avail) { - wil_err(wil, "TSO: ring overflow\n"); - goto dma_error; + wil_err_ratelimited(wil, "TSO: ring overflow\n"); + rc = -ENOMEM; + goto mem_error; } lenmss = min_t(int, rem_data, len); @@ -1322,8 +1358,10 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring, headlen -= lenmss; } - if (unlikely(dma_mapping_error(dev, pa))) - goto dma_error; + if (unlikely(dma_mapping_error(dev, pa))) { + wil_err(wil, "TSO: DMA map page error\n"); + goto mem_error; + } _desc = &vring->va[i].tx; @@ -1422,8 +1460,8 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring, } /* advance swhead */ - wil_dbg_txrx(wil, "TSO: Tx swhead %d -> %d\n", swhead, vring->swhead); wil_vring_advance_head(vring, descs_used); + wil_dbg_txrx(wil, "TSO: Tx swhead %d -> %d\n", swhead, vring->swhead); /* make sure all writes to descriptors (shared memory) are done before * committing them to HW @@ -1433,8 +1471,7 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring, wil_w(wil, vring->hwtail, vring->swhead); return 0; -dma_error: - wil_err(wil, "TSO: DMA map page error\n"); +mem_error: while (descs_used > 0) { struct wil_ctx *ctx; @@ -1445,14 +1482,11 @@ dma_error: _desc->dma.status = TX_DMA_STATUS_DU; ctx = &vring->ctx[i]; wil_txdesc_unmap(dev, d, ctx); - if (ctx->skb) - dev_kfree_skb_any(ctx->skb); memset(ctx, 0, sizeof(*ctx)); descs_used--; } - err_exit: - return -EINVAL; + return rc; } static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, @@ -1528,8 +1562,11 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, _d = &vring->va[i].tx; pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(dev, pa))) + if (unlikely(dma_mapping_error(dev, pa))) { + wil_err(wil, "Tx[%2d] failed to map fragment\n", + vring_index); goto dma_error; + } vring->ctx[i].mapped_as = wil_mapped_as_page; wil_tx_desc_map(d, pa, len, vring_index); /* no need to check return code - @@ -1589,9 +1626,6 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, _d->dma.status = TX_DMA_STATUS_DU; wil_txdesc_unmap(dev, d, ctx); - if (ctx->skb) - dev_kfree_skb_any(ctx->skb); - memset(ctx, 0, sizeof(*ctx)); } @@ -1633,7 +1667,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) goto drop; } if (unlikely(!test_bit(wil_status_fwconnected, wil->status))) { - wil_err(wil, "FW not connected\n"); + wil_err_ratelimited(wil, "FW not connected\n"); goto drop; } if (unlikely(wil->wdev->iftype == NL80211_IFTYPE_MONITOR)) { diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 82a8f9a030e7..ee7c7b4b9a17 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -464,6 +464,12 @@ static inline int wil_rxdesc_subtype(struct vring_rx_desc *d) return WIL_GET_BITS(d->mac.d0, 12, 15); } +/* 1-st byte (with frame type/subtype) of FC field */ +static inline u8 wil_rxdesc_fc1(struct vring_rx_desc *d) +{ + return (u8)(WIL_GET_BITS(d->mac.d0, 10, 15) << 2); +} + static inline int wil_rxdesc_seq(struct vring_rx_desc *d) { return WIL_GET_BITS(d->mac.d0, 16, 27); @@ -501,6 +507,7 @@ static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb) void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev); void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb); +void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq); struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, int size, u16 ssn); void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index dd4ea926b8e3..ade5f3b8274b 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -246,6 +246,10 @@ struct RGF_ICR { #define RGF_USER_JTAG_DEV_ID (0x880b34) /* device ID */ #define JTAG_DEV_ID_SPARROW_B0 (0x2632072f) +/* crash codes for FW/Ucode stored here */ +#define RGF_FW_ASSERT_CODE (0x91f020) +#define RGF_UCODE_ASSERT_CODE (0x91f028) + enum { HW_VER_UNKNOWN, HW_VER_SPARROW_B0, /* JTAG_DEV_ID_SPARROW_B0 */ @@ -398,13 +402,14 @@ struct vring_tx_data { }; enum { /* for wil6210_priv.status */ - wil_status_fwready = 0, + wil_status_fwready = 0, /* FW operational */ wil_status_fwconnecting, wil_status_fwconnected, wil_status_dontscan, - wil_status_reset_done, + wil_status_mbox_ready, /* MBOX structures ready */ wil_status_irqen, /* FIXME: interrupts enabled - for debug */ wil_status_napi_en, /* NAPI enabled protected by wil->mutex */ + wil_status_resetting, /* reset in progress */ wil_status_last /* keep last */ }; @@ -465,6 +470,9 @@ struct wil_net_stats { unsigned long tx_bytes; unsigned long tx_errors; unsigned long rx_dropped; + unsigned long rx_non_data_frame; + unsigned long rx_short_frame; + unsigned long rx_large_frame; u16 last_mcs_rx; u64 rx_per_mcs[WIL_MCS_MAX + 1]; }; @@ -820,4 +828,6 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime); int wil_suspend(struct wil6210_priv *wil, bool is_runtime); int wil_resume(struct wil6210_priv *wil, bool is_runtime); +void wil_fw_core_dump(struct wil6210_priv *wil); + #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c new file mode 100644 index 000000000000..7e70934990ae --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "wil6210.h" +#include <linux/devcoredump.h> + +static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil, + u32 *out_dump_size, u32 *out_host_min) +{ + int i; + const struct fw_map *map; + u32 host_min, host_max, tmp_max; + + if (!out_dump_size) + return -EINVAL; + + /* calculate the total size of the unpacked crash dump */ + BUILD_BUG_ON(ARRAY_SIZE(fw_mapping) == 0); + map = &fw_mapping[0]; + host_min = map->host; + host_max = map->host + (map->to - map->from); + + for (i = 1; i < ARRAY_SIZE(fw_mapping); i++) { + map = &fw_mapping[i]; + + if (map->host < host_min) + host_min = map->host; + + tmp_max = map->host + (map->to - map->from); + if (tmp_max > host_max) + host_max = tmp_max; + } + + *out_dump_size = host_max - host_min; + if (out_host_min) + *out_host_min = host_min; + + return 0; +} + +static int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, + u32 size) +{ + int i; + const struct fw_map *map; + void *data; + u32 host_min, dump_size, offset, len; + + if (wil_fw_get_crash_dump_bounds(wil, &dump_size, &host_min)) { + wil_err(wil, "%s: fail to obtain crash dump size\n", __func__); + return -EINVAL; + } + + if (dump_size > size) { + wil_err(wil, "%s: not enough space for dump. Need %d have %d\n", + __func__, dump_size, size); + return -EINVAL; + } + + /* copy to crash dump area */ + for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { + map = &fw_mapping[i]; + + data = (void * __force)wil->csr + HOSTADDR(map->host); + len = map->to - map->from; + offset = map->host - host_min; + + wil_dbg_misc(wil, "%s() - dump %s, size %d, offset %d\n", + __func__, fw_mapping[i].name, len, offset); + + wil_memcpy_fromio_32((void * __force)(dest + offset), + (const void __iomem * __force)data, len); + } + + return 0; +} + +void wil_fw_core_dump(struct wil6210_priv *wil) +{ + void *fw_dump_data; + u32 fw_dump_size; + + if (wil_fw_get_crash_dump_bounds(wil, &fw_dump_size, NULL)) { + wil_err(wil, "%s: fail to get fw dump size\n", __func__); + return; + } + + fw_dump_data = vzalloc(fw_dump_size); + if (!fw_dump_data) + return; + + if (wil_fw_copy_crash_dump(wil, fw_dump_data, fw_dump_size)) { + vfree(fw_dump_data); + return; + } + /* fw_dump_data will be free in device coredump release function + * after 5 min + */ + dev_coredumpv(wil_to_dev(wil), fw_dump_data, fw_dump_size, GFP_KERNEL); + wil_info(wil, "%s: fw core dumped, size %d bytes\n", __func__, + fw_dump_size); +} diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 2f35d4c51f34..6ed26baca0e5 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -293,12 +293,6 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) /* ignore MAC address, we already have it from the boot loader */ snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), "%d", wil->fw_version); -} - -static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, - int len) -{ - wil_dbg_wmi(wil, "WMI: got FW ready event\n"); wil_set_recovery_state(wil, fw_recovery_idle); set_bit(wil_status_fwready, wil->status); @@ -684,13 +678,22 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) spin_unlock_bh(&sta->tid_rx_lock); } +/** + * Some events are ignored for purpose; and need not be interpreted as + * "unhandled events" + */ +static void wmi_evt_ignore(struct wil6210_priv *wil, int id, void *d, int len) +{ + wil_dbg_wmi(wil, "Ignore event 0x%04x len %d\n", id, len); +} + static const struct { int eventid; void (*handler)(struct wil6210_priv *wil, int eventid, void *data, int data_len); } wmi_evt_handlers[] = { {WMI_READY_EVENTID, wmi_evt_ready}, - {WMI_FW_READY_EVENTID, wmi_evt_fw_ready}, + {WMI_FW_READY_EVENTID, wmi_evt_ignore}, {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt}, {WMI_TX_MGMT_PACKET_EVENTID, wmi_evt_tx_mgmt}, {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, @@ -701,6 +704,7 @@ static const struct { {WMI_RCP_ADDBA_REQ_EVENTID, wmi_evt_addba_rx_req}, {WMI_DELBA_EVENTID, wmi_evt_delba}, {WMI_VRING_EN_EVENTID, wmi_evt_vring_en}, + {WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_ignore}, }; /* @@ -720,7 +724,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) ulong flags; unsigned n; - if (!test_bit(wil_status_reset_done, wil->status)) { + if (!test_bit(wil_status_mbox_ready, wil->status)) { wil_err(wil, "Reset in progress. Cannot handle WMI event\n"); return; } @@ -1120,7 +1124,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); cmd.sniffer_cfg.phy_support = cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) - ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); + ? WMI_SNIFFER_CP : WMI_SNIFFER_BOTH_PHYS); } else { /* Initialize offload (in non-sniffer mode). * Linux IP stack always calculates IP checksum |