]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
wifi: wlcore: add pn16 support
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Tue, 28 May 2024 09:18:07 +0000 (10:18 +0100)
committerKalle Valo <kvalo@kernel.org>
Tue, 18 Jun 2024 10:22:11 +0000 (13:22 +0300)
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) <rmk+kernel@armlinux.org.uk>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://msgid.link/E1sBsy7-00E8vu-Nc@rmk-PC.armlinux.org.uk
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/wlcore_i.h

index a939fd89a7f5e4f5dbaf07dcb6fc3bcc48c7c55a..0d1fcdca386997171f0e9ce2e71ec3be0a640330 100644 (file)
@@ -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);
 
index 8f82666f379cdfba0fdb942864e1f625c9b548f1..35d1114a28aa52715026e84d0b70b874d2fde6ec 100644 (file)
@@ -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;
@@ -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) {
+               u16 diff16, sec_pn16;
                u8 diff, tx_lnk_free_pkts;
+
                lnk = &wl->links[i];
 
                /* prevent wrap-around in freed-packets counter */
                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 = tx_lnk_free_pkts;
+               /* 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 */
index eefae3f867b9f6fab663e85cddad30362a772a29..5fbed64302f120e5fc05d32c6959c0f0a4842c71 100644 (file)
@@ -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];