[third_party/bcmdhd] Expose logic for wstats.

Expose more constants, macros, and some logic for the proper handling of
the firmware iovar wstats_counters (per-antenna histograms).

Also there are two type fixes in bcmwifi_channels.h, where uint8 and
uint16 were accidentally used (instead of uint8_t and uint16_t).

Bug: 29698
Change-Id: I07ad8fd29d884e25afe943a039b64b7961196b71
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/bcmdhd/+/404954
Reviewed-by: Joshua Conner <joshconner@google.com>
diff --git a/crossdriver/BUILD.gn b/crossdriver/BUILD.gn
index a5cdf93..15714e0 100644
--- a/crossdriver/BUILD.gn
+++ b/crossdriver/BUILD.gn
@@ -1,11 +1,19 @@
 # Crossdriver (bcmdhd/brcmfmac) symbols extracted from bcmdhd.
-static_library("bcmdhd_crossdriver") {
+source_set("bcmdhd_crossdriver") {
   public = [
+    "bcmwifi_channels.cc",
     "bcmwifi_channels.h",
+    "dhd.cc",
     "dhd.h",
+    "wl_cfg80211.cc",
+    "wl_cfg80211.h",
   ]
   sources = [
+    "bcmwifi_channels.cc",
     "bcmwifi_channels.h",
+    "dhd.cc",
     "dhd.h",
+    "wl_cfg80211.cc",
+    "wl_cfg80211.h",
   ]
 }
diff --git a/crossdriver/bcmwifi_channels.cc b/crossdriver/bcmwifi_channels.cc
new file mode 100644
index 0000000..e85883e
--- /dev/null
+++ b/crossdriver/bcmwifi_channels.cc
@@ -0,0 +1,79 @@
+/* Crossdriver (bcmdhd/brcmfmac) logic extracted from bcmdhd bcmwifi_channels.c.
+ *
+ * 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
+ */
+
+#include "bcmwifi_channels.h"
+
+bool chspec_malformed(chanspec_t chanspec) {
+  uint16_t chspec_bw = CHSPEC_BW(chanspec);
+  uint16_t chspec_ch = CHSPEC_CHANNEL(chanspec);
+
+  /* must be 2G or 5G band */
+  if (CHSPEC_IS2G(chanspec)) {
+    /* must be valid bandwidth */
+    if (!BW_LE40(chspec_bw)) {
+      return true;
+    }
+  } else if (CHSPEC_IS5G(chanspec)) {
+    if (chspec_bw == WL_CHANSPEC_BW_8080) {
+      uint16_t ch1_id, ch2_id;
+
+      /* channel IDs in 80+80 must be in range */
+      ch1_id = CHSPEC_CHAN1(chanspec);
+      ch2_id = CHSPEC_CHAN2(chanspec);
+      if (ch1_id >= WF_NUM_5G_80M_CHANS || ch2_id >= WF_NUM_5G_80M_CHANS)
+        return true;
+
+    } else if (BW_LE160(chspec_bw)) {
+      if (chspec_ch > MAXCHANNEL) {
+        return true;
+      }
+    } else {
+      /* invalid bandwidth */
+      return true;
+    }
+  } else {
+    /* must be 2G or 5G band */
+    return true;
+  }
+
+  /* side band needs to be consistent with bandwidth */
+  if (BW_LE20(chspec_bw)) {
+    if (CHSPEC_CTL_SB(chanspec) != WL_CHANSPEC_CTL_SB_LLL)
+      return true;
+  } else if (chspec_bw == WL_CHANSPEC_BW_40) {
+    if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LLU)
+      return true;
+  } else if (chspec_bw == WL_CHANSPEC_BW_80 || chspec_bw == WL_CHANSPEC_BW_8080) {
+    if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_LUU)
+      return true;
+  } else if (chspec_bw == WL_CHANSPEC_BW_160) {
+    // This condition was originally an assertion, which is inverted here.
+    // Assertion was: ASSERT(CHSPEC_CTL_SB(chanspec) <= WL_CHANSPEC_CTL_SB_UUU);
+    if (CHSPEC_CTL_SB(chanspec) > WL_CHANSPEC_CTL_SB_UUU) {
+      return true;
+    }
+  }
+  return false;
+}
diff --git a/crossdriver/bcmwifi_channels.h b/crossdriver/bcmwifi_channels.h
index d46382c..cde38ce 100644
--- a/crossdriver/bcmwifi_channels.h
+++ b/crossdriver/bcmwifi_channels.h
@@ -28,21 +28,19 @@
 
 #include <stdint.h>
 
-// clang-format off
-
 /* A chanspec holds the channel number, band, bandwidth and control sideband */
 using chanspec_t = uint16_t;
 
 /* channel defines */
-#define CH_UPPER_SB                     0x01
-#define CH_LOWER_SB                     0x02
-#define CH_EWA_VALID                    0x04
-#define CH_80MHZ_APART                  16
-#define CH_40MHZ_APART                  8
-#define CH_20MHZ_APART                  4
-#define CH_10MHZ_APART                  2
-#define CH_5MHZ_APART                   1       /* 2G band channels are 5 Mhz apart */
-#define CH_MAX_2G_CHANNEL               14      /* Max channel in 2G band */
+#define CH_UPPER_SB 0x01
+#define CH_LOWER_SB 0x02
+#define CH_EWA_VALID 0x04
+#define CH_80MHZ_APART 16
+#define CH_40MHZ_APART 8
+#define CH_20MHZ_APART 4
+#define CH_10MHZ_APART 2
+#define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */
+#define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */
 
 /*
  * max # supported channels. The max channel no is 216, this is that + 1
@@ -51,142 +49,191 @@
  */
 #define MAXCHANNEL 224
 
-#define MAXCHANNEL_NUM  (MAXCHANNEL - 1)        /* max channel number */
+#define MAXCHANNEL_NUM (MAXCHANNEL - 1) /* max channel number */
 
-#define WL_CHANSPEC_CHAN_MASK           0x00ff
-#define WL_CHANSPEC_CHAN_SHIFT          0
-#define WL_CHANSPEC_CHAN1_MASK          0x000f
-#define WL_CHANSPEC_CHAN1_SHIFT         0
-#define WL_CHANSPEC_CHAN2_MASK          0x00f0
-#define WL_CHANSPEC_CHAN2_SHIFT         4
+#define WL_CHANSPEC_CHAN_MASK 0x00ff
+#define WL_CHANSPEC_CHAN_SHIFT 0
+#define WL_CHANSPEC_CHAN1_MASK 0x000f
+#define WL_CHANSPEC_CHAN1_SHIFT 0
+#define WL_CHANSPEC_CHAN2_MASK 0x00f0
+#define WL_CHANSPEC_CHAN2_SHIFT 4
 
-#define WL_CHANSPEC_CTL_SB_MASK         0x0700
-#define WL_CHANSPEC_CTL_SB_SHIFT        8
-#define WL_CHANSPEC_CTL_SB_LLL          0x0000
-#define WL_CHANSPEC_CTL_SB_LLU          0x0100
-#define WL_CHANSPEC_CTL_SB_LUL          0x0200
-#define WL_CHANSPEC_CTL_SB_LUU          0x0300
-#define WL_CHANSPEC_CTL_SB_ULL          0x0400
-#define WL_CHANSPEC_CTL_SB_ULU          0x0500
-#define WL_CHANSPEC_CTL_SB_UUL          0x0600
-#define WL_CHANSPEC_CTL_SB_UUU          0x0700
-#define WL_CHANSPEC_CTL_SB_LL           WL_CHANSPEC_CTL_SB_LLL
-#define WL_CHANSPEC_CTL_SB_LU           WL_CHANSPEC_CTL_SB_LLU
-#define WL_CHANSPEC_CTL_SB_UL           WL_CHANSPEC_CTL_SB_LUL
-#define WL_CHANSPEC_CTL_SB_UU           WL_CHANSPEC_CTL_SB_LUU
-#define WL_CHANSPEC_CTL_SB_L            WL_CHANSPEC_CTL_SB_LLL
-#define WL_CHANSPEC_CTL_SB_U            WL_CHANSPEC_CTL_SB_LLU
-#define WL_CHANSPEC_CTL_SB_LOWER        WL_CHANSPEC_CTL_SB_LLL
-#define WL_CHANSPEC_CTL_SB_UPPER        WL_CHANSPEC_CTL_SB_LLU
-#define WL_CHANSPEC_CTL_SB_NONE         WL_CHANSPEC_CTL_SB_LLL
+#define WL_CHANSPEC_CTL_SB_MASK 0x0700
+#define WL_CHANSPEC_CTL_SB_SHIFT 8
+#define WL_CHANSPEC_CTL_SB_LLL 0x0000
+#define WL_CHANSPEC_CTL_SB_LLU 0x0100
+#define WL_CHANSPEC_CTL_SB_LUL 0x0200
+#define WL_CHANSPEC_CTL_SB_LUU 0x0300
+#define WL_CHANSPEC_CTL_SB_ULL 0x0400
+#define WL_CHANSPEC_CTL_SB_ULU 0x0500
+#define WL_CHANSPEC_CTL_SB_UUL 0x0600
+#define WL_CHANSPEC_CTL_SB_UUU 0x0700
+#define WL_CHANSPEC_CTL_SB_LL WL_CHANSPEC_CTL_SB_LLL
+#define WL_CHANSPEC_CTL_SB_LU WL_CHANSPEC_CTL_SB_LLU
+#define WL_CHANSPEC_CTL_SB_UL WL_CHANSPEC_CTL_SB_LUL
+#define WL_CHANSPEC_CTL_SB_UU WL_CHANSPEC_CTL_SB_LUU
+#define WL_CHANSPEC_CTL_SB_L WL_CHANSPEC_CTL_SB_LLL
+#define WL_CHANSPEC_CTL_SB_U WL_CHANSPEC_CTL_SB_LLU
+#define WL_CHANSPEC_CTL_SB_LOWER WL_CHANSPEC_CTL_SB_LLL
+#define WL_CHANSPEC_CTL_SB_UPPER WL_CHANSPEC_CTL_SB_LLU
+#define WL_CHANSPEC_CTL_SB_NONE WL_CHANSPEC_CTL_SB_LLL
 
-#define WL_CHANSPEC_BW_MASK             0x3800
-#define WL_CHANSPEC_BW_SHIFT            11
-#define WL_CHANSPEC_BW_5                0x0000
-#define WL_CHANSPEC_BW_10               0x0800
-#define WL_CHANSPEC_BW_20               0x1000
-#define WL_CHANSPEC_BW_40               0x1800
-#define WL_CHANSPEC_BW_80               0x2000
-#define WL_CHANSPEC_BW_160              0x2800
-#define WL_CHANSPEC_BW_8080             0x3000
-#define WL_CHANSPEC_BW_2P5              0x3800
-#define WL_CHANSPEC_BAND_MASK           0xc000
-#define WL_CHANSPEC_BAND_SHIFT          14
-#define WL_CHANSPEC_BAND_2G             0x0000
-#define WL_CHANSPEC_BAND_3G             0x4000
-#define WL_CHANSPEC_BAND_4G             0x8000
-#define WL_CHANSPEC_BAND_5G             0xc000
-#define INVCHANSPEC                     255
-#define MAX_CHANSPEC                            0xFFFF
+#define WL_CHANSPEC_BW_MASK 0x3800
+#define WL_CHANSPEC_BW_SHIFT 11
+#define WL_CHANSPEC_BW_5 0x0000
+#define WL_CHANSPEC_BW_10 0x0800
+#define WL_CHANSPEC_BW_20 0x1000
+#define WL_CHANSPEC_BW_40 0x1800
+#define WL_CHANSPEC_BW_80 0x2000
+#define WL_CHANSPEC_BW_160 0x2800
+#define WL_CHANSPEC_BW_8080 0x3000
+#define WL_CHANSPEC_BW_2P5 0x3800
+#define WL_CHANSPEC_BAND_MASK 0xc000
+#define WL_CHANSPEC_BAND_SHIFT 14
+#define WL_CHANSPEC_BAND_2G 0x0000
+#define WL_CHANSPEC_BAND_3G 0x4000
+#define WL_CHANSPEC_BAND_4G 0x8000
+#define WL_CHANSPEC_BAND_5G 0xc000
+#define INVCHANSPEC 255
+#define MAX_CHANSPEC 0xFFFF
 
 /* channel defines */
-#define LOWER_20_SB(channel)            (((channel) > CH_10MHZ_APART) ? \
-                                        ((channel) - CH_10MHZ_APART) : 0)
-#define UPPER_20_SB(channel)            (((channel) < (MAXCHANNEL - CH_10MHZ_APART)) ? \
-                                        ((channel) + CH_10MHZ_APART) : 0)
+#define LOWER_20_SB(channel) (((channel) > CH_10MHZ_APART) ? ((channel)-CH_10MHZ_APART) : 0)
+#define UPPER_20_SB(channel) \
+  (((channel) < (MAXCHANNEL - CH_10MHZ_APART)) ? ((channel) + CH_10MHZ_APART) : 0)
 
-#define LL_20_SB(channel) (((channel) > 3 * CH_10MHZ_APART) ? ((channel) - 3 * CH_10MHZ_APART) : 0)
-#define UU_20_SB(channel)       (((channel) < (MAXCHANNEL - 3 * CH_10MHZ_APART)) ? \
-                                ((channel) + 3 * CH_10MHZ_APART) : 0)
+#define LL_20_SB(channel) (((channel) > 3 * CH_10MHZ_APART) ? ((channel)-3 * CH_10MHZ_APART) : 0)
+#define UU_20_SB(channel) \
+  (((channel) < (MAXCHANNEL - 3 * CH_10MHZ_APART)) ? ((channel) + 3 * CH_10MHZ_APART) : 0)
 #define LU_20_SB(channel) LOWER_20_SB(channel)
 #define UL_20_SB(channel) UPPER_20_SB(channel)
 
-#define LOWER_40_SB(channel)            ((channel) - CH_20MHZ_APART)
-#define UPPER_40_SB(channel)            ((channel) + CH_20MHZ_APART)
-#define CHSPEC_WLCBANDUNIT(chspec)      (CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX)
-#define CH20MHZ_CHSPEC(channel)         (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \
-                                        (((channel) <= CH_MAX_2G_CHANNEL) ? \
-                                        WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
-#define CH2P5MHZ_CHSPEC(channel)        (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_2P5 | \
-                                                (((channel) <= CH_MAX_2G_CHANNEL) ? \
-                                                WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
-#define CH5MHZ_CHSPEC(channel)          (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_5 | \
-                                                (((channel) <= CH_MAX_2G_CHANNEL) ? \
-                                                WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
-#define CH10MHZ_CHSPEC(channel)         (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_10 | \
-                                                (((channel) <= CH_MAX_2G_CHANNEL) ? \
-                                                WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
-#define NEXT_20MHZ_CHAN(channel)        (((channel) < (MAXCHANNEL - CH_20MHZ_APART)) ? \
-                                        ((channel) + CH_20MHZ_APART) : 0)
-#define CH40MHZ_CHSPEC(channel, ctlsb)  (chanspec_t) \
-                                        ((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \
-                                        ((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \
-                                        WL_CHANSPEC_BAND_5G))
-#define CH80MHZ_CHSPEC(channel, ctlsb)  (chanspec_t) \
-                                        ((channel) | (ctlsb) | \
-                                         WL_CHANSPEC_BW_80 | WL_CHANSPEC_BAND_5G)
-#define CH160MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \
-                                        ((channel) | (ctlsb) | \
-                                         WL_CHANSPEC_BW_160 | WL_CHANSPEC_BAND_5G)
-#define CHBW_CHSPEC(bw, channel)        (chanspec_t)((chanspec_t)(channel) | (bw) | \
-                                                        (((channel) <= CH_MAX_2G_CHANNEL) ? \
-                                                        WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define LOWER_40_SB(channel) ((channel)-CH_20MHZ_APART)
+#define UPPER_40_SB(channel) ((channel) + CH_20MHZ_APART)
+#define CHSPEC_WLCBANDUNIT(chspec) (CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX)
+#define CH20MHZ_CHSPEC(channel)                            \
+  (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \
+               (((channel) <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define CH2P5MHZ_CHSPEC(channel)                            \
+  (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_2P5 | \
+               (((channel) <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define CH5MHZ_CHSPEC(channel)                            \
+  (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_5 | \
+               (((channel) <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define CH10MHZ_CHSPEC(channel)                            \
+  (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_10 | \
+               (((channel) <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define NEXT_20MHZ_CHAN(channel) \
+  (((channel) < (MAXCHANNEL - CH_20MHZ_APART)) ? ((channel) + CH_20MHZ_APART) : 0)
+#define CH40MHZ_CHSPEC(channel, ctlsb)                   \
+  (chanspec_t)((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \
+               ((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define CH80MHZ_CHSPEC(channel, ctlsb) \
+  (chanspec_t)((channel) | (ctlsb) | WL_CHANSPEC_BW_80 | WL_CHANSPEC_BAND_5G)
+#define CH160MHZ_CHSPEC(channel, ctlsb) \
+  (chanspec_t)((channel) | (ctlsb) | WL_CHANSPEC_BW_160 | WL_CHANSPEC_BAND_5G)
+#define CHBW_CHSPEC(bw, channel)              \
+  (chanspec_t)((chanspec_t)(channel) | (bw) | \
+               (((channel) <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+
+/* simple MACROs to get different fields of chanspec */
+// TODO(karlward) What about WL11AC_80P80 version of CHSPEC_CHANNEL?
+#define CHSPEC_CHANNEL(chspec) ((uint8_t)((chspec)&WL_CHANSPEC_CHAN_MASK))
+#define CHSPEC_CHAN1(chspec) ((chspec)&WL_CHANSPEC_CHAN1_MASK) >> WL_CHANSPEC_CHAN1_SHIFT
+#define CHSPEC_CHAN2(chspec) ((chspec)&WL_CHANSPEC_CHAN2_MASK) >> WL_CHANSPEC_CHAN2_SHIFT
+#define CHSPEC_BAND(chspec) ((chspec)&WL_CHANSPEC_BAND_MASK)
+#define CHSPEC_CTL_SB(chspec) ((chspec)&WL_CHANSPEC_CTL_SB_MASK)
+#define CHSPEC_BW(chspec) ((chspec)&WL_CHANSPEC_BW_MASK)
+
+// TODO(karlward) What about WL11N_20MHZONLY version of these?
+#define CHSPEC_IS2P5(chspec) (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_2P5)
+#define CHSPEC_IS5(chspec) (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_5)
+#define CHSPEC_IS10(chspec) (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10)
+#define CHSPEC_IS20(chspec) (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
+#define CHSPEC_IS40(chspec) (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
+#define CHSPEC_IS80(chspec) (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_80)
+#define CHSPEC_IS160(chspec) (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_160)
+#define CHSPEC_IS8080(chspec) (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_8080)
+
+// TODO(karlward) What about WL11N_20MHZONLY and WL11ULB versions of this?
+/* This MACRO is strictly to avoid abandons in existing code with ULB feature and is in no way
+ * optimial to use. Should be replaced with CHSPEC_BW_LE() instead
+ */
+#define BW_LE20(bw)                                                                             \
+  (((bw) == WL_CHANSPEC_BW_2P5) || ((bw) == WL_CHANSPEC_BW_5) || ((bw) == WL_CHANSPEC_BW_10) || \
+   ((bw) == WL_CHANSPEC_BW_20))
+
+#define BW_LE40(bw) (BW_LE20(bw) || ((bw) == WL_CHANSPEC_BW_40))
+#define BW_LE80(bw) (BW_LE40(bw) || ((bw) == WL_CHANSPEC_BW_80))
+#define BW_LE160(bw) (BW_LE80(bw) || ((bw) == WL_CHANSPEC_BW_160))
+
+#define CHSPEC_IS5G(chspec) (((chspec)&WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G)
+#define CHSPEC_IS2G(chspec) (((chspec)&WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G)
+#define CHSPEC_SB_UPPER(chspec)                                        \
+  ((((chspec)&WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) && \
+   (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40))
+#define CHSPEC_SB_LOWER(chspec)                                        \
+  ((((chspec)&WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) && \
+   (((chspec)&WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40))
 
 /* Legacy Chanspec defines
  * These are the defines for the previous format of the chanspec_t
  */
-#define WL_LCHANSPEC_CHAN_MASK          0x00ff
-#define WL_LCHANSPEC_CHAN_SHIFT              0
+#define WL_LCHANSPEC_CHAN_MASK 0x00ff
+#define WL_LCHANSPEC_CHAN_SHIFT 0
 
-#define WL_LCHANSPEC_CTL_SB_MASK        0x0300
-#define WL_LCHANSPEC_CTL_SB_SHIFT            8
-#define WL_LCHANSPEC_CTL_SB_LOWER       0x0100
-#define WL_LCHANSPEC_CTL_SB_UPPER       0x0200
-#define WL_LCHANSPEC_CTL_SB_NONE        0x0300
+#define WL_LCHANSPEC_CTL_SB_MASK 0x0300
+#define WL_LCHANSPEC_CTL_SB_SHIFT 8
+#define WL_LCHANSPEC_CTL_SB_LOWER 0x0100
+#define WL_LCHANSPEC_CTL_SB_UPPER 0x0200
+#define WL_LCHANSPEC_CTL_SB_NONE 0x0300
 
-#define WL_LCHANSPEC_BW_MASK            0x0C00
-#define WL_LCHANSPEC_BW_SHIFT               10
-#define WL_LCHANSPEC_BW_10              0x0400
-#define WL_LCHANSPEC_BW_20              0x0800
-#define WL_LCHANSPEC_BW_40              0x0C00
+#define WL_LCHANSPEC_BW_MASK 0x0C00
+#define WL_LCHANSPEC_BW_SHIFT 10
+#define WL_LCHANSPEC_BW_10 0x0400
+#define WL_LCHANSPEC_BW_20 0x0800
+#define WL_LCHANSPEC_BW_40 0x0C00
 
-#define WL_LCHANSPEC_BAND_MASK          0xf000
-#define WL_LCHANSPEC_BAND_SHIFT             12
-#define WL_LCHANSPEC_BAND_5G            0x1000
-#define WL_LCHANSPEC_BAND_2G            0x2000
+#define WL_LCHANSPEC_BAND_MASK 0xf000
+#define WL_LCHANSPEC_BAND_SHIFT 12
+#define WL_LCHANSPEC_BAND_5G 0x1000
+#define WL_LCHANSPEC_BAND_2G 0x2000
 
-#define LCHSPEC_CHANNEL(chspec) ((uint8)((chspec) & WL_LCHANSPEC_CHAN_MASK))
-#define LCHSPEC_BAND(chspec)    ((chspec) & WL_LCHANSPEC_BAND_MASK)
-#define LCHSPEC_CTL_SB(chspec)  ((chspec) & WL_LCHANSPEC_CTL_SB_MASK)
-#define LCHSPEC_BW(chspec)      ((chspec) & WL_LCHANSPEC_BW_MASK)
-#define LCHSPEC_IS10(chspec)    (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_10)
-#define LCHSPEC_IS20(chspec)    (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_20)
-#define LCHSPEC_IS40(chspec)    (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_40)
-#define LCHSPEC_IS5G(chspec)    (((chspec) & WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_5G)
-#define LCHSPEC_IS2G(chspec)    (((chspec) & WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_2G)
+#define LCHSPEC_CHANNEL(chspec) ((uint8_t)((chspec)&WL_LCHANSPEC_CHAN_MASK))
+#define LCHSPEC_BAND(chspec) ((chspec)&WL_LCHANSPEC_BAND_MASK)
+#define LCHSPEC_CTL_SB(chspec) ((chspec)&WL_LCHANSPEC_CTL_SB_MASK)
+#define LCHSPEC_BW(chspec) ((chspec)&WL_LCHANSPEC_BW_MASK)
+#define LCHSPEC_IS10(chspec) (((chspec)&WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_10)
+#define LCHSPEC_IS20(chspec) (((chspec)&WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_20)
+#define LCHSPEC_IS40(chspec) (((chspec)&WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_40)
+#define LCHSPEC_IS5G(chspec) (((chspec)&WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_5G)
+#define LCHSPEC_IS2G(chspec) (((chspec)&WL_LCHANSPEC_BAND_MASK) == WL_LCHANSPEC_BAND_2G)
 
-#define LCHSPEC_SB_UPPER(chspec)        \
-        ((((chspec) & WL_LCHANSPEC_CTL_SB_MASK) == WL_LCHANSPEC_CTL_SB_UPPER) && \
-        (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_40))
-#define LCHSPEC_SB_LOWER(chspec)        \
-        ((((chspec) & WL_LCHANSPEC_CTL_SB_MASK) == WL_LCHANSPEC_CTL_SB_LOWER) && \
-        (((chspec) & WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_40))
+#define LCHSPEC_SB_UPPER(chspec)                                         \
+  ((((chspec)&WL_LCHANSPEC_CTL_SB_MASK) == WL_LCHANSPEC_CTL_SB_UPPER) && \
+   (((chspec)&WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_40))
+#define LCHSPEC_SB_LOWER(chspec)                                         \
+  ((((chspec)&WL_LCHANSPEC_CTL_SB_MASK) == WL_LCHANSPEC_CTL_SB_LOWER) && \
+   (((chspec)&WL_LCHANSPEC_BW_MASK) == WL_LCHANSPEC_BW_40))
 
-#define LCHSPEC_CREATE(chan, band, bw, sb)  ((uint16)((chan) | (sb) | (bw) | (band)))
+#define LCHSPEC_CREATE(chan, band, bw, sb) ((uint16_t)((chan) | (sb) | (bw) | (band)))
 
-#define CH20MHZ_LCHSPEC(channel) \
-        (chanspec_t)((chanspec_t)(channel) | WL_LCHANSPEC_BW_20 | \
-        WL_LCHANSPEC_CTL_SB_NONE | (((channel) <= CH_MAX_2G_CHANNEL) ? \
-        WL_LCHANSPEC_BAND_2G : WL_LCHANSPEC_BAND_5G))
+#define CH20MHZ_LCHSPEC(channel)                                                       \
+  (chanspec_t)((chanspec_t)(channel) | WL_LCHANSPEC_BW_20 | WL_LCHANSPEC_CTL_SB_NONE | \
+               (((channel) <= CH_MAX_2G_CHANNEL) ? WL_LCHANSPEC_BAND_2G : WL_LCHANSPEC_BAND_5G))
+
+/* 80MHz channels in 5GHz band */
+static const uint8_t wf_5g_80m_chans[] = {42, 58, 106, 122, 138, 155};
+#define WF_NUM_5G_80M_CHANS (sizeof(wf_5g_80m_chans) / sizeof(uint8_t))
+
+/*
+ * Verify the chanspec is using a legal set of parameters, i.e. that the
+ * chanspec specified a band, bw, ctl_sb and channel and that the
+ * combination could be legal given any set of circumstances.
+ * Returns true if chanspec is malformed, false if it looks good.
+ *
+ * Ported from wf_chspec_malformed.
+ */
+bool chspec_malformed(chanspec_t chanspec);
 
 #endif  // THIRD_PARTY_BCMDHD_CROSSDRIVER_BCMWIFI_CHANNELS_H_
diff --git a/crossdriver/dhd.cc b/crossdriver/dhd.cc
new file mode 100644
index 0000000..a7980b4
--- /dev/null
+++ b/crossdriver/dhd.cc
@@ -0,0 +1,59 @@
+/* Crossdriver (bcmdhd/brcmfmac) logic extracted from bcmdhd dhd.h externs.
+ *
+ * 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
+ */
+
+#include "dhd.h"
+
+#include <memory>
+
+#include "wl_cfg80211.h"
+
+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) {
+    // Unexpected wl_stats_cnt version.
+    return false;
+  }
+
+  const chanspec_t real_chanspec = interpret_chanspec(chanspec, version);
+  if (real_chanspec == INVCHANSPEC) {
+    return false;
+  }
+
+  int band = chanspec & WL_CHANSPEC_BAND_MASK;
+  AntennaId antenna_id;
+  antenna_id.freq = (band == WL_CHANSPEC_BAND_5G) ? ANTENNA_5G : ANTENNA_2G;
+  antenna_id.idx = (rxchain == 1) ? 0 : 1;
+
+  std::memset(out_report, 0, sizeof(histograms_report_t));
+  std::memcpy(out_report->rxsnr, wl_stats_cnt.rxsnr, sizeof(out_report->rxsnr));
+  std::memcpy(out_report->rxnoiseflr, wl_stats_cnt.rxnoiseflr, sizeof(out_report->rxnoiseflr));
+  std::memcpy(out_report->rxrssi, wl_stats_cnt.rxrssi, sizeof(out_report->rxrssi));
+  std::memcpy(out_report->rx11b, wl_stats_cnt.rx11b, sizeof(out_report->rx11b));
+  std::memcpy(out_report->rx11g, wl_stats_cnt.rx11g, sizeof(out_report->rx11g));
+  std::memcpy(out_report->rx11n, wl_stats_cnt.rx11n, sizeof(out_report->rx11n));
+  std::memcpy(out_report->rx11ac, wl_stats_cnt.rx11ac, sizeof(out_report->rx11ac));
+  std::memcpy(&out_report->antennaid, &antenna_id, sizeof(out_report->antennaid));
+  return true;
+}
diff --git a/crossdriver/dhd.h b/crossdriver/dhd.h
index 057dae3..0ad38f9 100644
--- a/crossdriver/dhd.h
+++ b/crossdriver/dhd.h
@@ -26,79 +26,80 @@
 #ifndef THIRD_PARTY_BCMDHD_CROSSDRIVER_DHD_H_
 #define THIRD_PARTY_BCMDHD_CROSSDRIVER_DHD_H_
 
-// clang-format off
-
 #include <stdint.h>
 
-#define WSTATS_CNT_T_VERSION    1
-#define WSTATS_RATE_RANGE_11B   4
-#define WSTATS_RATE_RANGE_11G   8
-#define WSTATS_SGI_RANGE        2
-#define WSTATS_BW_RANGE_11N     2
-#define WSTATS_MCS_RANGE_11N    16
-#define WSTATS_BW_RANGE_11AC    3
-#define WSTATS_NSS_RANGE        2
-#define WSTATS_MCS_RANGE_11AC   10
-#define WSTATS_SNR_RANGE        256
-#define WSTATS_NOISE_FLR_RANGE  256
-#define WSTATS_RSSI_RANGE       256
+#include "bcmwifi_channels.h"
+
+#define WSTATS_CNT_T_VERSION 1
+#define WSTATS_RATE_RANGE_11B 4
+#define WSTATS_RATE_RANGE_11G 8
+#define WSTATS_SGI_RANGE 2
+#define WSTATS_BW_RANGE_11N 2
+#define WSTATS_MCS_RANGE_11N 16
+#define WSTATS_BW_RANGE_11AC 3
+#define WSTATS_NSS_RANGE 2
+#define WSTATS_MCS_RANGE_11AC 10
+#define WSTATS_SNR_RANGE 256
+#define WSTATS_NOISE_FLR_RANGE 256
+#define WSTATS_RSSI_RANGE 256
 
 struct wl_wstats_cnt {
-	uint16_t  version;
-	uint16_t  length;
+  uint16_t version;
+  uint16_t length;
 
-	/* pkt counters per snr */
-	uint32_t rxsnr[WSTATS_SNR_RANGE];
-	/* pkt counters per noise floor */
-	uint32_t rxnoiseflr[WSTATS_NOISE_FLR_RANGE];
-	/* pkt counters per signal level */
-	uint32_t rxrssi[WSTATS_RSSI_RANGE];
-	/* [RATE 1,2,55,11] */
-	uint32_t rx11b[WSTATS_RATE_RANGE_11B];
-	/* [RATE 6,9,12,18,24,36,48,54] */
-	uint32_t rx11g[WSTATS_RATE_RANGE_11G];
-	/* [SGI off/on][BW 20/40][MCS 0-15] */
-	uint32_t rx11n[WSTATS_SGI_RANGE]
-		[WSTATS_BW_RANGE_11N][WSTATS_MCS_RANGE_11N];
-	/* [NSS 1/2][SGI off/on][BW 20/40/80][MCS 0-9] */
-	uint32_t rx11ac[WSTATS_NSS_RANGE][WSTATS_SGI_RANGE]
-		[WSTATS_BW_RANGE_11AC][WSTATS_MCS_RANGE_11AC];
+  /* pkt counters per snr */
+  uint32_t rxsnr[WSTATS_SNR_RANGE];
+  /* pkt counters per noise floor */
+  uint32_t rxnoiseflr[WSTATS_NOISE_FLR_RANGE];
+  /* pkt counters per signal level */
+  uint32_t rxrssi[WSTATS_RSSI_RANGE];
+  /* [RATE 1,2,55,11] */
+  uint32_t rx11b[WSTATS_RATE_RANGE_11B];
+  /* [RATE 6,9,12,18,24,36,48,54] */
+  uint32_t rx11g[WSTATS_RATE_RANGE_11G];
+  /* [SGI off/on][BW 20/40][MCS 0-15] */
+  uint32_t rx11n[WSTATS_SGI_RANGE][WSTATS_BW_RANGE_11N][WSTATS_MCS_RANGE_11N];
+  /* [NSS 1/2][SGI off/on][BW 20/40/80][MCS 0-9] */
+  uint32_t rx11ac[WSTATS_NSS_RANGE][WSTATS_SGI_RANGE][WSTATS_BW_RANGE_11AC][WSTATS_MCS_RANGE_11AC];
 };
 
 using wl_wstats_cnt_t = wl_wstats_cnt;
 
 enum AntennaFreq {
-	ANTENNA_2G,
-	ANTENNA_5G,
-	NUM_ANTENNA_FREQ,
+  ANTENNA_2G,
+  ANTENNA_5G,
+  NUM_ANTENNA_FREQ,
 };
 
 struct AntennaId {
-	int freq;
-	int idx;
+  int freq;
+  int idx;
 };
 
 struct histograms_report {
-	struct AntennaId antennaid;
+  struct AntennaId antennaid;
 
-	/* pkt counters per snr */
-	uint32_t rxsnr[WSTATS_SNR_RANGE];
-	/* pkt counters per noise floor */
-	uint32_t rxnoiseflr[WSTATS_NOISE_FLR_RANGE];
-	/* pkt counters per signal level */
-	uint32_t rxrssi[WSTATS_RSSI_RANGE];
-	/* [RATE 1,2,55,11]: 0-3 */
-	uint32_t rx11b[WSTATS_RATE_RANGE_11B];
-	/* [RATE 6,9,12,18,24,36,48,54]: 4-11 */
-	uint32_t rx11g[WSTATS_RATE_RANGE_11G];
-	/* [SGI off/on][BW 20/40][MCS 0-15]: 12-75 */
-	uint32_t rx11n[WSTATS_SGI_RANGE]
-		[WSTATS_BW_RANGE_11N][WSTATS_MCS_RANGE_11N];
-	/* [NSS 1/2][SGI off/on][BW 20/40/80][MCS 0-9]: */
-	uint32_t rx11ac[WSTATS_NSS_RANGE][WSTATS_SGI_RANGE]
-		[WSTATS_BW_RANGE_11AC][WSTATS_MCS_RANGE_11AC];
+  /* pkt counters per snr */
+  uint32_t rxsnr[WSTATS_SNR_RANGE];
+  /* pkt counters per noise floor */
+  uint32_t rxnoiseflr[WSTATS_NOISE_FLR_RANGE];
+  /* pkt counters per signal level */
+  uint32_t rxrssi[WSTATS_RSSI_RANGE];
+  /* [RATE 1,2,55,11]: 0-3 */
+  uint32_t rx11b[WSTATS_RATE_RANGE_11B];
+  /* [RATE 6,9,12,18,24,36,48,54]: 4-11 */
+  uint32_t rx11g[WSTATS_RATE_RANGE_11G];
+  /* [SGI off/on][BW 20/40][MCS 0-15]: 12-75 */
+  uint32_t rx11n[WSTATS_SGI_RANGE][WSTATS_BW_RANGE_11N][WSTATS_MCS_RANGE_11N];
+  /* [NSS 1/2][SGI off/on][BW 20/40/80][MCS 0-9]: */
+  uint32_t rx11ac[WSTATS_NSS_RANGE][WSTATS_SGI_RANGE][WSTATS_BW_RANGE_11AC][WSTATS_MCS_RANGE_11AC];
 };
 
 using histograms_report_t = histograms_report;
 
+// 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,
+                    uint32_t rxchain, histograms_report_t* out_report);
+
 #endif  // THIRD_PARTY_BCMDHD_CROSSDRIVER_DHD_H_
diff --git a/crossdriver/wl_cfg80211.cc b/crossdriver/wl_cfg80211.cc
new file mode 100644
index 0000000..08f2c93
--- /dev/null
+++ b/crossdriver/wl_cfg80211.cc
@@ -0,0 +1,65 @@
+/* Crossdriver (bcmdhd/brcmfmac) logic extracted from bcmdhd wl_cfg80211.c.
+ *
+ * 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
+ */
+
+#include "wl_cfg80211.h"
+
+#include "bcmwifi_channels.h"
+
+chanspec_t chspec_from_legacy(chanspec_t legacy_chspec) {
+  chanspec_t chspec;
+  /* get the channel number */
+  chspec = LCHSPEC_CHANNEL(legacy_chspec);
+
+  /* convert the band */
+  if (LCHSPEC_IS2G(legacy_chspec)) {
+    chspec |= WL_CHANSPEC_BAND_2G;
+  } else {
+    chspec |= WL_CHANSPEC_BAND_5G;
+  }
+
+  /* convert the bw and sideband */
+  if (LCHSPEC_IS20(legacy_chspec)) {
+    chspec |= WL_CHANSPEC_BW_20;
+  } else {
+    chspec |= WL_CHANSPEC_BW_40;
+    if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) {
+      chspec |= WL_CHANSPEC_CTL_SB_L;
+    } else {
+      chspec |= WL_CHANSPEC_CTL_SB_U;
+    }
+  }
+
+  if (chspec_malformed(chspec)) {
+    return INVCHANSPEC;
+  }
+  return chspec;
+}
+
+chanspec_t interpret_chanspec(chanspec_t chanspec, uint32_t version) {
+  if (version == 1) {
+    return chspec_from_legacy(chanspec);
+  }
+  return chanspec;
+}
diff --git a/crossdriver/wl_cfg80211.h b/crossdriver/wl_cfg80211.h
new file mode 100644
index 0000000..c14c1bf
--- /dev/null
+++ b/crossdriver/wl_cfg80211.h
@@ -0,0 +1,39 @@
+/* Crossdriver (bcmdhd/brcmfmac) symbols extracted from bcmdhd wl_cfg80211.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_WL_CFG80211_H_
+#define THIRD_PARTY_BCMDHD_CROSSDRIVER_WL_CFG80211_H_
+
+#include "bcmwifi_channels.h"
+
+// Return a new chanspec given a legacy chanspec.
+// Ported from wl_chspec_from_legacy.
+chanspec_t chspec_from_legacy(chanspec_t legacy_chspec);
+
+// Convert firmware chanspec from legacy chanspec if necessary.
+// Ported from wl_chspec_driver_to_host.
+chanspec_t interpret_chanspec(chanspec_t chanspec, uint32_t version);
+
+#endif  // THIRD_PARTY_BCMDHD_CROSSDRIVER_WL_CFG80211_H_