# Base git commit: 0c3836482481 # (Linux 6.10) # # Author: Russell King (Oracle) (Mon 29 Jul 19:08:28 BST 2024) # Committer: Russell King (Oracle) (Tue 6 Aug 10:23:36 BST 2024) # # net: wlcore: clean up open BA sessions when removing STA # # When a peer is removed, it may have been moved out of range with open # BA sessions on the AP. Each open BA session consumes firmware resources # and if allowed to increase, will eventually cause the firmware to run # BA sessions. Not only does this impact firmware, but also the driver's # state tracking as well. # # Fix this by ensuring that all BA sessions associated with a STA are # properly removed from the firmware and driver state just before the STA # is removed. # # Signed-off-by: Russell King (Oracle) # # 16b91c21d6ed8b32bc44aad674b938301c900250 # drivers/net/wireless/ti/wlcore/main.c | 19 +++++++++++++++++++ # 1 file changed, 19 insertions(+) # # Author: Russell King (Oracle) (Sun 28 Jul 14:32:12 BST 2024) # Committer: Russell King (Oracle) (Tue 6 Aug 10:23:23 BST 2024) # # net: wlcore: expand "no fw rx ba" message to include hlid # # Signed-off-by: Russell King (Oracle) # # 9adf976d80627ec5605b80bc5e46ed20aafb2470 # drivers/net/wireless/ti/wlcore/acx.c | 3 ++- # 1 file changed, 2 insertions(+), 1 deletion(-) # # Author: Russell King (Oracle) (Sun 28 Jul 15:39:54 BST 2024) # Committer: Russell King (Oracle) (Tue 6 Aug 10:23:15 BST 2024) # # net: wlcore: add del_peer debug # # Signed-off-by: Russell King (Oracle) # # f67a21d09e666fee21886149d3f20ccdacd61b81 # drivers/net/wireless/ti/wlcore/cmd.c | 2 ++ # 1 file changed, 2 insertions(+) # # Author: Russell King (Oracle) (Tue 18 Jun 23:58:38 BST 2024) # Committer: Russell King (Oracle) (Tue 6 Aug 10:23:05 BST 2024) # # net: wlcore: fw log reading update # # Signed-off-by: Russell King (Oracle) # # d164dcf7fa4348a05773eebf10669538f2666422 # drivers/net/wireless/ti/wlcore/event.c | 3 ++- # 1 file changed, 2 insertions(+), 1 deletion(-) # # Author: Russell King (Oracle) (Sun 26 May 19:34:45 BST 2024) # Committer: Russell King (Oracle) (Tue 6 Aug 10:23:04 BST 2024) # # net: wlcore: dump firmware queued packets # # Dump some information about the firmware queued packets that have # not flushed. # # Signed-off-by: Russell King (Oracle) # # b90ffa5e053100dd4ffdfdb01bd1d56a9cf36d3a # drivers/net/wireless/ti/wlcore/debugfs.c | 11 +++++++--- # drivers/net/wireless/ti/wlcore/tx.c | 36 +++++++++++++++++++++++++++++--- # 2 files changed, 41 insertions(+), 6 deletions(-) # # Author: Russell King (Oracle) (Sat 27 Apr 14:11:54 BST 2024) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:13 BST 2024) # # net: wlcore: wl12xx_cmd_add_peer debug # # 4b9ee280360efb13dffc62bf35f9eb877365e15a # drivers/net/wireless/ti/wlcore/cmd.c | 3 +++ # 1 file changed, 3 insertions(+) # # Author: Russell King (Oracle) (Thu 23 May 08:37:30 BST 2024) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:11 BST 2024) # # net: wlcore: debugfs: export information on queued firmware frames # # Add information about the frames queued to firmware into the debugfs # driver state output. # # Signed-off-by: Russell King (Oracle) # # f7eb28017c36b20e917e5d203e381a9db7503f08 # drivers/net/wireless/ti/wlcore/debugfs.c | 29 ++++++++++++++++++++++++++++- # 1 file changed, 28 insertions(+), 1 deletion(-) # # Author: Russell King (Oracle) (Thu 18 Apr 09:53:32 BST 2024) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:10 BST 2024) # # net: wlcore: debugfs: export hlid mac address/allocated packets # # Signed-off-by: Russell King (Oracle) # # 4651f7ff5261add5f68857a0ab94a702b07fc0ca # drivers/net/wireless/ti/wlcore/debugfs.c | 12 ++++++++++++ # 1 file changed, 12 insertions(+) # # Author: Russell King (Oracle) (Thu 11 May 17:09:50 BST 2023) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:08 BST 2024) # # net: wlcore: debugfs: export tx_total # # Signed-off-by: Russell King (Oracle) # # a8b1f080627af374f3be36e7130609a3b03aa2fa # drivers/net/wireless/ti/wlcore/debugfs.c | 1 + # drivers/net/wireless/ti/wlcore/main.c | 1 + # drivers/net/wireless/ti/wlcore/wlcore.h | 1 + # 3 files changed, 3 insertions(+) # # Author: Russell King (Oracle) (Sat 20 Apr 22:02:29 BST 2024) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:07 BST 2024) # # net: wlcore: add support for AES-CMAC # # Add support for the AES-CMAC cipher to TI Wireless. Based on TI's # patch: # # https://git.ti.com/cgit/wilink8-wlan/build-utilites/tree/patches/kernel_patches/4.19.38/0007-Adding-support-to-IGTK-key-AES-CMAC128-in-the-wlcore.patch?h=r8.9&id=a2ee50aa5190ed3b334373d6cd09b1bff56ffcf7 # # Signed-off-by: Russell King (Oracle) # # 97a96f404f0cba3c78eb0fdc44904f83ed6bdbec # drivers/net/wireless/ti/wlcore/cmd.h | 1 + # drivers/net/wireless/ti/wlcore/main.c | 4 ++++ # 2 files changed, 5 insertions(+) # # Author: Russell King (Oracle) (Thu 23 May 08:37:47 BST 2024) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:06 BST 2024) # # net: wlcore: implement .flush_sta operation # # Signed-off-by: Russell King (Oracle) # # 328f79074bf17a4bcb3178e4bea4c7059ab7831b # drivers/net/wireless/ti/wlcore/main.c | 10 ++++++++ # drivers/net/wireless/ti/wlcore/tx.c | 47 +++++++++++++++++++++++++++++++++++ # drivers/net/wireless/ti/wlcore/tx.h | 1 + # 3 files changed, 58 insertions(+) # # Author: Russell King (Oracle) (Sun 14 Apr 18:11:54 BST 2024) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:05 BST 2024) # # net: wl18xx: allow firmwares > 8.9.0.x.58 # # wlcore firmware versions are structured thusly: # # chip.if-type.major.sub-type.minor # e.g. 8 9 0 0 58 # # With WL18xx ignoring the major firmware version, looking for a # firmware version that conforms to: # # chip >= 8 # if-type >= 9 # major (don't care) # sub-type (don't care) # minor >= 58 # # Each test is satisfied if the value read from the firmware is greater # than the minimum, but if it is equal (or we don't care about the # field), then the next field is checked. # # Thus it doesn't recognise 8.9.1.x.0 as being newer than 8.9.0.x.58 # since the major and sub-type numbers are "don't care" and the minor # needs to be greater or equal to 58. # # We need to change the major version from "ignore" to "0" for this later # firmware to be correctly detected, and allow the dual-firmware version # support to work. # # Signed-off-by: Russell King (Oracle) # # cd073bbcc1a2e4992da81c74f089585f4c70298c # drivers/net/wireless/ti/wl18xx/wl18xx.h | 2 +- # 1 file changed, 1 insertion(+), 1 deletion(-) # # Author: Russell King (Oracle) (Sun 14 Apr 16:57:06 BST 2024) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:03 BST 2024) # # net: wl18xx: add support for reading 8.9.1 fw status # # Add the necessary code to read the 8.9.1 firmware status into the # driver private status structure, augmenting the 8.9.0 firmware # status code. New structure layout taken from: # # https://git.ti.com/cgit/wilink8-wlan/build-utilites/tree/patches/kernel_patches/4.19.38/0023-wlcore-Fixing-PN-drift-on-encrypted-link-after-recov.patch?h=r8.9&id=a2ee50aa5190ed3b334373d6cd09b1bff56ffcf7 # # Signed-off-by: Russell King (Oracle) # # a84dc4bba16e0e6133362ee899a6dfac15276d25 # drivers/net/wireless/ti/wl18xx/main.c | 71 ++++++++++++++++++++++++++++++++- # drivers/net/wireless/ti/wl18xx/wl18xx.h | 60 ++++++++++++++++++++++++++++ # 2 files changed, 129 insertions(+), 2 deletions(-) # # Author: Russell King (Oracle) (Thu 11 May 13:23:37 BST 2023) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:02 BST 2024) # # net: wlcore: add pn16 support # # TI Wl18xx firmware adds a "pn16" field for AES and TKIP keys as per # their patch: # # https://git.ti.com/cgit/wilink8-wlan/build-utilites/tree/patches/kernel_patches/4.19.38/0023-wlcore-Fixing-PN-drift-on-encrypted-link-after-recov.patch?h=r8.9&id=a2ee50aa5190ed3b334373d6cd09b1bff56ffcf7 # # Add support for this, but rather than requiring the field to be # present (which would break existing firmwares), make it optional. # # Signed-off-by: Russell King (Oracle) # # 1ff74c71f8f25b8bc10b557f907683fe813bb651 # drivers/net/wireless/ti/wlcore/cmd.c | 9 ++++ # drivers/net/wireless/ti/wlcore/main.c | 89 +++++++++++++++++++++++++++++-- # drivers/net/wireless/ti/wlcore/wlcore_i.h | 4 ++ # 3 files changed, 97 insertions(+), 5 deletions(-) # # Author: Russell King (Oracle) (Thu 11 May 13:25:44 BST 2023) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:01 BST 2024) # # net: wlcore: store AP encryption key type # # Updates for WL18xx firmware 8.9.1.x.x need to know the AP encryption # key type. Store this when a new key is set. Patch extracted from: # # https://git.ti.com/cgit/wilink8-wlan/build-utilites/tree/patches/kernel_patches/4.19.38/0023-wlcore-Fixing-PN-drift-on-encrypted-link-after-recov.patch?h=r8.9&id=a2ee50aa5190ed3b334373d6cd09b1bff56ffcf7 # # Signed-off-by: Russell King (Oracle) # # 3639c4be11f68ca57f7e6697af5aecd14d14039a # drivers/net/wireless/ti/wlcore/main.c | 4 ++++ # 1 file changed, 4 insertions(+) # # Author: Russell King (Oracle) (Sun 5 May 12:05:37 BST 2024) # Committer: Russell King (Oracle) (Tue 30 Jul 15:20:00 BST 2024) # # net: wlcore: pass "status" to wlcore_hw_convert_fw_status() # # wlcore_fw_status() is passed a pointer to the struct wl_fw_status to # decode the status into, which is always wl->fw_status. Rather than # referencing wl->fw_status within wlcore_fw_status(), use the supplied # argument so that we access this member in a consistent manner. # # Signed-off-by: Russell King (Oracle) # # 053834b19505864dd42000517b61fb5cc052982f # drivers/net/wireless/ti/wlcore/main.c | 2 +- # 1 file changed, 1 insertion(+), 1 deletion(-) # # Author: Russell King (Oracle) (Thu 11 May 15:15:14 BST 2023) # Committer: Russell King (Oracle) (Tue 30 Jul 15:19:59 BST 2024) # # net: wlcore: improve code in wlcore_fw_status() # # Referring to status->counters.tx_lnk_free_pkts[i] multiple times leads # to less efficient code. Cache this value in a local variable. This # also makes the code clearer. # # Signed-off-by: Russell King (Oracle) # # 3aafa791f1667390015605dc8cc226f5db59b113 # drivers/net/wireless/ti/wlcore/main.c | 8 ++++---- # 1 file changed, 4 insertions(+), 4 deletions(-) # # Author: Russell King (Oracle) (Thu 11 May 17:07:26 BST 2023) # Committer: Russell King (Oracle) (Tue 30 Jul 15:19:57 BST 2024) # # net: wl18xx: make wl18xx_tx_immediate_complete() more efficient # # wl18xx_tx_immediate_complete() iterates through the completed transmit # descriptors in a circular fashion, and in doing so uses a modulus # operation that is not a power of two. This leads to inefficient code # generation, which can be easily solved by providing a helper to # increment to the next descriptor. Use this more efficient solution. # # Signed-off-by: Russell King (Oracle) # # 7070ff102db17fe54710206657da0cf8cae29d08 # drivers/net/wireless/ti/wl18xx/tx.c | 13 ++++++++++--- # 1 file changed, 10 insertions(+), 3 deletions(-) # # Author: Russell King (Oracle) (Fri 12 May 15:07:10 BST 2023) # Committer: Russell King (Oracle) (Tue 30 Jul 15:19:56 BST 2024) # # net: wlcore: correctness fix fwlog reading # # Fix the calculation of clear_offset, which may overflow the end of # the buffer. However, this is harmless if it does because in that case # it will be recalculated when we copy the chunk of messages at the # start of the buffer. # # Signed-off-by: Russell King (Oracle) # # e0ebeb81f4851b8ba22566711f74bafecf1faca2 # drivers/net/wireless/ti/wlcore/event.c | 2 +- # 1 file changed, 1 insertion(+), 1 deletion(-) # diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 2ccac1cdec01..39d8eebb9b6e 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1177,8 +1177,49 @@ static int wl18xx_hw_init(struct wl1271 *wl) return ret; } -static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status, - struct wl_fw_status *fw_status) +static void wl18xx_convert_fw_status_8_9_1(struct wl1271 *wl, + void *raw_fw_status, + struct wl_fw_status *fw_status) +{ + struct wl18xx_fw_status_8_9_1 *int_fw_status = raw_fw_status; + + fw_status->intr = le32_to_cpu(int_fw_status->intr); + fw_status->fw_rx_counter = int_fw_status->fw_rx_counter; + fw_status->drv_rx_counter = int_fw_status->drv_rx_counter; + fw_status->tx_results_counter = int_fw_status->tx_results_counter; + fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs; + + fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime); + fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap); + fw_status->link_fast_bitmap = + le32_to_cpu(int_fw_status->link_fast_bitmap); + fw_status->total_released_blks = + le32_to_cpu(int_fw_status->total_released_blks); + fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total); + + fw_status->counters.tx_released_pkts = + int_fw_status->counters.tx_released_pkts; + fw_status->counters.tx_lnk_free_pkts = + int_fw_status->counters.tx_lnk_free_pkts; + fw_status->counters.tx_lnk_sec_pn16 = + int_fw_status->counters.tx_lnk_sec_pn16; + fw_status->counters.tx_voice_released_blks = + int_fw_status->counters.tx_voice_released_blks; + fw_status->counters.tx_last_rate = + int_fw_status->counters.tx_last_rate; + fw_status->counters.tx_last_rate_mbps = + int_fw_status->counters.tx_last_rate_mbps; + fw_status->counters.hlid = + int_fw_status->counters.hlid; + + fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr); + + fw_status->priv = &int_fw_status->priv; +} + +static void wl18xx_convert_fw_status_8_9_0(struct wl1271 *wl, + void *raw_fw_status, + struct wl_fw_status *fw_status) { struct wl18xx_fw_status *int_fw_status = raw_fw_status; @@ -1214,6 +1255,15 @@ static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status, fw_status->priv = &int_fw_status->priv; } +static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status, + struct wl_fw_status *fw_status) +{ + if (wl->chip.fw_ver[FW_VER_MAJOR] == 0) + wl18xx_convert_fw_status_8_9_0(wl, raw_fw_status, fw_status); + else + wl18xx_convert_fw_status_8_9_1(wl, raw_fw_status, fw_status); +} + static void wl18xx_set_tx_desc_csum(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb) @@ -1515,12 +1565,29 @@ static int wl18xx_handle_static_data(struct wl1271 *wl, { struct wl18xx_static_data_priv *static_data_priv = (struct wl18xx_static_data_priv *) static_data->priv; + size_t fw_status_len; strscpy(wl->chip.phy_fw_ver_str, static_data_priv->phy_version, sizeof(wl->chip.phy_fw_ver_str)); wl1271_info("PHY firmware version: %s", static_data_priv->phy_version); + /* Adjust the firmware status size according to the firmware version */ + if (wl->chip.fw_ver[FW_VER_MAJOR] == 0) + fw_status_len = sizeof(struct wl18xx_fw_status); + else + fw_status_len = sizeof(struct wl18xx_fw_status_8_9_1); + + if (wl->fw_status_len != fw_status_len) { + void *new_status = krealloc(wl->raw_fw_status, fw_status_len, + GFP_KERNEL | __GFP_ZERO); + if (!new_status) + return -ENOMEM; + + wl->raw_fw_status = new_status; + wl->fw_status_len = fw_status_len; + } + return 0; } diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c index 55d9b0861c53..beef393853ef 100644 --- a/drivers/net/wireless/ti/wl18xx/tx.c +++ b/drivers/net/wireless/ti/wl18xx/tx.c @@ -129,6 +129,14 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) wl1271_free_tx_id(wl, id); } +static u8 wl18xx_next_tx_idx(u8 idx) +{ + if (++idx >= WL18XX_FW_MAX_TX_STATUS_DESC) + idx = 0; + + return idx; +} + void wl18xx_tx_immediate_complete(struct wl1271 *wl) { struct wl18xx_fw_status_priv *status_priv = @@ -161,9 +169,8 @@ void wl18xx_tx_immediate_complete(struct wl1271 *wl) return; } - for (i = priv->last_fw_rls_idx; - i != status_priv->fw_release_idx; - i = (i + 1) % WL18XX_FW_MAX_TX_STATUS_DESC) { + for (i = priv->last_fw_rls_idx; i != status_priv->fw_release_idx; + i = wl18xx_next_tx_idx(i)) { wl18xx_tx_complete_packet(wl, status_priv->released_tx_desc[i]); diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index b642e0c437bb..de6c671c4be6 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -13,7 +13,7 @@ /* minimum FW required for driver */ #define WL18XX_CHIP_VER 8 #define WL18XX_IFTYPE_VER 9 -#define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE +#define WL18XX_MAJOR_VER 0 #define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE #define WL18XX_MINOR_VER 58 @@ -155,6 +155,66 @@ struct wl18xx_fw_status { struct wl18xx_fw_status_priv priv; } __packed; +struct wl18xx_fw_packet_counters_8_9_1 { + /* Cumulative counter of released packets per AC */ + u8 tx_released_pkts[NUM_TX_QUEUES]; + + /* Cumulative counter of freed packets per HLID */ + u8 tx_lnk_free_pkts[WL18XX_MAX_LINKS]; + + /* PN16 of last TKIP/AES seq-num per HLID */ + __le16 tx_lnk_sec_pn16[WL18XX_MAX_LINKS]; + + /* Cumulative counter of released Voice memory blocks */ + u8 tx_voice_released_blks; + + /* Tx rate of the last transmitted packet */ + u8 tx_last_rate; + + /* Tx rate or Tx rate estimate pre-calculated by fw in mbps units */ + u8 tx_last_rate_mbps; + + /* hlid for which the rates were reported */ + u8 hlid; +} __packed; + +/* FW status registers */ +struct wl18xx_fw_status_8_9_1 { + __le32 intr; + u8 fw_rx_counter; + u8 drv_rx_counter; + u8 reserved; + u8 tx_results_counter; + __le32 rx_pkt_descs[WL18XX_NUM_RX_DESCRIPTORS]; + + __le32 fw_localtime; + + /* + * A bitmap (where each bit represents a single HLID) + * to indicate if the station is in PS mode. + */ + __le32 link_ps_bitmap; + + /* + * A bitmap (where each bit represents a single HLID) to indicate + * if the station is in Fast mode + */ + __le32 link_fast_bitmap; + + /* Cumulative counter of total released mem blocks since FW-reset */ + __le32 total_released_blks; + + /* Size (in Memory Blocks) of TX pool */ + __le32 tx_total; + + struct wl18xx_fw_packet_counters_8_9_1 counters; + + __le32 log_start_addr; + + /* Private status to be used by the lower drivers */ + struct wl18xx_fw_status_priv priv; +} __packed; + #define WL18XX_PHY_VERSION_MAX_LEN 20 struct wl18xx_static_data_priv { diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index e820fe694121..9667d0ae533e 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -1434,7 +1434,8 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, /* sometimes we can't start the session */ if (ret == CMD_STATUS_NO_RX_BA_SESSION) { - wl1271_warning("no fw rx ba on tid %d", tid_index); + wl1271_warning("no fw rx ba on tid %u hlid %u", tid_index, + peer_hlid); ret = -EBUSY; goto out; } diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 92fc2d456c2c..281090e005e4 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -332,6 +332,14 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) wl->fw_status->counters.tx_lnk_free_pkts[link]; wl->links[link].wlvif = wlvif; + /* + * Take the last sec_pn16 value from the current FW status. On recovery, + * we might not have fw_status yet, and tx_lnk_sec_pn16[] will be NULL. + */ + if (wl->fw_status->counters.tx_lnk_sec_pn16) + wl->links[link].prev_sec_pn16 = + le16_to_cpu(wl->fw_status->counters.tx_lnk_sec_pn16[link]); + /* * Take saved value for total freed packets from wlvif, in case this is * recovery/resume @@ -360,6 +368,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) wl->links[*hlid].allocated_pkts = 0; wl->links[*hlid].prev_freed_pkts = 0; + wl->links[*hlid].prev_sec_pn16 = 0; wl->links[*hlid].ba_bitmap = 0; eth_zero_addr(wl->links[*hlid].addr); @@ -1566,6 +1575,9 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates, wlvif->band)); + pr_info("wl18xx: add_peer: %pM hlid=%u rates=0x%x queues=0x%x\n", + sta->addr, hlid, cmd->supported_rates, sta->uapsd_queues); + wl1271_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x", cmd->supported_rates, sta->uapsd_queues); @@ -1597,6 +1609,8 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, goto out; } + pr_info("wl18xx: del_peer: hlid=%u\n", hlid); + cmd->hlid = hlid; /* We never send a deauth, mac80211 is in charge of this */ cmd->reason_opcode = 0; diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 4c2f2608ef3b..ab6121b92b00 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -456,6 +456,7 @@ enum wl1271_cmd_key_type { KEY_TKIP = 2, KEY_AES = 3, KEY_GEM = 4, + KEY_IGTK = 5, }; struct wl1271_cmd_set_keys { diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index eb3d3f0e0b4d..effd81e9f6cc 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -423,7 +423,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, char *buf; struct wl12xx_vif *wlvif; -#define DRIVER_STATE_BUF_LEN 1024 +#define DRIVER_STATE_BUF_LEN 4096 buf = kmalloc(DRIVER_STATE_BUF_LEN, GFP_KERNEL); if (!buf) @@ -457,6 +457,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel, wlvif->p2p ? "P2P-GO" : "AP"); + DRIVER_STATE_PRINT_INT(tx_blocks_total); DRIVER_STATE_PRINT_INT(tx_blocks_available); DRIVER_STATE_PRINT_INT(tx_allocated_blocks); DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]); @@ -492,6 +493,50 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str); DRIVER_STATE_PRINT_INT(recovery_count); + wl12xx_for_each_wlvif_ap(wl, wlvif) { + int hlid; + for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, wl->num_links) { + res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res, + "links[%d].allocated_pkts = %u\n", + hlid, wl->links[hlid].allocated_pkts); + res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res, + "links[%d].addr = %pM\n", + hlid, wl->links[hlid].addr); + } + } + + if (wl->fw_status) { + DRIVER_STATE_PRINT(fw_status->fw_localtime, "%u"); + } + + { + struct wl1271_tx_hw_descr *tx_desc; + struct ieee80211_hdr *hdr; + struct sk_buff *skb; + u32 start, end; + u16 lt; + int id; + + for (id = 0; id < WLCORE_MAX_TX_DESCRIPTORS; id++) { + skb = wl->tx_frames[id]; + if (!skb) + continue; + + tx_desc = (struct wl1271_tx_hw_descr *)skb->data; + hdr = (void *)(tx_desc + 1); + start = le32_to_cpu(tx_desc->start_time); + lt = le16_to_cpu(tx_desc->life_time); + end = start + 1024 * lt; + + res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res, + "tx_frames[%2u] hlid %2u st %10u lt %u et %10u SA %pM DA %pM FC %04x\n", + id, tx_desc->hlid, start, lt, end, + ieee80211_get_SA(hdr), + ieee80211_get_DA(hdr), + hdr->frame_control); + } + } + #undef DRIVER_STATE_PRINT_INT #undef DRIVER_STATE_PRINT_LONG #undef DRIVER_STATE_PRINT_HEX diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 2499dc908305..4190ce26e587 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -72,7 +72,8 @@ int wlcore_event_fw_logger(struct wl1271 *wl) buff_read_ptr = le32_to_cpu(fw_log.buff_read_ptr); if (buff_read_ptr < buff_start_ptr || buff_read_ptr >= buff_end_ptr) { - wl1271_error("buffer read pointer out of bounds: %x not in (%x-%x)\n", + actual_len = 0; + wl1271_error("buffer read pointer out of bounds: %x not in (%x-%x)", buff_read_ptr, buff_start_ptr, buff_end_ptr); goto free_out; } @@ -83,7 +84,7 @@ int wlcore_event_fw_logger(struct wl1271 *wl) /* Copy initial part up to the end of ring buffer */ len = min(actual_len, available_len); wl12xx_copy_fwlog(wl, &buffer[start_loc], len); - clear_ptr = addr_ptr + start_loc + actual_len; + clear_ptr = addr_ptr + start_loc + len; if (clear_ptr == buff_end_ptr) clear_ptr = buff_start_ptr; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 492cd7aef44f..25820e6228a5 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -379,6 +379,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status) { + struct wl12xx_vif *wlvifsta; + struct wl12xx_vif *wlvifap; struct wl12xx_vif *wlvif; u32 old_tx_blk_count = wl->tx_blocks_available; int avail, freed_blocks; @@ -392,7 +394,7 @@ static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status) if (ret < 0) return ret; - wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, wl->fw_status); + wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, status); wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", @@ -410,23 +412,100 @@ static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status) wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i]; } + /* Find an authorized STA vif */ + wlvifsta = NULL; + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (wlvif->sta.hlid != WL12XX_INVALID_LINK_ID && + test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags)) { + wlvifsta = wlvif; + break; + } + } + + /* Find a started AP vif */ + wlvifap = NULL; + wl12xx_for_each_wlvif(wl, wlvif) { + if (wlvif->bss_type == BSS_TYPE_AP_BSS && + wlvif->inconn_count == 0 && + test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) { + wlvifap = wlvif; + break; + } + } for_each_set_bit(i, wl->links_map, wl->num_links) { - u8 diff; + u16 diff16, sec_pn16; + u8 diff, tx_lnk_free_pkts; + lnk = &wl->links[i]; /* prevent wrap-around in freed-packets counter */ - diff = (status->counters.tx_lnk_free_pkts[i] - - lnk->prev_freed_pkts) & 0xff; + tx_lnk_free_pkts = status->counters.tx_lnk_free_pkts[i]; + diff = (tx_lnk_free_pkts - lnk->prev_freed_pkts) & 0xff; - if (diff == 0) + if (diff) { + lnk->allocated_pkts -= diff; + lnk->prev_freed_pkts = tx_lnk_free_pkts; + } + + /* Get the current sec_pn16 value if present */ + if (status->counters.tx_lnk_sec_pn16) + sec_pn16 = __le16_to_cpu(status->counters.tx_lnk_sec_pn16[i]); + else + sec_pn16 = 0; + /* prevent wrap-around in pn16 counter */ + diff16 = (sec_pn16 - lnk->prev_sec_pn16) & 0xffff; + + /* FIXME: since free_pkts is a 8-bit counter of packets that + * rolls over, it can become zero. If it is zero, then we + * omit processing below. Is that really correct? + */ + if (tx_lnk_free_pkts <= 0) continue; - lnk->allocated_pkts -= diff; - lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[i]; + /* For a station that has an authorized link: */ + if (wlvifsta && wlvifsta->sta.hlid == i) { + if (wlvifsta->encryption_type == KEY_TKIP || + wlvifsta->encryption_type == KEY_AES) { + if (diff16) { + lnk->prev_sec_pn16 = sec_pn16; + /* accumulate the prev_freed_pkts + * counter according to the PN from + * firmware + */ + lnk->total_freed_pkts += diff16; + } + } else { + if (diff) + /* accumulate the prev_freed_pkts + * counter according to the free packets + * count from firmware + */ + lnk->total_freed_pkts += diff; + } + } - /* accumulate the prev_freed_pkts counter */ - lnk->total_freed_pkts += diff; + /* For an AP that has been started */ + if (wlvifap && test_bit(i, wlvifap->ap.sta_hlid_map)) { + if (wlvifap->encryption_type == KEY_TKIP || + wlvifap->encryption_type == KEY_AES) { + if (diff16) { + lnk->prev_sec_pn16 = sec_pn16; + /* accumulate the prev_freed_pkts + * counter according to the PN from + * firmware + */ + lnk->total_freed_pkts += diff16; + } + } else { + if (diff) + /* accumulate the prev_freed_pkts + * counter according to the free packets + * count from firmware + */ + lnk->total_freed_pkts += diff; + } + } } /* prevent wrap-around in total blocks counter */ @@ -453,6 +532,7 @@ static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status) cancel_delayed_work(&wl->tx_watchdog_work); } + wl->tx_blocks_total = status->tx_total; avail = status->tx_total - wl->tx_allocated_blocks; /* @@ -3518,6 +3598,9 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, case WL1271_CIPHER_SUITE_GEM: key_type = KEY_GEM; break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key_type = KEY_IGTK; + break; default: wl1271_error("Unknown key algo 0x%x", key_conf->cipher); @@ -3537,6 +3620,10 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, return ret; } + /* Store AP encryption key type */ + if (wlvif->bss_type == BSS_TYPE_AP_BSS) + wlvif->encryption_type = key_type; + /* * reconfiguring arp response if the unicast (or common) * encryption key type was changed @@ -5057,6 +5144,25 @@ static int wl12xx_sta_remove(struct wl1271 *wl, if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map))) return -EINVAL; + if (wl->links[id].ba_bitmap) { + u8 ba = wl->links[id].ba_bitmap; + u8 tid; + + pr_info("WARNING: removing peer with active ba 0x%08x, count %u\n", + ba, wl->ba_rx_session_count); + + for (tid = 0; tid < 8; tid++) + if (ba & BIT(tid)) + wl12xx_acx_set_ba_receiver_session(wl, tid, 0, + false, id, 0); + if (wl->ba_rx_session_count >= hweight8(ba)) + wl->ba_rx_session_count -= hweight8(ba); + else + wl->ba_rx_session_count = 0; + + wl->links[id].ba_bitmap = 0; + } + ret = wl12xx_cmd_remove_peer(wl, wlvif, wl_sta->hlid); if (ret < 0) return ret; @@ -5573,6 +5679,15 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, wl1271_tx_flush(wl); } +static void wlcore_op_flush_sta(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wl1271 *wl = hw->priv; + + wl1271_tx_flush_sta(wl, sta); +} + static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *chan, @@ -5961,6 +6076,7 @@ static const struct ieee80211_ops wl1271_ops = { .channel_switch = wl12xx_op_channel_switch, .channel_switch_beacon = wlcore_op_channel_switch_beacon, .flush = wlcore_op_flush, + .flush_sta = wlcore_op_flush_sta, .remain_on_channel = wlcore_op_remain_on_channel, .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel, .add_chanctx = wlcore_op_add_chanctx, @@ -6145,6 +6261,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, WL1271_CIPHER_SUITE_GEM, + WLAN_CIPHER_SUITE_AES_CMAC, }; /* The tx descriptor buffer */ diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 464587d16ab2..3a5b3fad028f 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -1181,6 +1181,83 @@ void wl1271_tx_flush(struct wl1271 *wl) } EXPORT_SYMBOL_GPL(wl1271_tx_flush); +void wl1271_tx_flush_sta(struct wl1271 *wl, struct ieee80211_sta *sta) +{ + u8 hlid = wl1271_station(sta)->hlid; + bool frames_queued = false; + unsigned long timeout; + int i; + + mutex_lock(&wl->mutex); + /* Flush queued packets for the link associated with this station */ + wl1271_tx_reset_link_queues(wl, hlid); + + /* Wait for any frames to be transmitted */ + timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT); + while (!time_after(jiffies, timeout)) { + frames_queued = false; + + /* Are there any frames for this hlid? */ + for (i = 0; i < WLCORE_MAX_TX_DESCRIPTORS; i++) { + struct wl1271_tx_hw_descr *desc; + struct sk_buff *skb; + + skb = wl->tx_frames[i]; + if (!skb) + continue; + + desc = (struct wl1271_tx_hw_descr *)skb->data; + if (desc->hlid == hlid) { + frames_queued = true; + break; + } + } + + if (!frames_queued) + break; + + mutex_unlock(&wl->mutex); + msleep(20); + mutex_lock(&wl->mutex); + } + + if (frames_queued) { + dev_warn(wl->dev, + "Unable to flush all frames for station %pM hlid %u\n", + sta->addr, hlid); + dev_warn(wl->dev, "FW time: %u", wl->fw_status->fw_localtime); + + for (i = 0; i < WLCORE_MAX_TX_DESCRIPTORS; i++) { + struct wl1271_tx_hw_descr *desc; + struct ieee80211_hdr *hdr; + struct sk_buff *skb; + u32 start; + u16 lt; + + skb = wl->tx_frames[i]; + if (!skb) + continue; + + desc = (struct wl1271_tx_hw_descr *)skb->data; + if (desc->hlid != hlid) + continue; + + /* The IEEE 802.11 header follows the TX descriptor */ + hdr = (void *)(desc + 1); + + start = le32_to_cpu(desc->start_time); + lt = le16_to_cpu(desc->life_time); + + dev_warn(wl->dev, + " Frame %d: expires %u MAC %pM FC 0x%04x\n", + i, start + 1024 * lt, + hdr->addr1, hdr->frame_control); + } + } + + mutex_unlock(&wl->mutex); +} + u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) { if (WARN_ON(!rate_set)) diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 9069bcf87158..e113ecca1b1d 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -232,6 +232,7 @@ int wlcore_tx_complete(struct wl1271 *wl); void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_tx_reset(struct wl1271 *wl); void wl1271_tx_flush(struct wl1271 *wl); +void wl1271_tx_flush_sta(struct wl1271 *wl, struct ieee80211_sta *sta); u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum nl80211_band band); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, enum nl80211_band rate_band); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 1f8511bf9bb3..97ade837c343 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -242,6 +242,7 @@ struct wl1271 { struct wl1271_acx_mem_map *target_mem_map; /* Accounting for allocated / available TX blocks on HW */ + u32 tx_blocks_total; u32 tx_blocks_freed; u32 tx_blocks_available; u32 tx_allocated_blocks; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 817a8a61cac6..5bdcb341629c 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -151,6 +151,9 @@ struct wl_fw_status { */ u8 *tx_lnk_free_pkts; + /* PN16 of last TKIP/AES seq-num per HLID */ + __le16 *tx_lnk_sec_pn16; + /* Cumulative counter of released Voice memory blocks */ u8 tx_voice_released_blks; @@ -259,6 +262,7 @@ struct wl1271_link { /* accounting for allocated / freed packets in FW */ u8 allocated_pkts; u8 prev_freed_pkts; + u16 prev_sec_pn16; u8 addr[ETH_ALEN];