diff --git a/package/kernel/mac80211/Makefile b/package/kernel/mac80211/Makefile index d0620c556a431..dd39c2d069322 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 bf87d3551a18b..3c9180b1137cc 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 d183419a47bd0..9ce44fd288018 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 ce8effe3c3d8c..fa007e73a1f17 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 acb9ad443c0b9..cd2bdbf1a0c52 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 e74d9a9aa0d8e..1c52132da65c8 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 ca02dfb06f9f0..0000000000000 --- 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 9c6e971f9da5a..0000000000000 --- 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 c3bf7ccc7abda..0000000000000 --- 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 df67d2f1019ea..0000000000000 --- 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 96ee595ac1a81..1cab2eb1947dd 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 0000000000000..ef57234bead8b --- /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); ++}