[third_party/bcmdhd] Extract ratespec logic.

Broadcom firmware specifies the current rate parameters (e.g. channel
bandwidth, MCS index) in the form of a "ratespec" (also called "rspec").
This change extracts ratespec parsing symbols and logic into the
crossdriver source set, for ease of use in brcmfmac.

Test: manually tested with a variety of ratespec values from a running
firmware on a live device. Unit tests will be added into the Fuchsia
tree in a following CL, after this CL makes the symbols available to
brcmfmac.

Bug: 52811
Change-Id: I1ee13a2020d214510c56934fc210a17a4b578a20
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/bcmdhd/+/483877
Reviewed-by: Kiet Tran <kiettran@google.com>
diff --git a/README.fuchsia b/README.fuchsia
index d506b25..bbc9482 100644
--- a/README.fuchsia
+++ b/README.fuchsia
@@ -5,23 +5,26 @@
 License File: LICENSE
 Local Modifications:
 
-* fxr/401313
+* fxrev.dev/401313
   * Added Broadcom to AUTHORS
   * Added LICENSE
   * Added README.fuchsia
   * Added stub README.md with name and short description
-* fxr/401994
+* fxrev.dev/401994
   * Extracted cross-driver (bcmdhd/brcmfmac) symbols into crossdriver/dhd.h
   * Extracted cross-driver (bcmdhd/brcmfmac) symbols into crossdriver/bcmwifi_channels.h
   * Added crossdriver/BUILD.gn
-* fxr/427094
+* fxrev.dev/427094
   * Extracted additional cross-driver (bcmdhd/brcmfmac) symbols into crossdriver/bcmwifi_channels.cc
-* fxr/428360
+* fxrev.dev/428360
   * Extracted eventmsgs_ext structure and related definitions into crossdriver/wlioctl.h
-* fxr/469614
+* fxrev.dev/469614
   * Extracted WMM structs and definitions into crossdriver/include/proto/802.11.h
-* fxr/470565
+* fxrev.dev/470565
   * Add include/proto/802.11.h to source_set
+* fxrev.dev/483877
+  * Extracted ratespec parsing logic into crossdriver/dhd.h, crossdriver/dhd.cc, and
+    crossdriver/include/devctrl_if/wlioctl_defs.h
 
 Description:
 
diff --git a/crossdriver/BUILD.gn b/crossdriver/BUILD.gn
index 056031d..cd0b3cf 100644
--- a/crossdriver/BUILD.gn
+++ b/crossdriver/BUILD.gn
@@ -5,6 +5,7 @@
     "bcmwifi_channels.h",
     "dhd.cc",
     "dhd.h",
+    "include/devctrl_if/wlioctl_defs.h",
     "include/proto/802.11.h",
     "wl_cfg80211.cc",
     "wl_cfg80211.h",
@@ -14,6 +15,7 @@
     "bcmwifi_channels.h",
     "dhd.cc",
     "dhd.h",
+    "include/devctrl_if/wlioctl_defs.h",
     "include/proto/802.11.h",
     "wl_cfg80211.cc",
     "wl_cfg80211.h",
diff --git a/crossdriver/dhd.cc b/crossdriver/dhd.cc
index a7980b4..2c3a573 100644
--- a/crossdriver/dhd.cc
+++ b/crossdriver/dhd.cc
@@ -25,10 +25,104 @@
 
 #include "dhd.h"
 
+#include <stdint.h>
+
 #include <memory>
 
+#include "include/devctrl_if/wlioctl_defs.h"
 #include "wl_cfg80211.h"
 
+bool rspec_to_rate_info(uint32_t rspec, rate_info_t* ri) {
+  uint32_t encode;
+  uint32_t rate;
+
+  if (rspec == 0) {
+    return false;
+  }
+  encode = (rspec & WL_RSPEC_ENCODING_MASK);
+  rate = (rspec & WL_RSPEC_RATE_MASK);
+
+  ri->sgi = ((rspec & WL_RSPEC_SGI) != 0) ? 1 : 0;
+
+  switch (rspec & WL_RSPEC_BW_MASK) {
+    case WL_RSPEC_BW_20MHZ:
+      ri->bw = RATE_INFO_BW_20_MHZ;
+      break;
+    case WL_RSPEC_BW_40MHZ:
+      ri->bw = RATE_INFO_BW_40_MHZ;
+      break;
+    case WL_RSPEC_BW_80MHZ:
+      ri->bw = RATE_INFO_BW_80_MHZ;
+      break;
+    case WL_RSPEC_BW_160MHZ:
+    default:
+      ri->bw = RATE_INFO_BW_160_MHZ;
+      break;
+  }
+
+  if (encode == WL_RSPEC_ENCODE_RATE) {
+    ri->type = RATE_LEGACY;
+    switch (rate) {
+      case 2:
+        ri->idx.b = 0;
+        break;
+      case 4:
+        ri->idx.b = 1;
+        break;
+      case 11:
+        ri->idx.b = 2;
+        break;
+      case 22:
+        ri->idx.b = 3;
+        break;
+      case 12:
+        ri->idx.g = 0;
+        break;
+      case 18:
+        ri->idx.g = 1;
+        break;
+      case 24:
+        ri->idx.g = 2;
+        break;
+      case 36:
+        ri->idx.g = 3;
+        break;
+      case 48:
+        ri->idx.g = 4;
+        break;
+      case 72:
+        ri->idx.g = 5;
+        break;
+      case 96:
+        ri->idx.g = 6;
+        break;
+      case 104:
+      default:
+        ri->idx.g = 7;
+        break;
+    }
+  } else if (encode == WL_RSPEC_ENCODE_HT) {
+    ri->type = RATE_HT;
+    ri->idx.mcs = rate % 16;
+
+    if (rate <= 7) {
+      ri->nss = 1;
+    } else if (rate <= 15) {
+      ri->nss = 2;
+    } else if (rate <= 23) {
+      ri->nss = 3;
+    } else {
+      ri->nss = 4;
+    }
+  } else {
+    ri->type = RATE_VHT;
+    ri->idx.vhtmcs = (rspec & WL_RSPEC_VHT_MCS_MASK);
+    ri->nss = ((rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT);
+  }
+
+  return true;
+}
+
 bool get_histograms(wl_wstats_cnt_t wl_stats_cnt, chanspec_t chanspec, uint32_t version,
                     uint32_t rxchain, histograms_report_t* out_report) {
   if (wl_stats_cnt.version > WSTATS_CNT_T_VERSION) {
diff --git a/crossdriver/dhd.h b/crossdriver/dhd.h
index 0ad38f9..dd4010c 100644
--- a/crossdriver/dhd.h
+++ b/crossdriver/dhd.h
@@ -30,6 +30,29 @@
 
 #include "bcmwifi_channels.h"
 
+enum { RATE_LEGACY, RATE_HT, RATE_VHT };
+
+// rate_info_t bw field uses these values.
+enum {
+  RATE_INFO_BW_20_MHZ,
+  RATE_INFO_BW_40_MHZ,
+  RATE_INFO_BW_80_MHZ,
+  RATE_INFO_BW_160_MHZ,
+};
+
+using rate_info_t = struct {
+  uint8_t type;
+  union {
+    uint8_t b;
+    uint8_t g;
+    uint8_t mcs;
+    uint8_t vhtmcs;
+  } idx;
+  uint8_t nss;
+  uint8_t bw;
+  uint8_t sgi;
+};
+
 #define WSTATS_CNT_T_VERSION 1
 #define WSTATS_RATE_RANGE_11B 4
 #define WSTATS_RATE_RANGE_11G 8
@@ -97,6 +120,10 @@
 
 using histograms_report_t = histograms_report;
 
+// Decipher a ratespec. Returns false if ratespec == 0, true otherwise.
+// Ported from dhd_rspec_to_rate_info in dhd_linux.c.
+bool rspec_to_rate_info(uint32_t rspec, rate_info_t* ri);
+
 // Construct a histograms_report_t from the given arguments.
 // Ported from dhd_dev_get_histograms in dhd_linux.c.
 bool get_histograms(wl_wstats_cnt_t wl_stats_cnt, chanspec_t chanspec, uint32_t version,
diff --git a/crossdriver/include/devctrl_if/wlioctl_defs.h b/crossdriver/include/devctrl_if/wlioctl_defs.h
new file mode 100644
index 0000000..eea8533
--- /dev/null
+++ b/crossdriver/include/devctrl_if/wlioctl_defs.h
@@ -0,0 +1,61 @@
+/* Crossdriver (bcmdhd/brcmfmac) symbols extracted from bcmdhd include/devctrl_if/wlioctl_defs.h
+ *
+ * Copyright 1999-2016, Broadcom Corporation
+ * All rights reserved,
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *
+ * This software is provided by the copyright holder "as is" and any express or
+ * implied warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose are disclaimed. In no event
+ * shall copyright holder be liable for any direct, indirect, incidental, special,
+ * exemplary, or consequential damages (including, but not limited to, procurement
+ * of substitute goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether in
+ * contract, strict liability, or tort (including negligence or otherwise) arising
+ * in any way out of the use of this software, even if advised of the possibility
+ * of such damage
+ */
+
+#ifndef THIRD_PARTY_BCMDHD_CROSSDRIVER_INCLUDE_DEVCTRL_IF_WLIOCTL_DEFS_H_
+#define THIRD_PARTY_BCMDHD_CROSSDRIVER_INCLUDE_DEVCTRL_IF_WLIOCTL_DEFS_H_
+
+/* WL_RSPEC defines for rate information */
+#define WL_RSPEC_RATE_MASK 0x000000FF    /* rate or HT MCS value */
+#define WL_RSPEC_VHT_MCS_MASK 0x0000000F /* VHT MCS value */
+#define WL_RSPEC_VHT_NSS_MASK 0x000000F0 /* VHT Nss value */
+#define WL_RSPEC_VHT_NSS_SHIFT 4         /* VHT Nss value shift */
+#define WL_RSPEC_TXEXP_MASK 0x00000300
+#define WL_RSPEC_TXEXP_SHIFT 8
+#define WL_RSPEC_BW_MASK 0x00070000       /* bandwidth mask */
+#define WL_RSPEC_BW_SHIFT 16              /* bandwidth shift */
+#define WL_RSPEC_STBC 0x00100000          /* STBC encoding, Nsts = 2 x Nss */
+#define WL_RSPEC_TXBF 0x00200000          /* bit indicates TXBF mode */
+#define WL_RSPEC_LDPC 0x00400000          /* bit indicates adv coding in use */
+#define WL_RSPEC_SGI 0x00800000           /* Short GI mode */
+#define WL_RSPEC_ENCODING_MASK 0x03000000 /* Encoding of Rate/MCS field */
+#define WL_RSPEC_OVERRIDE_RATE 0x40000000 /* bit indicate to override mcs only */
+#define WL_RSPEC_OVERRIDE_MODE 0x80000000 /* bit indicates override both rate & mode */
+
+/* WL_RSPEC_ENCODING field defs */
+#define WL_RSPEC_ENCODE_RATE 0x00000000 /* Legacy rate is stored in RSPEC_RATE_MASK */
+#define WL_RSPEC_ENCODE_HT 0x01000000   /* HT MCS is stored in RSPEC_RATE_MASK */
+#define WL_RSPEC_ENCODE_VHT 0x02000000  /* VHT MCS and Nss is stored in RSPEC_RATE_MASK */
+
+/* WL_RSPEC_BW field defs */
+#define WL_RSPEC_BW_UNSPECIFIED 0
+#define WL_RSPEC_BW_20MHZ 0x00010000
+#define WL_RSPEC_BW_40MHZ 0x00020000
+#define WL_RSPEC_BW_80MHZ 0x00030000
+#define WL_RSPEC_BW_160MHZ 0x00040000
+#define WL_RSPEC_BW_10MHZ 0x00050000
+#define WL_RSPEC_BW_5MHZ 0x00060000
+#define WL_RSPEC_BW_2P5MHZ 0x00070000
+
+#endif  // THIRD_PARTY_BCMDHD_CROSSDRIVER_INCLUDE_DEVCTRL_IF_WLIOCTL_DEFS_H_