From: Felix Fietkau Date: Fri, 18 Jun 2021 07:20:54 +0000 (+0200) Subject: mac80211: remove patches stripping down crypto support X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=53b6783907f3bd6f0f88f9d6feed20b21e2cd181;p=openwrt%2Fstaging%2Flinusw.git mac80211: remove patches stripping down crypto support Use of WPA3 and things like FILS is getting much more common, and platforms that can't affort the extra kilobytes for this code are fading away. Let's not hold back modern authentication methods any longer Signed-off-by: Felix Fietkau --- diff --git a/package/kernel/mac80211/Makefile b/package/kernel/mac80211/Makefile index d0620c556a..dd39c2d069 100644 --- a/package/kernel/mac80211/Makefile +++ b/package/kernel/mac80211/Makefile @@ -127,7 +127,7 @@ define KernelPackage/mac80211 $(call KernelPackage/mac80211/Default) TITLE:=Linux 802.11 Wireless Networking Stack # +kmod-crypto-cmac is a runtime only dependency of net/mac80211/aes_cmac.c - DEPENDS+= +kmod-cfg80211 +hostapd-common + DEPENDS+= +kmod-cfg80211 +kmod-crypto-cmac +kmod-crypto-ccm +kmod-crypto-gcm +hostapd-common KCONFIG:=\ CONFIG_AVERAGE=y FILES:= $(PKG_BUILD_DIR)/net/mac80211/mac80211.ko diff --git a/package/kernel/mac80211/patches/ath/402-ath_regd_optional.patch b/package/kernel/mac80211/patches/ath/402-ath_regd_optional.patch index bf87d3551a..3c9180b113 100644 --- a/package/kernel/mac80211/patches/ath/402-ath_regd_optional.patch +++ b/package/kernel/mac80211/patches/ath/402-ath_regd_optional.patch @@ -82,7 +82,7 @@ help --- a/local-symbols +++ b/local-symbols -@@ -85,6 +85,7 @@ ADM8211= +@@ -86,6 +86,7 @@ ADM8211= ATH_COMMON= WLAN_VENDOR_ATH= ATH_DEBUG= diff --git a/package/kernel/mac80211/patches/ath10k/080-ath10k_thermal_config.patch b/package/kernel/mac80211/patches/ath10k/080-ath10k_thermal_config.patch index d183419a47..9ce44fd288 100644 --- a/package/kernel/mac80211/patches/ath10k/080-ath10k_thermal_config.patch +++ b/package/kernel/mac80211/patches/ath10k/080-ath10k_thermal_config.patch @@ -37,7 +37,7 @@ void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature); --- a/local-symbols +++ b/local-symbols -@@ -144,6 +144,7 @@ ATH10K_SNOC= +@@ -145,6 +145,7 @@ ATH10K_SNOC= ATH10K_DEBUG= ATH10K_DEBUGFS= ATH10K_SPECTRAL= diff --git a/package/kernel/mac80211/patches/ath10k/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch b/package/kernel/mac80211/patches/ath10k/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch index ce8effe3c3..fa007e73a1 100644 --- a/package/kernel/mac80211/patches/ath10k/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch +++ b/package/kernel/mac80211/patches/ath10k/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch @@ -114,7 +114,7 @@ v13: ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o --- a/local-symbols +++ b/local-symbols -@@ -145,6 +145,7 @@ ATH10K_DEBUG= +@@ -146,6 +146,7 @@ ATH10K_DEBUG= ATH10K_DEBUGFS= ATH10K_SPECTRAL= ATH10K_THERMAL= diff --git a/package/kernel/mac80211/patches/ath9k/551-ath9k_ubnt_uap_plus_hsr.patch b/package/kernel/mac80211/patches/ath9k/551-ath9k_ubnt_uap_plus_hsr.patch index acb9ad443c..cd2bdbf1a0 100644 --- a/package/kernel/mac80211/patches/ath9k/551-ath9k_ubnt_uap_plus_hsr.patch +++ b/package/kernel/mac80211/patches/ath9k/551-ath9k_ubnt_uap_plus_hsr.patch @@ -371,7 +371,7 @@ --- a/local-symbols +++ b/local-symbols -@@ -112,6 +112,7 @@ ATH9K_WOW= +@@ -113,6 +113,7 @@ ATH9K_WOW= ATH9K_RFKILL= ATH9K_CHANNEL_CONTEXT= ATH9K_PCOEM= diff --git a/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch b/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch index e74d9a9aa0..1c52132da6 100644 --- a/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch +++ b/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch @@ -1,6 +1,6 @@ --- a/local-symbols +++ b/local-symbols -@@ -332,6 +332,7 @@ RT2X00_LIB_FIRMWARE= +@@ -333,6 +333,7 @@ RT2X00_LIB_FIRMWARE= RT2X00_LIB_CRYPTO= RT2X00_LIB_LEDS= RT2X00_LIB_DEBUGFS= diff --git a/package/kernel/mac80211/patches/subsys/100-remove-cryptoapi-dependencies.patch b/package/kernel/mac80211/patches/subsys/100-remove-cryptoapi-dependencies.patch deleted file mode 100644 index ca02dfb06f..0000000000 --- a/package/kernel/mac80211/patches/subsys/100-remove-cryptoapi-dependencies.patch +++ /dev/null @@ -1,699 +0,0 @@ ---- a/net/mac80211/Makefile -+++ b/net/mac80211/Makefile -@@ -7,7 +7,6 @@ mac80211-y := \ - driver-ops.o \ - sta_info.o \ - wep.o \ -- aead_api.o \ - wpa.o \ - scan.o offchannel.o \ - ht.o agg-tx.o agg-rx.o \ -@@ -19,8 +18,8 @@ mac80211-y := \ - rate.o \ - michael.o \ - tkip.o \ -+ aes_ccm.o \ - aes_cmac.o \ -- aes_gmac.o \ - fils_aead.o \ - cfg.o \ - ethtool.o \ ---- a/net/mac80211/aead_api.c -+++ /dev/null -@@ -1,113 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright 2003-2004, Instant802 Networks, Inc. -- * Copyright 2005-2006, Devicescape Software, Inc. -- * Copyright 2014-2015, Qualcomm Atheros, Inc. -- * -- * Rewrite: Copyright (C) 2013 Linaro Ltd -- */ -- --#include --#include --#include --#include --#include -- --#include "aead_api.h" -- --int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, -- u8 *data, size_t data_len, u8 *mic) --{ -- size_t mic_len = crypto_aead_authsize(tfm); -- struct scatterlist sg[3]; -- struct aead_request *aead_req; -- int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); -- u8 *__aad; -- int ret; -- -- aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC); -- if (!aead_req) -- return -ENOMEM; -- -- __aad = (u8 *)aead_req + reqsize; -- memcpy(__aad, aad, aad_len); -- -- sg_init_table(sg, 3); -- sg_set_buf(&sg[0], __aad, aad_len); -- sg_set_buf(&sg[1], data, data_len); -- sg_set_buf(&sg[2], mic, mic_len); -- -- aead_request_set_tfm(aead_req, tfm); -- aead_request_set_crypt(aead_req, sg, sg, data_len, b_0); -- aead_request_set_ad(aead_req, sg[0].length); -- -- ret = crypto_aead_encrypt(aead_req); -- kfree_sensitive(aead_req); -- -- return ret; --} -- --int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, -- u8 *data, size_t data_len, u8 *mic) --{ -- size_t mic_len = crypto_aead_authsize(tfm); -- struct scatterlist sg[3]; -- struct aead_request *aead_req; -- int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); -- u8 *__aad; -- int err; -- -- if (data_len == 0) -- return -EINVAL; -- -- aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC); -- if (!aead_req) -- return -ENOMEM; -- -- __aad = (u8 *)aead_req + reqsize; -- memcpy(__aad, aad, aad_len); -- -- sg_init_table(sg, 3); -- sg_set_buf(&sg[0], __aad, aad_len); -- sg_set_buf(&sg[1], data, data_len); -- sg_set_buf(&sg[2], mic, mic_len); -- -- aead_request_set_tfm(aead_req, tfm); -- aead_request_set_crypt(aead_req, sg, sg, data_len + mic_len, b_0); -- aead_request_set_ad(aead_req, sg[0].length); -- -- err = crypto_aead_decrypt(aead_req); -- kfree_sensitive(aead_req); -- -- return err; --} -- --struct crypto_aead * --aead_key_setup_encrypt(const char *alg, const u8 key[], -- size_t key_len, size_t mic_len) --{ -- struct crypto_aead *tfm; -- int err; -- -- tfm = crypto_alloc_aead(alg, 0, CRYPTO_ALG_ASYNC); -- if (IS_ERR(tfm)) -- return tfm; -- -- err = crypto_aead_setkey(tfm, key, key_len); -- if (err) -- goto free_aead; -- err = crypto_aead_setauthsize(tfm, mic_len); -- if (err) -- goto free_aead; -- -- return tfm; -- --free_aead: -- crypto_free_aead(tfm); -- return ERR_PTR(err); --} -- --void aead_key_free(struct crypto_aead *tfm) --{ -- crypto_free_aead(tfm); --} ---- a/net/mac80211/aead_api.h -+++ /dev/null -@@ -1,23 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ -- --#ifndef _AEAD_API_H --#define _AEAD_API_H -- --#include --#include -- --struct crypto_aead * --aead_key_setup_encrypt(const char *alg, const u8 key[], -- size_t key_len, size_t mic_len); -- --int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, -- size_t aad_len, u8 *data, -- size_t data_len, u8 *mic); -- --int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, -- size_t aad_len, u8 *data, -- size_t data_len, u8 *mic); -- --void aead_key_free(struct crypto_aead *tfm); -- --#endif /* _AEAD_API_H */ ---- a/net/mac80211/aes_ccm.h -+++ b/net/mac80211/aes_ccm.h -@@ -7,39 +7,17 @@ - #ifndef AES_CCM_H - #define AES_CCM_H - --#include "aead_api.h" -+#include - --#define CCM_AAD_LEN 32 -- --static inline struct crypto_aead * --ieee80211_aes_key_setup_encrypt(const u8 key[], size_t key_len, size_t mic_len) --{ -- return aead_key_setup_encrypt("ccm(aes)", key, key_len, mic_len); --} -- --static inline int --ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, -- u8 *b_0, u8 *aad, u8 *data, -- size_t data_len, u8 *mic) --{ -- return aead_encrypt(tfm, b_0, aad + 2, -- be16_to_cpup((__be16 *)aad), -- data, data_len, mic); --} -- --static inline int --ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, -- u8 *b_0, u8 *aad, u8 *data, -- size_t data_len, u8 *mic) --{ -- return aead_decrypt(tfm, b_0, aad + 2, -- be16_to_cpup((__be16 *)aad), -- data, data_len, mic); --} -- --static inline void ieee80211_aes_key_free(struct crypto_aead *tfm) --{ -- return aead_key_free(tfm); --} -+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[], -+ size_t key_len, -+ size_t mic_len); -+void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic, -+ size_t mic_len); -+int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic, -+ size_t mic_len); -+void ieee80211_aes_key_free(struct crypto_cipher *tfm); - - #endif /* AES_CCM_H */ ---- /dev/null -+++ b/net/mac80211/aes_gcm.c -@@ -0,0 +1,109 @@ -+/* -+ * Copyright 2014-2015, Qualcomm Atheros, Inc. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include "key.h" -+#include "aes_gcm.h" -+ -+int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic) -+{ -+ struct scatterlist sg[3]; -+ struct aead_request *aead_req; -+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); -+ u8 *__aad; -+ -+ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC); -+ if (!aead_req) -+ return -ENOMEM; -+ -+ __aad = (u8 *)aead_req + reqsize; -+ memcpy(__aad, aad, GCM_AAD_LEN); -+ -+ sg_init_table(sg, 3); -+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad)); -+ sg_set_buf(&sg[1], data, data_len); -+ sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN); -+ -+ aead_request_set_tfm(aead_req, tfm); -+ aead_request_set_crypt(aead_req, sg, sg, data_len, j_0); -+ aead_request_set_ad(aead_req, sg[0].length); -+ -+ crypto_aead_encrypt(aead_req); -+ kzfree(aead_req); -+ return 0; -+} -+ -+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic) -+{ -+ struct scatterlist sg[3]; -+ struct aead_request *aead_req; -+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); -+ u8 *__aad; -+ int err; -+ -+ if (data_len == 0) -+ return -EINVAL; -+ -+ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC); -+ if (!aead_req) -+ return -ENOMEM; -+ -+ __aad = (u8 *)aead_req + reqsize; -+ memcpy(__aad, aad, GCM_AAD_LEN); -+ -+ sg_init_table(sg, 3); -+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad)); -+ sg_set_buf(&sg[1], data, data_len); -+ sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN); -+ -+ aead_request_set_tfm(aead_req, tfm); -+ aead_request_set_crypt(aead_req, sg, sg, -+ data_len + IEEE80211_GCMP_MIC_LEN, j_0); -+ aead_request_set_ad(aead_req, sg[0].length); -+ -+ err = crypto_aead_decrypt(aead_req); -+ kzfree(aead_req); -+ -+ return err; -+} -+ -+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], -+ size_t key_len) -+{ -+ struct crypto_aead *tfm; -+ int err; -+ -+ tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC); -+ if (IS_ERR(tfm)) -+ return tfm; -+ -+ err = crypto_aead_setkey(tfm, key, key_len); -+ if (err) -+ goto free_aead; -+ err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN); -+ if (err) -+ goto free_aead; -+ -+ return tfm; -+ -+free_aead: -+ crypto_free_aead(tfm); -+ return ERR_PTR(err); -+} -+ -+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm) -+{ -+ crypto_free_aead(tfm); -+} ---- a/net/mac80211/aes_gcm.h -+++ b/net/mac80211/aes_gcm.h -@@ -6,38 +6,30 @@ - #ifndef AES_GCM_H - #define AES_GCM_H - --#include "aead_api.h" -+#include - --#define GCM_AAD_LEN 32 -- --static inline int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, -- u8 *j_0, u8 *aad, u8 *data, -- size_t data_len, u8 *mic) -+static inline void -+ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic) - { -- return aead_encrypt(tfm, j_0, aad + 2, -- be16_to_cpup((__be16 *)aad), -- data, data_len, mic); - } - --static inline int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, -- u8 *j_0, u8 *aad, u8 *data, -- size_t data_len, u8 *mic) -+static inline int -+ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic) - { -- return aead_decrypt(tfm, j_0, aad + 2, -- be16_to_cpup((__be16 *)aad), -- data, data_len, mic); -+ return -EOPNOTSUPP; - } - - static inline struct crypto_aead * - ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], size_t key_len) - { -- return aead_key_setup_encrypt("gcm(aes)", key, -- key_len, IEEE80211_GCMP_MIC_LEN); -+ return NULL; - } - --static inline void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm) -+static inline void -+ieee80211_aes_gcm_key_free(struct crypto_aead *tfm) - { -- return aead_key_free(tfm); - } - - #endif /* AES_GCM_H */ ---- a/net/mac80211/wpa.c -+++ b/net/mac80211/wpa.c -@@ -312,7 +312,8 @@ ieee80211_crypto_tkip_decrypt(struct iee - } - - --static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad) -+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, -+ u16 data_len) - { - __le16 mask_fc; - int a4_included, mgmt; -@@ -342,14 +343,8 @@ static void ccmp_special_blocks(struct s - else - qos_tid = 0; - -- /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC -- * mode authentication are not allowed to collide, yet both are derived -- * from this vector b_0. We only set L := 1 here to indicate that the -- * data size can be represented in (L+1) bytes. The CCM layer will take -- * care of storing the data length in the top (L+1) bytes and setting -- * and clearing the other bits as is required to derive the two IVs. -- */ -- b_0[0] = 0x1; -+ /* First block, b_0 */ -+ b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ - - /* Nonce: Nonce Flags | A2 | PN - * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) -@@ -357,6 +352,8 @@ static void ccmp_special_blocks(struct s - b_0[1] = qos_tid | (mgmt << 4); - memcpy(&b_0[2], hdr->addr2, ETH_ALEN); - memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); -+ /* l(m) */ -+ put_unaligned_be16(data_len, &b_0[14]); - - /* AAD (extra authenticate-only data) / masked 802.11 header - * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ -@@ -413,7 +410,7 @@ static int ccmp_encrypt_skb(struct ieee8 - u8 *pos; - u8 pn[6]; - u64 pn64; -- u8 aad[CCM_AAD_LEN]; -+ u8 aad[2 * AES_BLOCK_SIZE]; - u8 b_0[AES_BLOCK_SIZE]; - - if (info->control.hw_key && -@@ -468,9 +465,11 @@ static int ccmp_encrypt_skb(struct ieee8 - return 0; - - pos += IEEE80211_CCMP_HDR_LEN; -- ccmp_special_blocks(skb, pn, b_0, aad); -- return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, -- skb_put(skb, mic_len)); -+ ccmp_special_blocks(skb, pn, b_0, aad, len); -+ ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, -+ skb_put(skb, mic_len), mic_len); -+ -+ return 0; - } - - -@@ -543,13 +542,13 @@ ieee80211_crypto_ccmp_decrypt(struct iee - u8 aad[2 * AES_BLOCK_SIZE]; - u8 b_0[AES_BLOCK_SIZE]; - /* hardware didn't decrypt/verify MIC */ -- ccmp_special_blocks(skb, pn, b_0, aad); -+ ccmp_special_blocks(skb, pn, b_0, aad, data_len); - - if (ieee80211_aes_ccm_decrypt( - key->u.ccmp.tfm, b_0, aad, - skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, - data_len, -- skb->data + skb->len - mic_len)) -+ skb->data + skb->len - mic_len, mic_len)) - return RX_DROP_UNUSABLE; - } - -@@ -646,7 +645,7 @@ static int gcmp_encrypt_skb(struct ieee8 - u8 *pos; - u8 pn[6]; - u64 pn64; -- u8 aad[GCM_AAD_LEN]; -+ u8 aad[2 * AES_BLOCK_SIZE]; - u8 j_0[AES_BLOCK_SIZE]; - - if (info->control.hw_key && -@@ -703,8 +702,10 @@ static int gcmp_encrypt_skb(struct ieee8 - - pos += IEEE80211_GCMP_HDR_LEN; - gcmp_special_blocks(skb, pn, j_0, aad); -- return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len, -- skb_put(skb, IEEE80211_GCMP_MIC_LEN)); -+ ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len, -+ skb_put(skb, IEEE80211_GCMP_MIC_LEN)); -+ -+ return 0; - } - - ieee80211_tx_result -@@ -1133,9 +1134,9 @@ ieee80211_crypto_aes_gmac_encrypt(struct - struct ieee80211_key *key = tx->key; - struct ieee80211_mmie_16 *mmie; - struct ieee80211_hdr *hdr; -- u8 aad[GMAC_AAD_LEN]; -+ u8 aad[20]; - u64 pn64; -- u8 nonce[GMAC_NONCE_LEN]; -+ u8 nonce[12]; - - if (WARN_ON(skb_queue_len(&tx->skbs) != 1)) - return TX_DROP; -@@ -1181,7 +1182,7 @@ ieee80211_crypto_aes_gmac_decrypt(struct - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - struct ieee80211_key *key = rx->key; - struct ieee80211_mmie_16 *mmie; -- u8 aad[GMAC_AAD_LEN], *mic, ipn[6], nonce[GMAC_NONCE_LEN]; -+ u8 aad[20], *mic, ipn[6], nonce[12]; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - - if (!ieee80211_is_mgmt(hdr->frame_control)) ---- /dev/null -+++ b/net/mac80211/aes_ccm.c -@@ -0,0 +1,144 @@ -+/* -+ * Copyright 2003-2004, Instant802 Networks, Inc. -+ * Copyright 2005-2006, Devicescape Software, Inc. -+ * -+ * Rewrite: Copyright (C) 2013 Linaro Ltd -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include "key.h" -+#include "aes_ccm.h" -+ -+static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, u8 *s_0, -+ u8 *a, u8 *b) -+{ -+ int i; -+ -+ crypto_cipher_encrypt_one(tfm, b, b_0); -+ -+ /* Extra Authenticate-only data (always two AES blocks) */ -+ for (i = 0; i < AES_BLOCK_SIZE; i++) -+ aad[i] ^= b[i]; -+ crypto_cipher_encrypt_one(tfm, b, aad); -+ -+ aad += AES_BLOCK_SIZE; -+ -+ for (i = 0; i < AES_BLOCK_SIZE; i++) -+ aad[i] ^= b[i]; -+ crypto_cipher_encrypt_one(tfm, a, aad); -+ -+ /* Mask out bits from auth-only-b_0 */ -+ b_0[0] &= 0x07; -+ -+ /* S_0 is used to encrypt T (= MIC) */ -+ b_0[14] = 0; -+ b_0[15] = 0; -+ crypto_cipher_encrypt_one(tfm, s_0, b_0); -+} -+ -+ -+void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic, -+ size_t mic_len) -+{ -+ int i, j, last_len, num_blocks; -+ u8 b[AES_BLOCK_SIZE]; -+ u8 s_0[AES_BLOCK_SIZE]; -+ u8 e[AES_BLOCK_SIZE]; -+ u8 *pos, *cpos; -+ -+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); -+ last_len = data_len % AES_BLOCK_SIZE; -+ aes_ccm_prepare(tfm, b_0, aad, s_0, b, b); -+ -+ /* Process payload blocks */ -+ pos = data; -+ cpos = data; -+ for (j = 1; j <= num_blocks; j++) { -+ int blen = (j == num_blocks && last_len) ? -+ last_len : AES_BLOCK_SIZE; -+ -+ /* Authentication followed by encryption */ -+ for (i = 0; i < blen; i++) -+ b[i] ^= pos[i]; -+ crypto_cipher_encrypt_one(tfm, b, b); -+ -+ b_0[14] = (j >> 8) & 0xff; -+ b_0[15] = j & 0xff; -+ crypto_cipher_encrypt_one(tfm, e, b_0); -+ for (i = 0; i < blen; i++) -+ *cpos++ = *pos++ ^ e[i]; -+ } -+ -+ for (i = 0; i < mic_len; i++) -+ mic[i] = b[i] ^ s_0[i]; -+} -+ -+int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, -+ u8 *data, size_t data_len, u8 *mic, -+ size_t mic_len) -+{ -+ int i, j, last_len, num_blocks; -+ u8 *pos, *cpos; -+ u8 a[AES_BLOCK_SIZE]; -+ u8 b[AES_BLOCK_SIZE]; -+ u8 s_0[AES_BLOCK_SIZE]; -+ -+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); -+ last_len = data_len % AES_BLOCK_SIZE; -+ aes_ccm_prepare(tfm, b_0, aad, s_0, a, b); -+ -+ /* Process payload blocks */ -+ cpos = data; -+ pos = data; -+ for (j = 1; j <= num_blocks; j++) { -+ int blen = (j == num_blocks && last_len) ? -+ last_len : AES_BLOCK_SIZE; -+ -+ /* Decryption followed by authentication */ -+ b_0[14] = (j >> 8) & 0xff; -+ b_0[15] = j & 0xff; -+ crypto_cipher_encrypt_one(tfm, b, b_0); -+ for (i = 0; i < blen; i++) { -+ *pos = *cpos++ ^ b[i]; -+ a[i] ^= *pos++; -+ } -+ crypto_cipher_encrypt_one(tfm, a, a); -+ } -+ -+ for (i = 0; i < mic_len; i++) { -+ if ((mic[i] ^ s_0[i]) != a[i]) -+ return -1; -+ } -+ -+ return 0; -+} -+ -+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[], -+ size_t key_len, -+ size_t mic_len) -+{ -+ struct crypto_cipher *tfm; -+ -+ tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); -+ if (!IS_ERR(tfm)) -+ crypto_cipher_setkey(tfm, key, key_len); -+ -+ return tfm; -+} -+ -+ -+void ieee80211_aes_key_free(struct crypto_cipher *tfm) -+{ -+ crypto_free_cipher(tfm); -+} ---- a/net/mac80211/Kconfig -+++ b/net/mac80211/Kconfig -@@ -6,8 +6,6 @@ config MAC80211 - depends on CRYPTO - select BPAUTO_CRYPTO_LIB_ARC4 - depends on CRYPTO_AES -- depends on CRYPTO_CCM -- depends on CRYPTO_GCM - depends on CRYPTO_CMAC - depends on CRC32 - help ---- a/net/mac80211/aes_gmac.h -+++ b/net/mac80211/aes_gmac.h -@@ -12,10 +12,22 @@ - #define GMAC_MIC_LEN 16 - #define GMAC_NONCE_LEN 12 - --struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[], -- size_t key_len); --int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce, -- const u8 *data, size_t data_len, u8 *mic); --void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm); -+static inline struct crypto_aead * -+ieee80211_aes_gmac_key_setup(const u8 key[], size_t key_len) -+{ -+ return NULL; -+} -+ -+static inline int -+ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce, -+ const u8 *data, size_t data_len, u8 *mic) -+{ -+ return -EOPNOTSUPP; -+} -+ -+static inline void -+ieee80211_aes_gmac_key_free(struct crypto_aead *tfm) -+{ -+} - - #endif /* AES_GMAC_H */ ---- a/net/mac80211/key.h -+++ b/net/mac80211/key.h -@@ -89,7 +89,7 @@ struct ieee80211_key { - * Management frames. - */ - u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; -- struct crypto_aead *tfm; -+ struct crypto_cipher *tfm; - u32 replays; /* dot11RSNAStatsCCMPReplays */ - } ccmp; - struct { diff --git a/package/kernel/mac80211/patches/subsys/130-disable-fils.patch b/package/kernel/mac80211/patches/subsys/130-disable-fils.patch deleted file mode 100644 index 9c6e971f9d..0000000000 --- a/package/kernel/mac80211/patches/subsys/130-disable-fils.patch +++ /dev/null @@ -1,32 +0,0 @@ -Disable FILS support, since it pulls in crypto hash support - ---- a/net/mac80211/fils_aead.h -+++ b/net/mac80211/fils_aead.h -@@ -7,7 +7,7 @@ - #ifndef FILS_AEAD_H - #define FILS_AEAD_H - --#if LINUX_VERSION_IS_GEQ(4,3,0) -+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */ - int fils_encrypt_assoc_req(struct sk_buff *skb, - struct ieee80211_mgd_assoc_data *assoc_data); - int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, ---- a/net/mac80211/fils_aead.c -+++ b/net/mac80211/fils_aead.c -@@ -1,4 +1,4 @@ --#if LINUX_VERSION_IS_GEQ(4,3,0) -+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */ - // SPDX-License-Identifier: GPL-2.0-only - /* - * FILS AEAD for (Re)Association Request/Response frames ---- a/net/mac80211/main.c -+++ b/net/mac80211/main.c -@@ -591,7 +591,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_ - NL80211_FEATURE_MAC_ON_CREATE | - NL80211_FEATURE_USERSPACE_MPM | - NL80211_FEATURE_FULL_AP_CLIENT_STATE; --#if LINUX_VERSION_IS_GEQ(4,3,0) -+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */ - wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_STA); - #endif - wiphy_ext_feature_set(wiphy, diff --git a/package/kernel/mac80211/patches/subsys/131-Revert-mac80211-aes-cmac-switch-to-shash-CMAC-driver.patch b/package/kernel/mac80211/patches/subsys/131-Revert-mac80211-aes-cmac-switch-to-shash-CMAC-driver.patch deleted file mode 100644 index c3bf7ccc7a..0000000000 --- a/package/kernel/mac80211/patches/subsys/131-Revert-mac80211-aes-cmac-switch-to-shash-CMAC-driver.patch +++ /dev/null @@ -1,230 +0,0 @@ -From: Felix Fietkau -Date: Sat, 7 Oct 2017 09:37:28 +0200 -Subject: [PATCH] Revert "mac80211: aes-cmac: switch to shash CMAC - driver" - -This reverts commit 26717828b75dd5c46e97f7f4a9b937d038bb2852. -Reduces mac80211 dependencies for LEDE - -Signed-off-by: Felix Fietkau ---- - ---- a/net/mac80211/aes_cmac.c -+++ b/net/mac80211/aes_cmac.c -@@ -19,67 +19,151 @@ - #define CMAC_TLEN_256 16 /* CMAC TLen = 128 bits (16 octets) */ - #define AAD_LEN 20 - --static const u8 zero[CMAC_TLEN_256]; - --void ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad, -+void gf_mulx(u8 *pad) -+{ -+ int i, carry; -+ -+ carry = pad[0] & 0x80; -+ for (i = 0; i < AES_BLOCK_SIZE - 1; i++) -+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); -+ pad[AES_BLOCK_SIZE - 1] <<= 1; -+ if (carry) -+ pad[AES_BLOCK_SIZE - 1] ^= 0x87; -+} -+ -+void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, -+ const u8 *addr[], const size_t *len, u8 *mac, -+ size_t mac_len) -+{ -+ u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; -+ const u8 *pos, *end; -+ size_t i, e, left, total_len; -+ -+ memset(cbc, 0, AES_BLOCK_SIZE); -+ -+ total_len = 0; -+ for (e = 0; e < num_elem; e++) -+ total_len += len[e]; -+ left = total_len; -+ -+ e = 0; -+ pos = addr[0]; -+ end = pos + len[0]; -+ -+ while (left >= AES_BLOCK_SIZE) { -+ for (i = 0; i < AES_BLOCK_SIZE; i++) { -+ cbc[i] ^= *pos++; -+ if (pos >= end) { -+ e++; -+ pos = addr[e]; -+ end = pos + len[e]; -+ } -+ } -+ if (left > AES_BLOCK_SIZE) -+ crypto_cipher_encrypt_one(tfm, cbc, cbc); -+ left -= AES_BLOCK_SIZE; -+ } -+ -+ memset(pad, 0, AES_BLOCK_SIZE); -+ crypto_cipher_encrypt_one(tfm, pad, pad); -+ gf_mulx(pad); -+ -+ if (left || total_len == 0) { -+ for (i = 0; i < left; i++) { -+ cbc[i] ^= *pos++; -+ if (pos >= end) { -+ e++; -+ pos = addr[e]; -+ end = pos + len[e]; -+ } -+ } -+ cbc[left] ^= 0x80; -+ gf_mulx(pad); -+ } -+ -+ for (i = 0; i < AES_BLOCK_SIZE; i++) -+ pad[i] ^= cbc[i]; -+ crypto_cipher_encrypt_one(tfm, pad, pad); -+ memcpy(mac, pad, mac_len); -+} -+ -+ -+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, - const u8 *data, size_t data_len, u8 *mic) - { -- SHASH_DESC_ON_STACK(desc, tfm); -- u8 out[AES_BLOCK_SIZE]; -+ const u8 *addr[4]; -+ size_t len[4]; -+ u8 zero[CMAC_TLEN]; - const __le16 *fc; - -- desc->tfm = tfm; -- -- crypto_shash_init(desc); -- crypto_shash_update(desc, aad, AAD_LEN); -+ memset(zero, 0, CMAC_TLEN); -+ addr[0] = aad; -+ len[0] = AAD_LEN; - fc = (const __le16 *)aad; - if (ieee80211_is_beacon(*fc)) { - /* mask Timestamp field to zero */ -- crypto_shash_update(desc, zero, 8); -- crypto_shash_update(desc, data + 8, data_len - 8 - CMAC_TLEN); -+ addr[1] = zero; -+ len[1] = 8; -+ addr[2] = data + 8; -+ len[2] = data_len - 8 - CMAC_TLEN; -+ addr[3] = zero; -+ len[3] = CMAC_TLEN; -+ aes_cmac_vector(tfm, 4, addr, len, mic, CMAC_TLEN); - } else { -- crypto_shash_update(desc, data, data_len - CMAC_TLEN); -+ addr[1] = data; -+ len[1] = data_len - CMAC_TLEN; -+ addr[2] = zero; -+ len[2] = CMAC_TLEN; -+ aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN); - } -- crypto_shash_finup(desc, zero, CMAC_TLEN, out); -- -- memcpy(mic, out, CMAC_TLEN); - } - --void ieee80211_aes_cmac_256(struct crypto_shash *tfm, const u8 *aad, -+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad, - const u8 *data, size_t data_len, u8 *mic) - { -- SHASH_DESC_ON_STACK(desc, tfm); -+ const u8 *addr[4]; -+ size_t len[4]; -+ u8 zero[CMAC_TLEN_256]; - const __le16 *fc; - -- desc->tfm = tfm; -- -- crypto_shash_init(desc); -- crypto_shash_update(desc, aad, AAD_LEN); -+ memset(zero, 0, CMAC_TLEN_256); -+ addr[0] = aad; -+ len[0] = AAD_LEN; -+ addr[1] = data; - fc = (const __le16 *)aad; - if (ieee80211_is_beacon(*fc)) { - /* mask Timestamp field to zero */ -- crypto_shash_update(desc, zero, 8); -- crypto_shash_update(desc, data + 8, -- data_len - 8 - CMAC_TLEN_256); -+ addr[1] = zero; -+ len[1] = 8; -+ addr[2] = data + 8; -+ len[2] = data_len - 8 - CMAC_TLEN_256; -+ addr[3] = zero; -+ len[3] = CMAC_TLEN_256; -+ aes_cmac_vector(tfm, 4, addr, len, mic, CMAC_TLEN_256); - } else { -- crypto_shash_update(desc, data, data_len - CMAC_TLEN_256); -+ addr[1] = data; -+ len[1] = data_len - CMAC_TLEN_256; -+ addr[2] = zero; -+ len[2] = CMAC_TLEN_256; -+ aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN_256); - } -- crypto_shash_finup(desc, zero, CMAC_TLEN_256, mic); - } - --struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[], -- size_t key_len) -+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[], -+ size_t key_len) - { -- struct crypto_shash *tfm; -+ struct crypto_cipher *tfm; - -- tfm = crypto_alloc_shash("cmac(aes)", 0, 0); -+ tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); - if (!IS_ERR(tfm)) -- crypto_shash_setkey(tfm, key, key_len); -+ crypto_cipher_setkey(tfm, key, key_len); - - return tfm; - } - --void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm) -+ -+void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm) - { -- crypto_free_shash(tfm); -+ crypto_free_cipher(tfm); - } ---- a/net/mac80211/aes_cmac.h -+++ b/net/mac80211/aes_cmac.h -@@ -7,14 +7,13 @@ - #define AES_CMAC_H - - #include --#include - --struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[], -- size_t key_len); --void ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad, -+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[], -+ size_t key_len); -+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, - const u8 *data, size_t data_len, u8 *mic); --void ieee80211_aes_cmac_256(struct crypto_shash *tfm, const u8 *aad, -+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad, - const u8 *data, size_t data_len, u8 *mic); --void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm); -+void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm); - - #endif /* AES_CMAC_H */ ---- a/net/mac80211/key.h -+++ b/net/mac80211/key.h -@@ -94,7 +94,7 @@ struct ieee80211_key { - } ccmp; - struct { - u8 rx_pn[IEEE80211_CMAC_PN_LEN]; -- struct crypto_shash *tfm; -+ struct crypto_cipher *tfm; - u32 replays; /* dot11RSNAStatsCMACReplays */ - u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ - } aes_cmac; diff --git a/package/kernel/mac80211/patches/subsys/132-mac80211-remove-cmac-dependency.patch b/package/kernel/mac80211/patches/subsys/132-mac80211-remove-cmac-dependency.patch deleted file mode 100644 index df67d2f101..0000000000 --- a/package/kernel/mac80211/patches/subsys/132-mac80211-remove-cmac-dependency.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- a/net/mac80211/Kconfig -+++ b/net/mac80211/Kconfig -@@ -6,7 +6,6 @@ config MAC80211 - depends on CRYPTO - select BPAUTO_CRYPTO_LIB_ARC4 - depends on CRYPTO_AES -- depends on CRYPTO_CMAC - depends on CRC32 - help - This option enables the hardware independent IEEE 802.11 diff --git a/package/kernel/mac80211/patches/subsys/339-mac80211-remove-legacy-minstrel-rate-control.patch b/package/kernel/mac80211/patches/subsys/339-mac80211-remove-legacy-minstrel-rate-control.patch index 96ee595ac1..1cab2eb194 100644 --- a/package/kernel/mac80211/patches/subsys/339-mac80211-remove-legacy-minstrel-rate-control.patch +++ b/package/kernel/mac80211/patches/subsys/339-mac80211-remove-legacy-minstrel-rate-control.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile -@@ -55,11 +55,9 @@ mac80211-$(CONFIG_PM) += pm.o +@@ -56,11 +56,9 @@ mac80211-$(CONFIG_PM) += pm.o CFLAGS_trace.o := -I$(src) rc80211_minstrel-y := \ diff --git a/package/kernel/mac80211/patches/subsys/361-mac80211-minstrel_ht-add-debugfs-monitoring-controll.patch b/package/kernel/mac80211/patches/subsys/361-mac80211-minstrel_ht-add-debugfs-monitoring-controll.patch new file mode 100644 index 0000000000..ef57234bea --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/361-mac80211-minstrel_ht-add-debugfs-monitoring-controll.patch @@ -0,0 +1,888 @@ +From: Felix Fietkau +Date: Mon, 1 Feb 2021 10:47:58 +0100 +Subject: [PATCH] mac80211: minstrel_ht: add debugfs monitoring/controlling + API + +This allows user space to monitor tx status and take over rate control +functionality. + +Signed-off-by: Felix Fietkau +--- + create mode 100644 net/mac80211/rc80211_minstrel_ht_api.c + +--- a/local-symbols ++++ b/local-symbols +@@ -49,6 +49,7 @@ LIB80211_DEBUG= + MAC80211= + MAC80211_HAS_RC= + MAC80211_RC_MINSTREL= ++MAC80211_RC_MINSTREL_DEBUGFS_API= + MAC80211_RC_DEFAULT_MINSTREL= + MAC80211_RC_DEFAULT= + MAC80211_MESH= +--- a/net/mac80211/Kconfig ++++ b/net/mac80211/Kconfig +@@ -29,6 +29,15 @@ config MAC80211_RC_MINSTREL + help + This option enables the 'minstrel' TX rate control algorithm + ++config MAC80211_RC_MINSTREL_DEBUGFS_API ++ bool "Minstrel debugfs userspace control API" ++ depends on MAC80211_RC_MINSTREL ++ depends on MAC80211_DEBUGFS ++ select RELAY ++ help ++ This option creates debugfs files that allow user space to observe ++ and/or control minstrel rate selection behavior ++ + choice + prompt "Default rate control algorithm" + depends on MAC80211_HAS_RC +--- a/net/mac80211/Makefile ++++ b/net/mac80211/Makefile +@@ -61,6 +61,9 @@ rc80211_minstrel-y := \ + rc80211_minstrel-$(CPTCFG_MAC80211_DEBUGFS) += \ + rc80211_minstrel_ht_debugfs.o + ++rc80211_minstrel-$(CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API) += \ ++ rc80211_minstrel_ht_api.o ++ + mac80211-$(CPTCFG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) + + ccflags-y += -DDEBUG +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -276,7 +276,8 @@ static const u8 minstrel_sample_seq[] = + }; + + static void +-minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); ++minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ bool force); + + /* + * Some VHT MCSes are invalid (when Ndbps / Nes is not an integer) +@@ -346,7 +347,7 @@ minstrel_vht_get_group_idx(struct ieee80 + + static struct minstrel_rate_stats * + minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, +- struct ieee80211_tx_rate *rate) ++ struct ieee80211_tx_rate *rate, u16 *dest_idx) + { + int group, idx; + +@@ -381,6 +382,7 @@ minstrel_ht_get_stats(struct minstrel_pr + + idx = 0; + out: ++ *dest_idx = MI_RATE(group, idx); + return &mi->groups[group].rates[idx]; + } + +@@ -1024,6 +1026,8 @@ minstrel_ht_update_stats(struct minstrel + tp_rate = tmp_legacy_tp_rate; + + for (i = MCS_GROUP_RATES - 1; i >= 0; i--) { ++ bool changed; ++ + if (!(mi->supported[group] & BIT(i))) + continue; + +@@ -1031,7 +1035,11 @@ minstrel_ht_update_stats(struct minstrel + + mrs = &mg->rates[i]; + mrs->retry_updated = false; ++ changed = mrs->attempts > 0; + minstrel_ht_calc_rate_stats(mp, mrs); ++ if (changed) ++ minstrel_ht_report_rate_update(mp, mi, index, ++ mrs); + + if (mrs->att_hist) + last_prob = max(last_prob, mrs->prob_avg); +@@ -1080,7 +1088,8 @@ minstrel_ht_update_stats(struct minstrel + + mi->max_prob_rate = tmp_max_prob_rate; + +- minstrel_ht_refill_sample_rates(mi); ++ if (!minstrel_ht_manual_mode(mp)) ++ minstrel_ht_refill_sample_rates(mi); + + #ifdef CPTCFG_MAC80211_DEBUGFS + /* use fixed index if set */ +@@ -1177,6 +1186,7 @@ minstrel_ht_tx_status(void *priv, struct + struct minstrel_priv *mp = priv; + u32 update_interval = mp->update_interval; + bool last, update = false; ++ u16 rate_list[IEEE80211_TX_MAX_RATES] = {}; + int i; + + /* This packet was aggregated but doesn't carry status info */ +@@ -1208,13 +1218,15 @@ minstrel_ht_tx_status(void *priv, struct + last = (i == IEEE80211_TX_MAX_RATES - 1) || + !minstrel_ht_txstat_valid(mp, mi, &ar[i + 1]); + +- rate = minstrel_ht_get_stats(mp, mi, &ar[i]); ++ rate = minstrel_ht_get_stats(mp, mi, &ar[i], &rate_list[i]); + if (last) + rate->success += info->status.ampdu_ack_len; + + rate->attempts += ar[i].count * info->status.ampdu_len; + } + ++ minstrel_ht_report_tx_status(mp, mi, info, rate_list, i); ++ + if (mp->hw->max_rates > 1) { + /* + * check for sudden death of spatial multiplexing, +@@ -1236,7 +1248,7 @@ minstrel_ht_tx_status(void *priv, struct + } + + if (update) +- minstrel_ht_update_rates(mp, mi); ++ minstrel_ht_update_rates(mp, mi, false); + } + + static void +@@ -1299,7 +1311,7 @@ minstrel_calc_retransmit(struct minstrel + } + + +-static void ++void + minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, + struct ieee80211_sta_rates *ratetbl, int offset, int index) + { +@@ -1408,11 +1420,15 @@ minstrel_ht_get_max_amsdu_len(struct min + } + + static void +-minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) ++minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ bool force) + { + struct ieee80211_sta_rates *rates; + int i = 0; + ++ if (minstrel_ht_manual_mode(mp) && !force) ++ return; ++ + rates = kzalloc(sizeof(*rates), GFP_ATOMIC); + if (!rates) + return; +@@ -1439,7 +1455,7 @@ minstrel_ht_get_sample_rate(struct minst + { + u8 seq; + +- if (mp->hw->max_rates > 1) { ++ if (mp->hw->max_rates > 1 && !minstrel_ht_manual_mode(mp)) { + seq = mi->sample_seq; + mi->sample_seq = (seq + 1) % ARRAY_SIZE(minstrel_sample_seq); + seq = minstrel_sample_seq[seq]; +@@ -1689,7 +1705,9 @@ minstrel_ht_update_caps(void *priv, stru + + /* create an initial rate table with the lowest supported rates */ + minstrel_ht_update_stats(mp, mi); +- minstrel_ht_update_rates(mp, mi); ++ minstrel_ht_update_rates(mp, mi, true); ++ ++ minstrel_ht_sta_update(mp, mi); + } + + static void +@@ -1725,12 +1743,18 @@ minstrel_ht_alloc_sta(void *priv, struct + max_rates = sband->n_bitrates; + } + +- return kzalloc(sizeof(*mi), gfp); ++ mi = kzalloc(sizeof(*mi), gfp); ++#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API ++ INIT_LIST_HEAD(&mi->list); ++#endif ++ ++ return mi; + } + + static void + minstrel_ht_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) + { ++ minstrel_ht_sta_remove(priv, priv_sta); + kfree(priv_sta); + } + +@@ -1841,12 +1865,14 @@ static void minstrel_ht_add_debugfs(stru + mp->fixed_rate_idx = (u32) -1; + debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir, + &mp->fixed_rate_idx); ++ minstrel_ht_add_debugfs_api(hw, priv, debugfsdir); + } + #endif + + static void + minstrel_ht_free(void *priv) + { ++ minstrel_ht_remove_debugfs_api(priv); + kfree(priv); + } + +--- a/net/mac80211/rc80211_minstrel_ht.h ++++ b/net/mac80211/rc80211_minstrel_ht.h +@@ -72,6 +72,10 @@ + #define MINSTREL_SAMPLE_RATES 5 /* rates per sample type */ + #define MINSTREL_SAMPLE_INTERVAL (HZ / 50) + ++#define MINSTREL_MONITOR_STA BIT(0) ++#define MINSTREL_MONITOR_TXS BIT(1) ++#define MINSTREL_MONITOR_STATS BIT(2) ++ + struct minstrel_priv { + struct ieee80211_hw *hw; + bool has_mrr; +@@ -93,6 +97,13 @@ struct minstrel_priv { + */ + u32 fixed_rate_idx; + #endif ++#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API ++ struct rchan *relay_ev; ++ struct list_head stations; ++ spinlock_t lock; ++ u8 monitor; ++ bool manual; ++#endif + }; + + +@@ -153,6 +164,9 @@ struct minstrel_sample_category { + }; + + struct minstrel_ht_sta { ++#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API ++ struct list_head list; ++#endif + struct ieee80211_sta *sta; + + /* ampdu length (average, per sampling interval) */ +@@ -197,6 +211,80 @@ struct minstrel_ht_sta { + }; + + void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); ++ ++#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API ++void minstrel_ht_sta_update(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); ++void minstrel_ht_sta_remove(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); ++void __minstrel_ht_report_tx_status(struct minstrel_priv *mp, ++ struct minstrel_ht_sta *mi, ++ struct ieee80211_tx_info *info, ++ u16 *rate_list, int n_rates); ++void __minstrel_ht_report_rate_update(struct minstrel_priv *mp, ++ struct minstrel_ht_sta *mi, u16 rate, ++ struct minstrel_rate_stats *mrs); ++void minstrel_ht_add_debugfs_api(struct ieee80211_hw *hw, void *priv, ++ struct dentry *dir); ++void minstrel_ht_remove_debugfs_api(void *priv); ++#else ++static inline void ++minstrel_ht_sta_update(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) ++{ ++} ++static inline void ++minstrel_ht_sta_remove(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) ++{ ++} ++static inline void ++minstrel_ht_add_debugfs_api(struct ieee80211_hw *hw, void *priv, ++ struct dentry *dir) ++{ ++} ++static inline void ++minstrel_ht_remove_debugfs_api(void *priv) ++{ ++} ++#endif ++ ++static inline void ++minstrel_ht_report_tx_status(struct minstrel_priv *mp, ++ struct minstrel_ht_sta *mi, ++ struct ieee80211_tx_info *info, ++ u16 *rate_list, int n_rates) ++{ ++#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API ++ if (!(mp->monitor & MINSTREL_MONITOR_TXS)) ++ return; ++ ++ __minstrel_ht_report_tx_status(mp, mi, info, rate_list, n_rates); ++#endif ++} ++ ++static inline void ++minstrel_ht_report_rate_update(struct minstrel_priv *mp, ++ struct minstrel_ht_sta *mi, u16 rate, ++ struct minstrel_rate_stats *mrs) ++{ ++#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API ++ if (!(mp->monitor & MINSTREL_MONITOR_STATS)) ++ return; ++ ++ __minstrel_ht_report_rate_update(mp, mi, rate, mrs); ++#endif ++} ++ ++static inline bool ++minstrel_ht_manual_mode(struct minstrel_priv *mp) ++{ ++#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API ++ return mp->manual; ++#else ++ return false; ++#endif ++} ++ ++void minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ struct ieee80211_sta_rates *ratetbl, int offset, ++ int index); + int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, + int prob_avg); + +--- /dev/null ++++ b/net/mac80211/rc80211_minstrel_ht_api.c +@@ -0,0 +1,540 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) 2021 Felix Fietkau ++ */ ++#include ++#include ++#include ++#include ++#include "rc80211_minstrel_ht.h" ++ ++enum sta_cmd { ++ STA_CMD_PROBE, ++ STA_CMD_RATES, ++}; ++ ++static void ++minstrel_ht_print_rate_durations(struct seq_file *s, int group) ++{ ++ const struct mcs_group *g = &minstrel_mcs_groups[group]; ++ int n_rates; ++ int i; ++ ++ if (g->flags & IEEE80211_TX_RC_VHT_MCS) ++ n_rates = 10; ++ else ++ n_rates = 8; ++ ++ seq_printf(s, "%x", g->duration[0] << g->shift); ++ for (i = 1; i < n_rates; i++) ++ seq_printf(s, ",%x", g->duration[i] << g->shift); ++} ++ ++static int ++minstrel_ht_read_api_info(struct seq_file *s, void *data) ++{ ++ int i; ++ ++ seq_printf(s, "#group;index;offset;type;nss;bw;gi;airtime\n"); ++ seq_printf(s, "#sta;action;macaddr;overhead_mcs;overhead_legacy;supported\n"); ++ seq_printf(s, "#txs;macaddr;num_frames;num_acked;probe;rates;counts\n"); ++ seq_printf(s, "#stats;macaddr;rate;avg_prob;avg_tp;cur_success;cur_attempts;hist_success;hist_attempts\n"); ++ seq_printf(s, "#rates;macaddr;rates;counts\n"); ++ seq_printf(s, "#probe;macaddr;rate\n"); ++ for (i = 0; i < MINSTREL_GROUPS_NB; i++) { ++ const struct mcs_group *g = &minstrel_mcs_groups[i]; ++ const char *type; ++ ++ if (i == MINSTREL_CCK_GROUP) ++ type = "cck"; ++ else if (i == MINSTREL_OFDM_GROUP) ++ type = "ofdm"; ++ else if (g->flags & IEEE80211_TX_RC_VHT_MCS) ++ type = "vht"; ++ else ++ type = "ht"; ++ ++ seq_printf(s, "group;%x;%x;%s;%x;%x;%x;", ++ i, (u32) MI_RATE(i, 0), type, g->streams, g->bw, ++ !!(g->flags & IEEE80211_TX_RC_SHORT_GI)); ++ minstrel_ht_print_rate_durations(s, i); ++ seq_printf(s, "\n"); ++ } ++ ++ return 0; ++} ++ ++static struct dentry * ++create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode, ++ struct rchan_buf *buf, int *is_global) ++{ ++ struct dentry *f; ++ ++ f = debugfs_create_file("api_event", mode, parent, buf, ++ &relay_file_operations); ++ if (IS_ERR(f)) ++ return NULL; ++ ++ *is_global = 1; ++ ++ return f; ++} ++ ++static int ++remove_buf_file_cb(struct dentry *f) ++{ ++ debugfs_remove(f); ++ ++ return 0; ++} ++ ++static struct rchan_callbacks relay_ev_cb = { ++ .create_buf_file = create_buf_file_cb, ++ .remove_buf_file = remove_buf_file_cb, ++}; ++ ++static void ++minstrel_ht_dump_sta(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ const char *type) ++{ ++ char info[64 + MINSTREL_GROUPS_NB * 4]; ++ int ofs = 0; ++ int i; ++ ++ ofs += scnprintf(info + ofs, sizeof(info) - ofs, "%llx;sta;%s;%pM;%x;%x;", ++ (unsigned long long)ktime_get_boottime_ns(), ++ type, mi->sta->addr, mi->overhead, mi->overhead_legacy); ++ ++ ofs += scnprintf(info + ofs, sizeof(info) - ofs, "%x", ++ mi->supported[0]); ++ for (i = 1; i < MINSTREL_GROUPS_NB; i++) ++ ofs += scnprintf(info + ofs, sizeof(info) - ofs, ",%x", ++ mi->supported[i]); ++ ++ ofs += scnprintf(info + ofs, sizeof(info) - ofs, "\n"); ++ relay_write(mp->relay_ev, info, ofs); ++ relay_flush(mp->relay_ev); ++} ++ ++static void ++__minstrel_ht_dump_stations(struct minstrel_priv *mp, const char *type) ++{ ++ struct minstrel_ht_sta *mi; ++ ++ list_for_each_entry(mi, &mp->stations, list) ++ minstrel_ht_dump_sta(mp, mi, type); ++} ++ ++static void ++minstrel_ht_dump_stations(struct minstrel_priv *mp) ++{ ++ spin_lock_bh(&mp->lock); ++ __minstrel_ht_dump_stations(mp, "dump"); ++ spin_unlock_bh(&mp->lock); ++} ++ ++static void ++minstrel_ht_api_start(struct minstrel_priv *mp, char *params) ++{ ++ char *cur; ++ u8 mask = 0; ++ ++ spin_lock_bh(&mp->lock); ++ ++ while ((cur = strsep(¶ms, ";")) != NULL) { ++ if (!strlen(cur)) ++ break; ++ ++ if (!strcmp(cur, "txs")) ++ mask |= MINSTREL_MONITOR_TXS; ++ else if (!strcmp(cur, "sta")) ++ mask |= MINSTREL_MONITOR_STA; ++ else if (!strcmp(cur, "stats")) ++ mask |= MINSTREL_MONITOR_STATS; ++ } ++ ++ if (!mask) ++ mask = MINSTREL_MONITOR_TXS; ++ ++ if (!mp->monitor) ++ __minstrel_ht_dump_stations(mp, "add"); ++ mp->monitor = mask | MINSTREL_MONITOR_STA; ++ ++ spin_unlock_bh(&mp->lock); ++} ++ ++static void ++minstrel_ht_api_stop(struct minstrel_priv *mp) ++{ ++ spin_lock_bh(&mp->lock); ++ mp->monitor = 0; ++ relay_reset(mp->relay_ev); ++ spin_unlock_bh(&mp->lock); ++} ++ ++static void ++minstrel_ht_reset_sample_table(struct minstrel_ht_sta *mi) ++{ ++ memset(mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates, 0, ++ sizeof(mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates)); ++} ++ ++static void ++minstrel_ht_api_set_manual(struct minstrel_priv *mp, bool manual) ++{ ++ struct minstrel_ht_sta *mi; ++ ++ mp->manual = manual; ++ ++ spin_lock_bh(&mp->lock); ++ list_for_each_entry(mi, &mp->stations, list) ++ minstrel_ht_reset_sample_table(mi); ++ spin_unlock_bh(&mp->lock); ++} ++ ++static struct minstrel_ht_sta * ++minstrel_ht_api_get_sta(struct minstrel_priv *mp, const u8 *macaddr) ++{ ++ struct minstrel_ht_sta *mi; ++ ++ list_for_each_entry(mi, &mp->stations, list) { ++ if (!memcmp(mi->sta->addr, macaddr, ETH_ALEN)) ++ return mi; ++ } ++ ++ return NULL; ++} ++ ++static int ++minstrel_ht_get_args(char **dest, int dest_size, char *str, char *sep) ++{ ++ int i, n; ++ ++ for (i = 0, n = 0; i < dest_size; i++) { ++ if (!str) { ++ dest[i] = NULL; ++ continue; ++ } ++ ++ dest[i] = strsep(&str, sep); ++ if (dest[i]) ++ n++; ++ } ++ ++ return n; ++} ++ ++static bool ++minstrel_ht_valid_rate(struct minstrel_ht_sta *mi, u32 rate) ++{ ++ int group, idx; ++ ++ group = MI_RATE_GROUP(rate); ++ if (group >= MINSTREL_GROUPS_NB) ++ return false; ++ ++ idx = MI_RATE_IDX(rate); ++ ++ return !!(mi->supported[group] & BIT(idx)); ++} ++ ++static int ++minstrel_ht_rate_from_str(struct minstrel_ht_sta *mi, const char *str) ++{ ++ unsigned int rate; ++ ++ if (kstrtouint(str, 16, &rate)) ++ return -EINVAL; ++ ++ if (!minstrel_ht_valid_rate(mi, rate)) ++ return -EINVAL; ++ ++ return rate; ++} ++ ++static int ++minstrel_ht_set_probe_rate(struct minstrel_ht_sta *mi, const char *rate_str) ++{ ++ u16 *sample_rates; ++ int rate, i; ++ ++ if (!rate_str) ++ return -EINVAL; ++ ++ rate = minstrel_ht_rate_from_str(mi, rate_str); ++ if (rate < 0) ++ return rate; ++ ++ sample_rates = mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates; ++ for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) { ++ if (sample_rates[i]) ++ continue; ++ ++ sample_rates[i] = rate; ++ mi->sample_time = jiffies; ++ return 0; ++ } ++ ++ return -ENOSPC; ++} ++ ++static int ++minstrel_ht_set_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ char *rate_str, char *count_str) ++{ ++ struct ieee80211_sta_rates *ratetbl; ++ unsigned int count; ++ char *countlist[4]; ++ char *ratelist[4]; ++ int rate; ++ int n_rates; ++ int n_count; ++ int err = -EINVAL; ++ int i; ++ ++ if (!rate_str || !count_str) ++ return -EINVAL; ++ ++ ratetbl = kzalloc(sizeof(*ratetbl), GFP_ATOMIC); ++ if (!ratetbl) ++ return -ENOMEM; ++ ++ n_rates = minstrel_ht_get_args(ratelist, ARRAY_SIZE(ratelist), ++ rate_str, ","); ++ n_count = minstrel_ht_get_args(countlist, ARRAY_SIZE(countlist), ++ count_str, ","); ++ for (i = 0; i < min(n_rates, n_count); i++) { ++ rate = minstrel_ht_rate_from_str(mi, ratelist[i]); ++ if (rate < 0) ++ goto error; ++ ++ if (kstrtouint(countlist[0], 16, &count)) ++ goto error; ++ ++ minstrel_ht_set_rate(mp, mi, ratetbl, i, rate); ++ ratetbl->rate[i].count = count; ++ ratetbl->rate[i].count_rts = count; ++ ratetbl->rate[i].count_cts = count; ++ } ++ ++ rate_control_set_rates(mp->hw, mi->sta, ratetbl); ++ ++ return 0; ++ ++error: ++ kfree(ratetbl); ++ return err; ++} ++ ++static int ++minstrel_ht_api_sta_cmd(struct minstrel_priv *mp, enum sta_cmd cmd, ++ char *arg_str) ++{ ++ struct minstrel_ht_sta *mi; ++ uint8_t macaddr[ETH_ALEN]; ++ char *args[3]; ++ int n_args; ++ int ret = -EINVAL; ++ ++ spin_lock_bh(&mp->lock); ++ if (!mp->manual) ++ goto out; ++ ++ n_args = minstrel_ht_get_args(args, ARRAY_SIZE(args), arg_str, ";"); ++ if (!args[0]) ++ goto out; ++ ++ if (!mac_pton(args[0], macaddr)) ++ goto out; ++ ++ mi = minstrel_ht_api_get_sta(mp, macaddr); ++ if (!mi) { ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ switch (cmd) { ++ case STA_CMD_PROBE: ++ ret = minstrel_ht_set_probe_rate(mi, args[1]); ++ break; ++ case STA_CMD_RATES: ++ ret = minstrel_ht_set_rates(mp, mi, args[1], args[2]); ++ break; ++ } ++ ++out: ++ spin_unlock_bh(&mp->lock); ++ ++ return ret; ++} ++ ++static ssize_t ++minstrel_ht_control_write(struct file *file, const char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct minstrel_priv *mp = file->private_data; ++ char *pos, *cur; ++ char buf[64]; ++ size_t len = count; ++ int err; ++ ++ if (len > sizeof(buf) - 1) ++ return -EINVAL; ++ ++ if (copy_from_user(buf, userbuf, len)) ++ return -EFAULT; ++ ++ if (count > 0 && buf[len - 1] == '\n') ++ len--; ++ ++ buf[len] = 0; ++ if (!len) ++ return count; ++ ++ pos = buf; ++ cur = strsep(&pos, ";"); ++ ++ err = 0; ++ if (!strcmp(cur, "dump")) ++ minstrel_ht_dump_stations(mp); ++ else if (!strcmp(cur, "start")) ++ minstrel_ht_api_start(mp, pos); ++ else if (!strcmp(cur, "stop")) ++ minstrel_ht_api_stop(mp); ++ else if (!strcmp(cur, "manual")) ++ minstrel_ht_api_set_manual(mp, true); ++ else if (!strcmp(cur, "auto")) ++ minstrel_ht_api_set_manual(mp, false); ++ else if (!strcmp(cur, "rates")) ++ err = minstrel_ht_api_sta_cmd(mp, STA_CMD_RATES, pos); ++ else if (!strcmp(cur, "probe")) ++ err = minstrel_ht_api_sta_cmd(mp, STA_CMD_PROBE, pos); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ return err; ++ ++ return count; ++} ++ ++static const struct file_operations fops_control = { ++ .open = simple_open, ++ .llseek = generic_file_llseek, ++ .write = minstrel_ht_control_write, ++}; ++ ++void minstrel_ht_sta_update(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) ++{ ++ bool add = list_empty(&mi->list); ++ ++ spin_lock_bh(&mp->lock); ++ if (add) ++ list_add(&mi->list, &mp->stations); ++ if (mp->monitor) ++ minstrel_ht_dump_sta(mp, mi, add ? "add" : "update"); ++ spin_unlock_bh(&mp->lock); ++} ++ ++void minstrel_ht_sta_remove(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) ++{ ++ char info[64]; ++ int ofs = 0; ++ ++ spin_lock_bh(&mp->lock); ++ list_del_init(&mi->list); ++ ++ if (!mp->monitor) ++ goto out; ++ ++ ofs = scnprintf(info, sizeof(info), "%llx;sta;remove;%pM;;;\n", ++ (unsigned long long)ktime_get_boottime_ns(), ++ mi->sta->addr); ++ relay_write(mp->relay_ev, info, ofs); ++ relay_flush(mp->relay_ev); ++ ++out: ++ spin_unlock_bh(&mp->lock); ++} ++ ++void __minstrel_ht_report_tx_status(struct minstrel_priv *mp, ++ struct minstrel_ht_sta *mi, ++ struct ieee80211_tx_info *info, ++ u16 *rate_list, ++ int n_rates) ++{ ++ char txs[64 + IEEE80211_TX_MAX_RATES * 8]; ++ int ofs = 0; ++ int i; ++ ++ if (!n_rates) ++ return; ++ ++ ofs += scnprintf(txs, sizeof(txs), "%llx;txs;%pM;%x;%x;%x;", ++ (unsigned long long)ktime_get_boottime_ns(), ++ mi->sta->addr, ++ info->status.ampdu_len, ++ info->status.ampdu_ack_len, ++ !!(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)); ++ ++ ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, "%x", ++ rate_list[0]); ++ for (i = 1; i < n_rates; i++) ++ ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, ",%x", ++ rate_list[i]); ++ ++ ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, ";%x", ++ info->status.rates[0].count); ++ for (i = 1; i < n_rates; i++) ++ ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, ",%x", ++ info->status.rates[i].count); ++ ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, "\n"); ++ relay_write(mp->relay_ev, txs, ofs); ++ relay_flush(mp->relay_ev); ++} ++ ++void __minstrel_ht_report_rate_update(struct minstrel_priv *mp, ++ struct minstrel_ht_sta *mi, u16 rate, ++ struct minstrel_rate_stats *mrs) ++{ ++ char stat[100]; ++ int ofs; ++ int tp; ++ ++ tp = minstrel_ht_get_tp_avg(mi, MI_RATE_GROUP(rate), MI_RATE_IDX(rate), ++ mrs->prob_avg); ++ ++ ofs = scnprintf(stat, sizeof(stat), ++ "%llx;stats;%pM;%x;%x;%x;%x;%x;%x;%x\n", ++ (unsigned long long)ktime_get_boottime_ns(), ++ mi->sta->addr, rate, ++ MINSTREL_TRUNC(mrs->prob_avg * 1000), tp, ++ mrs->last_success, ++ mrs->last_attempts, ++ mrs->succ_hist, mrs->att_hist); ++ ++ relay_write(mp->relay_ev, stat, ofs); ++ relay_flush(mp->relay_ev); ++} ++ ++void minstrel_ht_add_debugfs_api(struct ieee80211_hw *hw, void *priv, ++ struct dentry *dir) ++{ ++ struct minstrel_priv *mp = priv; ++ ++ spin_lock_init(&mp->lock); ++ INIT_LIST_HEAD(&mp->stations); ++ mp->relay_ev = relay_open("api_event", dir, 256, 512, &relay_ev_cb, ++ NULL); ++ debugfs_create_devm_seqfile(&hw->wiphy->dev, "api_info", ++ dir, minstrel_ht_read_api_info); ++ debugfs_create_file("api_control", 0200, dir, mp, &fops_control); ++} ++ ++void minstrel_ht_remove_debugfs_api(void *priv) ++{ ++ struct minstrel_priv *mp = priv; ++ ++ if (mp->relay_ev) ++ relay_close(mp->relay_ev); ++}