diff mbox series

cfg80211: S1G rate information and calculations

Message ID 20230504024923.1925530-1-gilad.itzkovitch@morsemicro.com (mailing list archive)
State Superseded
Delegated to: Johannes Berg
Headers show
Series cfg80211: S1G rate information and calculations | expand

Commit Message

Gilad Itzkovitch May 4, 2023, 2:49 a.m. UTC
Increase the size of S1G rate_info flags to support S1G and add
flags for new S1G MCS and the supported bandwidths. Also, include
S1G rate information to netlink STA rate message. Lastly, add
rate calculation function for S1G MCS.

Signed-off-by: Gilad Itzkovitch <gilad.itzkovitch@morsemicro.com>
---
 include/net/cfg80211.h       |  18 +++++-
 include/uapi/linux/nl80211.h |  14 +++++
 net/wireless/nl80211.c       |  23 ++++++++
 net/wireless/util.c          | 110 +++++++++++++++++++++++++++++++++++
 4 files changed, 162 insertions(+), 3 deletions(-)


base-commit: 28b17f6270f182e22cdad5a0fdc4979031e4486a

Comments

Johannes Berg May 4, 2023, 6:56 a.m. UTC | #1
On Thu, 2023-05-04 at 14:49 +1200, Gilad Itzkovitch wrote:
> 
>   * Information about a receiving or transmitting bitrate
>   *
>   * @flags: bitflag of flags from &enum rate_info_flags
> - * @mcs: mcs index if struct describes an HT/VHT/HE rate
>   * @legacy: bitrate in 100kbit/s for 802.11abg
> + * @mcs: mcs index if struct describes an HT/VHT/HE rate

Did you mean to add S1G to this list? Not sure why else you moved it at
all :) And maybe if yes, add EHT while at it. But I can do that
separately too, or maybe we should just remove the list and say "if not
legacy" or something...


But I can also just change that when I commit it, if you prefer/don't
mind.

>   * @nss: number of streams (VHT & HE only)
>   * @bw: bandwidth (from &enum rate_info_bw)
>   * @he_gi: HE guard interval (from &enum nl80211_he_gi)
> @@ -1761,9 +1773,9 @@ enum rate_info_bw {
>   *	only valid if bw is %RATE_INFO_BW_EHT_RU)
>   */
>  struct rate_info {
> -	u8 flags;
> -	u8 mcs;
> +	u16 flags;
>  	u16 legacy;
> +	u8 mcs;

Oh ... I see, you moved the kernel-doc because of this reordering? No
need to do that, FWIW, the kernel-doc order doesn't really matter.

But thanks for doing this reordering to keep the struct without holes!

johannes
Gilad Itzkovitch May 4, 2023, 9:42 p.m. UTC | #2
On Thu, May 4, 2023 at 6:56 PM Johannes Berg <johannes@sipsolutions.net> wrote:
>
> On Thu, 2023-05-04 at 14:49 +1200, Gilad Itzkovitch wrote:
> >
> >   * Information about a receiving or transmitting bitrate
> >   *
> >   * @flags: bitflag of flags from &enum rate_info_flags
> > - * @mcs: mcs index if struct describes an HT/VHT/HE rate
> >   * @legacy: bitrate in 100kbit/s for 802.11abg
> > + * @mcs: mcs index if struct describes an HT/VHT/HE rate
>
> Did you mean to add S1G to this list? Not sure why else you moved it at
> all :) And maybe if yes, add EHT while at it. But I can do that
> separately too, or maybe we should just remove the list and say "if not
> legacy" or something...
>

As you said below, we shifted the kernel-doc to match the new order.
So, yes, the S1G
is missing from that list as you mentioned. Maybe it's better to
change it to non legacy as the list
becomes too long. I can change it (and add EHT) if any other change
will be required as part
of version 2, otherwise, I don’t mind you changing it as part of
committing the change.


Gilad

>
> But I can also just change that when I commit it, if you prefer/don't
> mind.
>
> >   * @nss: number of streams (VHT & HE only)
> >   * @bw: bandwidth (from &enum rate_info_bw)
> >   * @he_gi: HE guard interval (from &enum nl80211_he_gi)
> > @@ -1761,9 +1773,9 @@ enum rate_info_bw {
> >   *   only valid if bw is %RATE_INFO_BW_EHT_RU)
> >   */
> >  struct rate_info {
> > -     u8 flags;
> > -     u8 mcs;
> > +     u16 flags;
> >       u16 legacy;
> > +     u8 mcs;
>
> Oh ... I see, you moved the kernel-doc because of this reordering? No
> need to do that, FWIW, the kernel-doc order doesn't really matter.
>
> But thanks for doing this reordering to keep the struct without holes!
>
> johannes
Dan Carpenter May 11, 2023, 10:52 a.m. UTC | #3
Hi Gilad,

kernel test robot noticed the following build warnings:

url:    https://github.com/intel-lab-lkp/linux/commits/Gilad-Itzkovitch/cfg80211-S1G-rate-information-and-calculations/20230504-105056
base:   28b17f6270f182e22cdad5a0fdc4979031e4486a
patch link:    https://lore.kernel.org/r/20230504024923.1925530-1-gilad.itzkovitch%40morsemicro.com
patch subject: [PATCH] cfg80211: S1G rate information and calculations
config: microblaze-randconfig-m031-20230509 (https://download.01.org/0day-ci/archive/20230511/202305110435.EhEiLYWA-lkp@intel.com/config)
compiler: microblaze-linux-gcc (GCC) 12.1.0

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <error27@gmail.com>
| Link: https://lore.kernel.org/r/202305110435.EhEiLYWA-lkp@intel.com/

New smatch warnings:
net/wireless/util.c:1744 cfg80211_calculate_bitrate_s1g() error: buffer overflow 'base[idx]' 11 <= 11

vim +1744 net/wireless/util.c

d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1649  static u32 cfg80211_calculate_bitrate_s1g(struct rate_info *rate)
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1650  {
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1651  	/* For 1, 2, 4, 8 and 16 MHz channels */
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1652  	static const u32 base[5][11] = {
                                                                                ^^^^
11 elements.

d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1653  		{  300000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1654  		   600000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1655  		   900000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1656  		  1200000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1657  		  1800000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1658  		  2400000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1659  		  2700000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1660  		  3000000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1661  		  3600000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1662  		  4000000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1663  		  /* MCS 10 supported in 1 MHz only */
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1664  		  150000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1665  		},
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1666  		{  650000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1667  		  1300000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1668  		  1950000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1669  		  2600000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1670  		  3900000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1671  		  5200000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1672  		  5850000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1673  		  6500000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1674  		  7800000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1675  		  /* MCS 9 not valid */
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1676  		},
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1677  		{  1350000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1678  		   2700000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1679  		   4050000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1680  		   5400000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1681  		   8100000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1682  		  10800000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1683  		  12150000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1684  		  13500000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1685  		  16200000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1686  		  18000000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1687  		},
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1688  		{  2925000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1689  		   5850000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1690  		   8775000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1691  		  11700000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1692  		  17550000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1693  		  23400000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1694  		  26325000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1695  		  29250000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1696  		  35100000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1697  		  39000000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1698  		},
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1699  		{  8580000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1700  		  11700000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1701  		  17550000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1702  		  23400000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1703  		  35100000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1704  		  46800000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1705  		  52650000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1706  		  58500000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1707  		  70200000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1708  		  78000000,
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1709  		},
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1710  	};
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1711  	u32 bitrate;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1712  	/* default is 1 MHz index */
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1713  	int idx = 0;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1714  
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1715  	if (rate->mcs > 11)
                                                                      ^^^^
Needs to be >= 11 to prevent off by one.

d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1716  		goto warn;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1717  
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1718  	switch (rate->bw) {
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1719  	case RATE_INFO_BW_16:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1720  		idx = 4;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1721  		break;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1722  	case RATE_INFO_BW_8:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1723  		idx = 3;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1724  		break;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1725  	case RATE_INFO_BW_4:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1726  		idx = 2;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1727  		break;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1728  	case RATE_INFO_BW_2:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1729  		idx = 1;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1730  		break;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1731  	case RATE_INFO_BW_1:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1732  		idx = 0;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1733  		break;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1734  	case RATE_INFO_BW_5:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1735  	case RATE_INFO_BW_10:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1736  	case RATE_INFO_BW_20:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1737  	case RATE_INFO_BW_40:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1738  	case RATE_INFO_BW_80:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1739  	case RATE_INFO_BW_160:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1740  	default:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1741  		goto warn;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1742  	}
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1743  
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04 @1744  	bitrate = base[idx][rate->mcs];
                                                                            ^^^^^^^^^

d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1745  	bitrate *= rate->nss;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1746  
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1747  	if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1748  		bitrate = (bitrate / 9) * 10;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1749  	/* do NOT round down here */
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1750  	return (bitrate + 50000) / 100000;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1751  warn:
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1752  	WARN_ONCE(1, "invalid rate bw=%d, mcs=%d, nss=%d\n",
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1753  		  rate->bw, rate->mcs, rate->nss);
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1754  	return 0;
d973f1b0d4a136 Gilad Itzkovitch     2023-05-04  1755  }
diff mbox series

Patch

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 9e04f69712b1..b7c26ccdb690 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1702,6 +1702,7 @@  int cfg80211_check_station_change(struct wiphy *wiphy,
  * @RATE_INFO_FLAGS_EDMG: 60GHz MCS in EDMG mode
  * @RATE_INFO_FLAGS_EXTENDED_SC_DMG: 60GHz extended SC MCS
  * @RATE_INFO_FLAGS_EHT_MCS: EHT MCS information
+ * @RATE_INFO_FLAGS_S1G_MCS: MCS field filled with S1G MCS
  */
 enum rate_info_flags {
 	RATE_INFO_FLAGS_MCS			= BIT(0),
@@ -1712,6 +1713,7 @@  enum rate_info_flags {
 	RATE_INFO_FLAGS_EDMG			= BIT(5),
 	RATE_INFO_FLAGS_EXTENDED_SC_DMG		= BIT(6),
 	RATE_INFO_FLAGS_EHT_MCS			= BIT(7),
+	RATE_INFO_FLAGS_S1G_MCS			= BIT(8),
 };
 
 /**
@@ -1728,6 +1730,11 @@  enum rate_info_flags {
  * @RATE_INFO_BW_HE_RU: bandwidth determined by HE RU allocation
  * @RATE_INFO_BW_320: 320 MHz bandwidth
  * @RATE_INFO_BW_EHT_RU: bandwidth determined by EHT RU allocation
+ * @RATE_INFO_BW_1: 1 MHz bandwidth
+ * @RATE_INFO_BW_2: 2 MHz bandwidth
+ * @RATE_INFO_BW_4: 4 MHz bandwidth
+ * @RATE_INFO_BW_8: 8 MHz bandwidth
+ * @RATE_INFO_BW_16: 16 MHz bandwidth
  */
 enum rate_info_bw {
 	RATE_INFO_BW_20 = 0,
@@ -1739,6 +1746,11 @@  enum rate_info_bw {
 	RATE_INFO_BW_HE_RU,
 	RATE_INFO_BW_320,
 	RATE_INFO_BW_EHT_RU,
+	RATE_INFO_BW_1,
+	RATE_INFO_BW_2,
+	RATE_INFO_BW_4,
+	RATE_INFO_BW_8,
+	RATE_INFO_BW_16,
 };
 
 /**
@@ -1747,8 +1759,8 @@  enum rate_info_bw {
  * Information about a receiving or transmitting bitrate
  *
  * @flags: bitflag of flags from &enum rate_info_flags
- * @mcs: mcs index if struct describes an HT/VHT/HE rate
  * @legacy: bitrate in 100kbit/s for 802.11abg
+ * @mcs: mcs index if struct describes an HT/VHT/HE rate
  * @nss: number of streams (VHT & HE only)
  * @bw: bandwidth (from &enum rate_info_bw)
  * @he_gi: HE guard interval (from &enum nl80211_he_gi)
@@ -1761,9 +1773,9 @@  enum rate_info_bw {
  *	only valid if bw is %RATE_INFO_BW_EHT_RU)
  */
 struct rate_info {
-	u8 flags;
-	u8 mcs;
+	u16 flags;
 	u16 legacy;
+	u8 mcs;
 	u8 nss;
 	u8 bw;
 	u8 he_gi;
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c59fec406da5..435c4ac5d9bf 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3667,6 +3667,13 @@  enum nl80211_eht_ru_alloc {
  *	(u8, see &enum nl80211_eht_gi)
  * @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then
  *	non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc)
+ * @NL80211_RATE_INFO_S1G_MCS: S1G MCS index (u8, 0-10)
+ * @NL80211_RATE_INFO_S1G_NSS: S1G NSS value (u8, 1-4)
+ * @NL80211_RATE_INFO_1_MHZ_WIDTH: 1 MHz S1G rate
+ * @NL80211_RATE_INFO_2_MHZ_WIDTH: 2 MHz S1G rate
+ * @NL80211_RATE_INFO_4_MHZ_WIDTH: 4 MHz S1G rate
+ * @NL80211_RATE_INFO_8_MHZ_WIDTH: 8 MHz S1G rate
+ * @NL80211_RATE_INFO_16_MHZ_WIDTH: 16 MHz S1G rate
  * @__NL80211_RATE_INFO_AFTER_LAST: internal use
  */
 enum nl80211_rate_info {
@@ -3693,6 +3700,13 @@  enum nl80211_rate_info {
 	NL80211_RATE_INFO_EHT_NSS,
 	NL80211_RATE_INFO_EHT_GI,
 	NL80211_RATE_INFO_EHT_RU_ALLOC,
+	NL80211_RATE_INFO_S1G_MCS,
+	NL80211_RATE_INFO_S1G_NSS,
+	NL80211_RATE_INFO_1_MHZ_WIDTH,
+	NL80211_RATE_INFO_2_MHZ_WIDTH,
+	NL80211_RATE_INFO_4_MHZ_WIDTH,
+	NL80211_RATE_INFO_8_MHZ_WIDTH,
+	NL80211_RATE_INFO_16_MHZ_WIDTH,
 
 	/* keep last */
 	__NL80211_RATE_INFO_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d95f8053020d..8fb8c8905b48 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6365,12 +6365,27 @@  bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
 		return false;
 
 	switch (info->bw) {
+	case RATE_INFO_BW_1:
+		rate_flg = NL80211_RATE_INFO_1_MHZ_WIDTH;
+		break;
+	case RATE_INFO_BW_2:
+		rate_flg = NL80211_RATE_INFO_2_MHZ_WIDTH;
+		break;
+	case RATE_INFO_BW_4:
+		rate_flg = NL80211_RATE_INFO_4_MHZ_WIDTH;
+		break;
 	case RATE_INFO_BW_5:
 		rate_flg = NL80211_RATE_INFO_5_MHZ_WIDTH;
 		break;
+	case RATE_INFO_BW_8:
+		rate_flg = NL80211_RATE_INFO_8_MHZ_WIDTH;
+		break;
 	case RATE_INFO_BW_10:
 		rate_flg = NL80211_RATE_INFO_10_MHZ_WIDTH;
 		break;
+	case RATE_INFO_BW_16:
+		rate_flg = NL80211_RATE_INFO_16_MHZ_WIDTH;
+		break;
 	default:
 		WARN_ON(1);
 		fallthrough;
@@ -6429,6 +6444,14 @@  bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
 		    nla_put_u8(msg, NL80211_RATE_INFO_HE_RU_ALLOC,
 			       info->he_ru_alloc))
 			return false;
+	} else if (info->flags & RATE_INFO_FLAGS_S1G_MCS) {
+		if (nla_put_u8(msg, NL80211_RATE_INFO_S1G_MCS, info->mcs))
+			return false;
+		if (nla_put_u8(msg, NL80211_RATE_INFO_S1G_NSS, info->nss))
+			return false;
+		if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+		    nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+			return false;
 	} else if (info->flags & RATE_INFO_FLAGS_EHT_MCS) {
 		if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_MCS, info->mcs))
 			return false;
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 3bc0c3072e78..9874a8469ef1 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1646,6 +1646,114 @@  static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
 	return result / 10000;
 }
 
+static u32 cfg80211_calculate_bitrate_s1g(struct rate_info *rate)
+{
+	/* For 1, 2, 4, 8 and 16 MHz channels */
+	static const u32 base[5][11] = {
+		{  300000,
+		   600000,
+		   900000,
+		  1200000,
+		  1800000,
+		  2400000,
+		  2700000,
+		  3000000,
+		  3600000,
+		  4000000,
+		  /* MCS 10 supported in 1 MHz only */
+		  150000,
+		},
+		{  650000,
+		  1300000,
+		  1950000,
+		  2600000,
+		  3900000,
+		  5200000,
+		  5850000,
+		  6500000,
+		  7800000,
+		  /* MCS 9 not valid */
+		},
+		{  1350000,
+		   2700000,
+		   4050000,
+		   5400000,
+		   8100000,
+		  10800000,
+		  12150000,
+		  13500000,
+		  16200000,
+		  18000000,
+		},
+		{  2925000,
+		   5850000,
+		   8775000,
+		  11700000,
+		  17550000,
+		  23400000,
+		  26325000,
+		  29250000,
+		  35100000,
+		  39000000,
+		},
+		{  8580000,
+		  11700000,
+		  17550000,
+		  23400000,
+		  35100000,
+		  46800000,
+		  52650000,
+		  58500000,
+		  70200000,
+		  78000000,
+		},
+	};
+	u32 bitrate;
+	/* default is 1 MHz index */
+	int idx = 0;
+
+	if (rate->mcs > 11)
+		goto warn;
+
+	switch (rate->bw) {
+	case RATE_INFO_BW_16:
+		idx = 4;
+		break;
+	case RATE_INFO_BW_8:
+		idx = 3;
+		break;
+	case RATE_INFO_BW_4:
+		idx = 2;
+		break;
+	case RATE_INFO_BW_2:
+		idx = 1;
+		break;
+	case RATE_INFO_BW_1:
+		idx = 0;
+		break;
+	case RATE_INFO_BW_5:
+	case RATE_INFO_BW_10:
+	case RATE_INFO_BW_20:
+	case RATE_INFO_BW_40:
+	case RATE_INFO_BW_80:
+	case RATE_INFO_BW_160:
+	default:
+		goto warn;
+	}
+
+	bitrate = base[idx][rate->mcs];
+	bitrate *= rate->nss;
+
+	if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+		bitrate = (bitrate / 9) * 10;
+	/* do NOT round down here */
+	return (bitrate + 50000) / 100000;
+warn:
+	WARN_ONCE(1, "invalid rate bw=%d, mcs=%d, nss=%d\n",
+		  rate->bw, rate->mcs, rate->nss);
+	return 0;
+}
+
 u32 cfg80211_calculate_bitrate(struct rate_info *rate)
 {
 	if (rate->flags & RATE_INFO_FLAGS_MCS)
@@ -1662,6 +1770,8 @@  u32 cfg80211_calculate_bitrate(struct rate_info *rate)
 		return cfg80211_calculate_bitrate_he(rate);
 	if (rate->flags & RATE_INFO_FLAGS_EHT_MCS)
 		return cfg80211_calculate_bitrate_eht(rate);
+	if (rate->flags & RATE_INFO_FLAGS_S1G_MCS)
+		return cfg80211_calculate_bitrate_s1g(rate);
 
 	return rate->legacy;
 }