[PATCH ath-next] wifi: ath12k: Fix legacy rate mapping for monitor mode capture
Baochen Qiang
baochen.qiang at oss.qualcomm.com
Wed Feb 25 17:48:08 PST 2026
On 2/9/2026 1:49 PM, Thiraviyam Mariyappan wrote:
> From: P Praneesh <praneesh.p at oss.qualcomm.com>
>
> The current implementation incorrectly reports legacy CCK and OFDM rates
> in monitor mode radiotap headers. The rate field displays wrong values,
> for example showing 11 Mbps when the actual rate is 1 Mbps. This occurs
> because the HAL layer uses a unified enum for both CCK and OFDM rates
> without distinguishing between long/short preamble variants and proper
> rate mapping to hardware rate indices.
>
> The root cause is threefold:
>
> 1. The hal_rx_legacy_rate enum conflates CCK and OFDM rates into a
> single enumeration, making it impossible to differentiate between
> 802.11b CCK rates (with long/short preamble variants) and 802.11a/g
> OFDM rates.
>
> 2. The L-SIG-B parsing function maps hardware rate values to the wrong
> enum values. For CCK rates, it incorrectly combines long and short
> preamble cases (e.g., cases 2 and 5 both map to 2 Mbps), losing
> preamble information critical for proper rate identification.
>
> 3. The mac layer's rate-to-index conversion function does not properly
> handle the precedence between long preamble, short preamble, and
> OFDM rates when matching hardware rate values.
>
> Split the hal_rx_legacy_rate enum into two separate enumerations:
> hal_rx_legacy_rate for CCK rates with explicit long preamble (LP) and
> short preamble (SP) variants, and hal_rx_legacy_rates_ofdm for OFDM
> rates. This separation allows proper identification of rate types and
> preamble modes.
>
> Introduce a new mapping ath12k_wifi7_hal_mon_map_legacy_rate_to_hw_rate()
> that converts HAL CCK rate enums to hardware rate indices defined in
> ath12k_hw_rate_cck. This ensures the rate field in ppdu_info contains
> the correct hardware rate index that matches the mac layer's expectations.
>
> Update the L-SIG-B parsing to map each hardware rate value (1-7) to its
> corresponding CCK rate enum with proper preamble designation:
> - Cases 1-4: Long preamble (1, 2, 5.5, 11 Mbps)
> - Cases 5-7: Short preamble (2, 5.5, 11 Mbps)
>
> Update the L-SIG-A parsing to use the new OFDM-specific enum values,
> maintaining the existing rate mapping for 802.11a/g OFDM rates.
>
> Refactor the mac layer's ath12k_mac_hw_rate_to_idx() function to
> implement proper matching precedence:
> 1. First match OFDM rates using the IEEE80211_RATE_MANDATORY_A flag
> 2. Then match CCK short preamble rates
> 3. Finally match CCK long preamble rates as fallback
>
> Add helper macros ATH12K_MAC_RATE_A_M and ATH12K_MAC_RATE_B to improve
> readability of the rate table initialization and ensure the mandatory
> flag is set for OFDM rates.
>
> This fix ensures monitor mode captures display accurate rate information
> in the radiotap header, correctly distinguishing between 1 Mbps and
> 11 Mbps, and properly identifying preamble types for CCK rates.
>
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01181-QCAHKSWPL_SILICONZ-1
>
> Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
> Signed-off-by: P Praneesh <praneesh.p at oss.qualcomm.com>
> Signed-off-by: Thiraviyam Mariyappan <thiraviyam.mariyappan at oss.qualcomm.com>
> ---
> drivers/net/wireless/ath/ath12k/hal.h | 31 +++++---
> drivers/net/wireless/ath/ath12k/mac.c | 51 +++++++------
> .../net/wireless/ath/ath12k/wifi7/dp_mon.c | 76 +++++++++++++++----
> 3 files changed, 108 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h
> index 43e3880f8257..bf4f7dbae866 100644
> --- a/drivers/net/wireless/ath/ath12k/hal.h
> +++ b/drivers/net/wireless/ath/ath12k/hal.h
> @@ -268,21 +268,28 @@ enum hal_rx_reception_type {
> };
>
> enum hal_rx_legacy_rate {
> - HAL_RX_LEGACY_RATE_1_MBPS,
> - HAL_RX_LEGACY_RATE_2_MBPS,
> - HAL_RX_LEGACY_RATE_5_5_MBPS,
> - HAL_RX_LEGACY_RATE_6_MBPS,
> - HAL_RX_LEGACY_RATE_9_MBPS,
> - HAL_RX_LEGACY_RATE_11_MBPS,
> - HAL_RX_LEGACY_RATE_12_MBPS,
> - HAL_RX_LEGACY_RATE_18_MBPS,
> - HAL_RX_LEGACY_RATE_24_MBPS,
> - HAL_RX_LEGACY_RATE_36_MBPS,
> - HAL_RX_LEGACY_RATE_48_MBPS,
> - HAL_RX_LEGACY_RATE_54_MBPS,
> + HAL_RX_LEGACY_RATE_LP_1_MBPS,
> + HAL_RX_LEGACY_RATE_LP_2_MBPS,
> + HAL_RX_LEGACY_RATE_LP_5_5_MBPS,
> + HAL_RX_LEGACY_RATE_LP_11_MBPS,
> + HAL_RX_LEGACY_RATE_SP_2_MBPS,
> + HAL_RX_LEGACY_RATE_SP_5_5_MBPS,
> + HAL_RX_LEGACY_RATE_SP_11_MBPS,
> HAL_RX_LEGACY_RATE_INVALID,
> };
>
> +enum hal_rx_legacy_rates_ofdm {
> + HAL_RX_LEGACY_RATE_OFDM_48_MBPS,
> + HAL_RX_LEGACY_RATE_OFDM_24_MBPS,
> + HAL_RX_LEGACY_RATE_OFDM_12_MBPS,
> + HAL_RX_LEGACY_RATE_OFDM_6_MBPS,
> + HAL_RX_LEGACY_RATE_OFDM_54_MBPS,
> + HAL_RX_LEGACY_RATE_OFDM_36_MBPS,
> + HAL_RX_LEGACY_RATE_OFDM_18_MBPS,
> + HAL_RX_LEGACY_RATE_OFDM_9_MBPS,
> + HAL_RX_LEGACY_RATE_OFDM_INVALID,
> +};
> +
> enum hal_ring_type {
> HAL_REO_DST,
> HAL_REO_EXCEPTION,
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index 6c534f0d59ce..035937dde5c6 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -164,30 +164,31 @@ static const struct ieee80211_channel ath12k_6ghz_channels[] = {
> CHAN6G(233, 7115, 0),
> };
>
> +#define ATH12K_MAC_RATE_A_M(bps, code) \
> + { .bitrate = (bps), .hw_value = (code),\
> + .flags = IEEE80211_RATE_MANDATORY_A }
> +
> +#define ATH12K_MAC_RATE_B(bps, code, code_short) \
> + { .bitrate = (bps), .hw_value = (code), .hw_value_short = (code_short),\
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE }
> +
> static struct ieee80211_rate ath12k_legacy_rates[] = {
> { .bitrate = 10,
> .hw_value = ATH12K_HW_RATE_CCK_LP_1M },
> - { .bitrate = 20,
> - .hw_value = ATH12K_HW_RATE_CCK_LP_2M,
> - .hw_value_short = ATH12K_HW_RATE_CCK_SP_2M,
> - .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> - { .bitrate = 55,
> - .hw_value = ATH12K_HW_RATE_CCK_LP_5_5M,
> - .hw_value_short = ATH12K_HW_RATE_CCK_SP_5_5M,
> - .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> - { .bitrate = 110,
> - .hw_value = ATH12K_HW_RATE_CCK_LP_11M,
> - .hw_value_short = ATH12K_HW_RATE_CCK_SP_11M,
> - .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> -
> - { .bitrate = 60, .hw_value = ATH12K_HW_RATE_OFDM_6M },
> - { .bitrate = 90, .hw_value = ATH12K_HW_RATE_OFDM_9M },
> - { .bitrate = 120, .hw_value = ATH12K_HW_RATE_OFDM_12M },
> - { .bitrate = 180, .hw_value = ATH12K_HW_RATE_OFDM_18M },
> - { .bitrate = 240, .hw_value = ATH12K_HW_RATE_OFDM_24M },
> - { .bitrate = 360, .hw_value = ATH12K_HW_RATE_OFDM_36M },
> - { .bitrate = 480, .hw_value = ATH12K_HW_RATE_OFDM_48M },
> - { .bitrate = 540, .hw_value = ATH12K_HW_RATE_OFDM_54M },
> + ATH12K_MAC_RATE_B(20, ATH12K_HW_RATE_CCK_LP_2M,
> + ATH12K_HW_RATE_CCK_SP_2M),
> + ATH12K_MAC_RATE_B(55, ATH12K_HW_RATE_CCK_LP_5_5M,
> + ATH12K_HW_RATE_CCK_SP_5_5M),
> + ATH12K_MAC_RATE_B(110, ATH12K_HW_RATE_CCK_LP_11M,
> + ATH12K_HW_RATE_CCK_SP_11M),
> + ATH12K_MAC_RATE_A_M(60, ATH12K_HW_RATE_OFDM_6M),
> + ATH12K_MAC_RATE_A_M(90, ATH12K_HW_RATE_OFDM_9M),
> + ATH12K_MAC_RATE_A_M(120, ATH12K_HW_RATE_OFDM_12M),
> + ATH12K_MAC_RATE_A_M(180, ATH12K_HW_RATE_OFDM_18M),
> + ATH12K_MAC_RATE_A_M(240, ATH12K_HW_RATE_OFDM_24M),
> + ATH12K_MAC_RATE_A_M(360, ATH12K_HW_RATE_OFDM_36M),
> + ATH12K_MAC_RATE_A_M(480, ATH12K_HW_RATE_OFDM_48M),
> + ATH12K_MAC_RATE_A_M(540, ATH12K_HW_RATE_OFDM_54M),
> };
>
> static const int
> @@ -732,11 +733,17 @@ u8 ath12k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
> if (ath12k_mac_bitrate_is_cck(rate->bitrate) != cck)
> continue;
>
> - if (rate->hw_value == hw_rate)
> + /* To handle 802.11a PPDU type */
> + if ((!cck) && (rate->hw_value == hw_rate) &&
> + (rate->flags & IEEE80211_RATE_MANDATORY_A))
> return i;
> + /* To handle 802.11b short PPDU type */
> else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE &&
> rate->hw_value_short == hw_rate)
> return i;
> + /* To handle 802.11b long PPDU type */
> + else if (rate->hw_value == hw_rate)
> + return i;
> }
>
> return 0;
> diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c
> index bd741532b7dc..074df2ab6bbf 100644
> --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c
> +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c
> @@ -405,6 +405,42 @@ ath12k_wifi7_dp_mon_hal_rx_parse_user_info(const struct hal_receive_user_info *r
> }
> }
>
> +static __always_inline u8
> +ath12k_wifi7_hal_mon_map_legacy_rate_to_hw_rate(u8 rate)
> +{
> + u8 ath12k_rate;
> +
> + /* Map hal_rx_legacy_rate to ath12k_hw_rate_cck */
> + switch (rate) {
> + case HAL_RX_LEGACY_RATE_LP_1_MBPS:
> + ath12k_rate = ATH12K_HW_RATE_CCK_LP_1M;
> + break;
> + case HAL_RX_LEGACY_RATE_LP_2_MBPS:
> + ath12k_rate = ATH12K_HW_RATE_CCK_LP_2M;
> + break;
> + case HAL_RX_LEGACY_RATE_LP_5_5_MBPS:
> + ath12k_rate = ATH12K_HW_RATE_CCK_LP_5_5M;
> + break;
> + case HAL_RX_LEGACY_RATE_LP_11_MBPS:
> + ath12k_rate = ATH12K_HW_RATE_CCK_LP_11M;
> + break;
> + case HAL_RX_LEGACY_RATE_SP_2_MBPS:
> + ath12k_rate = ATH12K_HW_RATE_CCK_SP_2M;
> + break;
> + case HAL_RX_LEGACY_RATE_SP_5_5_MBPS:
> + ath12k_rate = ATH12K_HW_RATE_CCK_SP_5_5M;
> + break;
> + case HAL_RX_LEGACY_RATE_SP_11_MBPS:
> + ath12k_rate = ATH12K_HW_RATE_CCK_SP_11M;
> + break;
> + default:
> + ath12k_rate = rate;
> + break;
> + }
> +
> + return ath12k_rate;
> +}
> +
> static void
> ath12k_wifi7_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb,
> struct hal_rx_mon_ppdu_info *ppdu_info)
> @@ -415,25 +451,32 @@ ath12k_wifi7_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb,
> rate = u32_get_bits(info0, HAL_RX_LSIG_B_INFO_INFO0_RATE);
> switch (rate) {
> case 1:
> - rate = HAL_RX_LEGACY_RATE_1_MBPS;
> + rate = HAL_RX_LEGACY_RATE_LP_1_MBPS;
> break;
> case 2:
> - case 5:
> - rate = HAL_RX_LEGACY_RATE_2_MBPS;
> + rate = HAL_RX_LEGACY_RATE_LP_2_MBPS;
> break;
> case 3:
> - case 6:
> - rate = HAL_RX_LEGACY_RATE_5_5_MBPS;
> + rate = HAL_RX_LEGACY_RATE_LP_5_5_MBPS;
> break;
> case 4:
> + rate = HAL_RX_LEGACY_RATE_LP_11_MBPS;
> + break;
> + case 5:
> + rate = HAL_RX_LEGACY_RATE_SP_2_MBPS;
> + break;
> + case 6:
> + rate = HAL_RX_LEGACY_RATE_SP_5_5_MBPS;
> + break;
> case 7:
> - rate = HAL_RX_LEGACY_RATE_11_MBPS;
> + rate = HAL_RX_LEGACY_RATE_SP_11_MBPS;
> break;
> default:
> rate = HAL_RX_LEGACY_RATE_INVALID;
> + break;
> }
>
> - ppdu_info->rate = rate;
> + ppdu_info->rate = ath12k_wifi7_hal_mon_map_legacy_rate_to_hw_rate(rate);
> ppdu_info->cck_flag = 1;
> }
>
> @@ -447,31 +490,32 @@ ath12k_wifi7_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga,
> rate = u32_get_bits(info0, HAL_RX_LSIG_A_INFO_INFO0_RATE);
> switch (rate) {
> case 8:
> - rate = HAL_RX_LEGACY_RATE_48_MBPS;
> + rate = HAL_RX_LEGACY_RATE_OFDM_48_MBPS;
> break;
> case 9:
> - rate = HAL_RX_LEGACY_RATE_24_MBPS;
> + rate = HAL_RX_LEGACY_RATE_OFDM_24_MBPS;
> break;
> case 10:
> - rate = HAL_RX_LEGACY_RATE_12_MBPS;
> + rate = HAL_RX_LEGACY_RATE_OFDM_12_MBPS;
> break;
> case 11:
> - rate = HAL_RX_LEGACY_RATE_6_MBPS;
> + rate = HAL_RX_LEGACY_RATE_OFDM_6_MBPS;
> break;
> case 12:
> - rate = HAL_RX_LEGACY_RATE_54_MBPS;
> + rate = HAL_RX_LEGACY_RATE_OFDM_54_MBPS;
> break;
> case 13:
> - rate = HAL_RX_LEGACY_RATE_36_MBPS;
> + rate = HAL_RX_LEGACY_RATE_OFDM_36_MBPS;
> break;
> case 14:
> - rate = HAL_RX_LEGACY_RATE_18_MBPS;
> + rate = HAL_RX_LEGACY_RATE_OFDM_18_MBPS;
> break;
> case 15:
> - rate = HAL_RX_LEGACY_RATE_9_MBPS;
> + rate = HAL_RX_LEGACY_RATE_OFDM_9_MBPS;
> break;
> default:
> - rate = HAL_RX_LEGACY_RATE_INVALID;
> + rate = HAL_RX_LEGACY_RATE_OFDM_INVALID;
> + break;
> }
>
> ppdu_info->rate = rate;
>
> base-commit: 2c1ba9c2adf0fda96eaaebd8799268a7506a8fc9
Reviewed-by: Baochen Qiang <baochen.qiang at oss.qualcomm.com>
More information about the ath12k
mailing list