[third_party/bcmdhd] Include additional channel code.

Bug: 59186

Including functions that are related
to determining control channel of a given chanspec.

Change-Id: I7e1b787c41376c9ac6bb9d642f4a5bef8506ba64
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/bcmdhd/+/427094
Reviewed-by: Karl Ward <karlward@google.com>
diff --git a/README.fuchsia b/README.fuchsia
index e9c120c..ccf2cbd 100644
--- a/README.fuchsia
+++ b/README.fuchsia
@@ -14,6 +14,8 @@
   * 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
+  * Extracted additional cross-driver (bcmdhd/brcmfmac) symbols into crossdriver/bcmwifi_channels.cc
 
 Description:
 
diff --git a/crossdriver/bcmwifi_channels.cc b/crossdriver/bcmwifi_channels.cc
index e85883e..742d629 100644
--- a/crossdriver/bcmwifi_channels.cc
+++ b/crossdriver/bcmwifi_channels.cc
@@ -25,6 +25,13 @@
 
 #include "bcmwifi_channels.h"
 
+#include <zircon/errors.h>
+#include <zircon/status.h>
+
+static const uint8_t chspec_bw_mhz[] = {5, 10, 20, 40, 80, 160, 160};
+
+#define WF_NUM_BW (sizeof(chspec_bw_mhz) / sizeof(uint8_t))
+
 bool chspec_malformed(chanspec_t chanspec) {
   uint16_t chspec_bw = CHSPEC_BW(chanspec);
   uint16_t chspec_ch = CHSPEC_CHANNEL(chanspec);
@@ -77,3 +84,63 @@
   }
   return false;
 }
+
+// bw in MHz, return the channel count from the center channel to the the channel at the edge of the
+// band
+static uint8_t center_chan_to_edge(uint32_t bw) {
+  // edge channels separated by BW - 10MHz on each side delta from cf to edge is half of that, MHz
+  // to channel num conversion is 5MHz/channel
+  return (uint8_t)(((bw - 20) / 2) / 5);
+}
+
+// return channel number of the low edge of the band given the center channel and BW
+static uint8_t channel_low_edge(uint32_t center_ch, uint32_t bw) {
+  return (uint8_t)(center_ch - center_chan_to_edge(bw));
+}
+
+// return control channel given center channel and side band
+static uint8_t channel_to_ctl_chan(uint32_t center_ch, uint32_t bw, uint32_t sb) {
+  return (uint8_t)(channel_low_edge(center_ch, bw) + sb * 4);
+}
+
+// convert bandwidth from chanspec to MHz
+static uint32_t bw_chspec_to_mhz(chanspec_t chspec) {
+  uint32_t bw;
+
+  bw = (chspec & WL_CHANSPEC_BW_MASK) >> WL_CHANSPEC_BW_SHIFT;
+  return (bw >= WF_NUM_BW ? 0 : chspec_bw_mhz[bw]);
+}
+
+// Return the control channel of the given chanspec.
+zx_status_t chspec_ctlchan(chanspec_t chspec, uint8_t* ctrl_chan) {
+  uint32_t center_chan;
+  uint32_t bw_mhz;
+  uint32_t sb;
+
+  if (chspec_malformed(chspec) || ctrl_chan == nullptr)
+    return ZX_ERR_INVALID_ARGS;
+
+  // Is there a sideband ?
+  if (CHSPEC_BW_LE20(chspec)) {
+    *ctrl_chan = CHSPEC_CHANNEL(chspec);
+  } else {
+    sb = CHSPEC_CTL_SB(chspec) >> WL_CHANSPEC_CTL_SB_SHIFT;
+
+    if (CHSPEC_IS8080(chspec)) {
+      // For an 80+80 MHz channel, the sideband 'sb' field is an 80 MHz sideband (LL, LU, UL, LU)
+      // for the 80 MHz frequency segment 0.
+      uint32_t chan_id = CHSPEC_CHAN1(chspec);
+
+      bw_mhz = 80;
+
+      // convert from channel index to channel number
+      center_chan = wf_5g_80m_chans[chan_id];
+    } else {
+      bw_mhz = bw_chspec_to_mhz(chspec);
+      center_chan = CHSPEC_CHANNEL(chspec) >> WL_CHANSPEC_CHAN_SHIFT;
+    }
+
+    *ctrl_chan = channel_to_ctl_chan(center_chan, bw_mhz, sb);
+  }
+  return ZX_OK;
+}
diff --git a/crossdriver/bcmwifi_channels.h b/crossdriver/bcmwifi_channels.h
index cde38ce..21f0e2a 100644
--- a/crossdriver/bcmwifi_channels.h
+++ b/crossdriver/bcmwifi_channels.h
@@ -27,6 +27,7 @@
 #define THIRD_PARTY_BCMDHD_CROSSDRIVER_BCMWIFI_CHANNELS_H_
 
 #include <stdint.h>
+#include <zircon/status.h>
 
 /* A chanspec holds the channel number, band, bandwidth and control sideband */
 using chanspec_t = uint16_t;
@@ -166,6 +167,7 @@
 #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_BW_LE20(chspec) (BW_LE20(CHSPEC_BW(chspec)))
 
 #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)
@@ -236,4 +238,9 @@
  */
 bool chspec_malformed(chanspec_t chanspec);
 
+// This function returns the channel number that control traffic is being sent on, for 20MHz
+// channels this is just the channel number, for 40MHZ, 80MHz, 160MHz channels it is the 20MHZ
+// sideband depending on the chanspec selected. Ported from wf_chspec_ctlchan().
+zx_status_t chspec_ctlchan(chanspec_t chspec, uint8_t* ctrl_chan);
+
 #endif  // THIRD_PARTY_BCMDHD_CROSSDRIVER_BCMWIFI_CHANNELS_H_