[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