[wlan][common] Channel utilities for CBW80 and beyond

Check wlan_channel_t validity for CBW80, CBW80P80, CBW160.
Calculate center frequency in MHz and in channel index

Test: Unit test included
Change-Id: I69e81f8e625d7b889a219fca598ee38713994ddd
diff --git a/lib/wlan/common/channel.cpp b/lib/wlan/common/channel.cpp
index e32256a..3d88c7d 100644
--- a/lib/wlan/common/channel.cpp
+++ b/lib/wlan/common/channel.cpp
@@ -72,6 +72,12 @@
 
 bool IsValidChan5Ghz(const wlan_channel_t& chan) {
     uint8_t p = chan.primary;
+    uint8_t s = chan.secondary80;
+
+    // See IEEE Std 802.11-2016, Table 9-252, 9-253
+    // TODO(porce): Augment wlan_channel_t to carry
+    // "channel width" subfield of VHT Operation Info of VHT Operation IE.
+    // Test the validity of CCFS1, and the relation to the CCFS0.
 
     if (p < 36 || p > 173) { return false; }
     if (p > 64 && p < 100) { return false; }
@@ -93,6 +99,19 @@
     case CBW80:
         if (p == 165) { return false; }
         break;
+    case CBW80P80: {
+        if (!(s == 42 || s == 58 || s == 106 || s == 122 || s == 138 || s == 155)) { return false; }
+
+        uint8_t ccfs0 = GetCenterChanIdx(chan);
+        uint8_t ccfs1 = s;
+        uint8_t gap = (ccfs0 >= ccfs1) ? (ccfs0 - ccfs1) : (ccfs1 - ccfs0);
+        if (gap <= 16) { return false; }
+        break;
+    }
+    case CBW160: {
+        if (p >= 132) { return false; }
+        break;
+    }
     default:
         return false;
     }
@@ -117,26 +136,55 @@
     if (Is2Ghz(chan)) {
         channel_starting_frequency = kBaseFreq2Ghz;
     } else {
-        // 5Ghz
+        // 5 GHz
         channel_starting_frequency = kBaseFreq5Ghz;
     }
 
     // IEEE Std 802.11-2016, 21.3.14
-    return channel_starting_frequency + spacing * chan.primary;
+    return channel_starting_frequency + spacing * GetCenterChanIdx(chan);
 }
 
+// Returns the channel index corresponding to the first frequency segment's center frequency
 uint8_t GetCenterChanIdx(const wlan_channel_t& chan) {
-    ZX_DEBUG_ASSERT(IsValidChan(chan));
-
+    uint8_t p = chan.primary;
     switch (chan.cbw) {
     case CBW20:
-        return chan.primary;
+        return p;
     case CBW40ABOVE:
-        return chan.primary + 2;
+        return p + 2;
     case CBW40BELOW:
-        return chan.primary - 2;
+        return p - 2;
+    case CBW80:
+    case CBW80P80:
+        if (p <= 48) {
+            return 42;
+        } else if (p <= 64) {
+            return 58;
+        } else if (p <= 112) {
+            return 106;
+        } else if (p <= 128) {
+            return 122;
+        } else if (p <= 144) {
+            return 138;
+        } else if (p <= 161) {
+            return 155;
+        } else {
+            // Not reachable
+            return p;
+        }
+    case CBW160:
+        // See IEEE Std 802.11-2016 Table 9-252 and 9-253.
+        // Note CBW160 has only one frequency segment, regardless of
+        // encodings on CCFS0 and CCFS1 in VHT Operation Information IE.
+        if (p <= 64) {
+            return 50;
+        } else if (p <= 128) {
+            return 114;
+        } else {
+            // Not reachable
+            return p;
+        }
     default:
-        // TODO(porce): Support CBW80 and beyond
         return chan.primary;
     }
 }
diff --git a/lib/wlan/mlme/tests/channel_unittest.cpp b/lib/wlan/mlme/tests/channel_unittest.cpp
index c15eace..0cc519b 100644
--- a/lib/wlan/mlme/tests/channel_unittest.cpp
+++ b/lib/wlan/mlme/tests/channel_unittest.cpp
@@ -17,50 +17,38 @@
    protected:
 };
 
-struct TestVector {
-    uint8_t primary;
-    uint8_t cbw;
-    bool want;
-};
-
-struct TestVectorCbw {
-    uint8_t primary;
-    uint8_t cbw;
-    uint8_t want;
-};
-
 TEST_F(ChannelTest, ValidCombo) {
-    TestVector tvs[] = {
+    std::vector<wlan_channel_t> tvs = {
         // clang-format off
-        {1, CBW20, true},
-        {11, CBW20, true},
-        {1, CBW40ABOVE, true},
-        {6, CBW40ABOVE, true},
-        {6, CBW40BELOW, true},
-        {11, CBW40BELOW, true},
-        {36, CBW40ABOVE, true},
-        {40, CBW40BELOW, true},
-        {100, CBW40ABOVE, true},
-        {104, CBW40BELOW, true},
-        {149, CBW40ABOVE, true},
-        {153, CBW40BELOW, true},
-        {36, CBW80, true},
-        {40, CBW80, true},
-        {100, CBW80, true},
-        {149, CBW80, true},
-        {161, CBW80, true},
-        // Add more interesting cases
+        {  1, CBW20,        0},
+        { 11, CBW20,        0},
+        {  1, CBW40ABOVE,   0},
+        {  6, CBW40ABOVE,   0},
+        {  6, CBW40BELOW,   0},
+        { 11, CBW40BELOW,   0},
+        { 36, CBW40ABOVE,   0},
+        { 40, CBW40BELOW,   0},
+        {100, CBW40ABOVE,   0},
+        {104, CBW40BELOW,   0},
+        {149, CBW40ABOVE,   0},
+        {153, CBW40BELOW,   0},
+        { 36, CBW80,        0},
+        { 40, CBW80,        0},
+        {100, CBW80,        0},
+        {149, CBW80,        0},
+        {161, CBW80,        0},
+        { 36, CBW80P80,   106},
+        { 52, CBW80P80,   106},
+        {100, CBW80P80,    42},
+        {149, CBW80P80,    42},
+        {161, CBW80P80,    42},
+        { 36, CBW160,       0},
+        {100, CBW160,       0},
         // clang-format on
     };
 
-    for (uint32_t i = 0; i < sizeof(tvs) / sizeof(TestVector); i++) {
-        wlan_channel_t chan = {
-            .primary = tvs[i].primary,
-            .cbw = tvs[i].cbw,
-        };
-        bool want = tvs[i].want;
-
-        EXPECT_EQ(want, IsValidChan(chan));
+    for (auto tv : tvs) {
+        EXPECT_TRUE(IsValidChan(tv));
     }
 }
 
@@ -90,38 +78,46 @@
 }
 
 TEST_F(ChannelTest, InvalidCombo) {
-    std::vector<TestVector> tvs = {
+    std::vector<wlan_channel_t> tvs = {
         // clang-format off
-        {0, CBW20, false},
-        {15, CBW20, false},
-        {8, CBW40ABOVE, false},
-        {4, CBW40BELOW, false},
-        {32, CBW20, false},
-        {68, CBW20, false},
-        {96, CBW20, false},
-        {148, CBW20, false},
-        {169, CBW20, true},
-        {183, CBW20, false},
-        {36, CBW40BELOW, false},
-        {40, CBW40ABOVE, false},
-        {149, CBW40BELOW, false},
-        {153, CBW40ABOVE, false},
-        {165, CBW80, false},
+        {  0, CBW20,        0},
+        { 15, CBW20,        0},
+        {  8, CBW40ABOVE,   0},
+        {  4, CBW40BELOW,   0},
+        { 32, CBW20,        0},
+        { 68, CBW20,        0},
+        { 96, CBW20,        0},
+        {148, CBW20,        0},
+        {183, CBW20,        0},
+        { 36, CBW40BELOW,   0},
+        { 40, CBW40ABOVE,   0},
+        {149, CBW40BELOW,   0},
+        {153, CBW40ABOVE,   0},
+        {165, CBW80,        0},
+        { 36, CBW80P80,     0},
+        { 48, CBW80P80,    42},
+        {149, CBW80P80,   155},
+        {132, CBW160,      50},
         // Add more interesting cases
         // clang-format on
     };
 
-    for (TestVector tv : tvs) {
-        wlan_channel_t chan = {
-            .primary = tv.primary,
-            .cbw = tv.cbw,
-        };
-
-        EXPECT_EQ(tv.want, IsValidChan(chan));
+    for (auto tv : tvs) {
+        if (IsValidChan(tv)) {
+            printf("Test failed: Should treat this channel invalid:: %s\n",
+                   wlan::common::ChanStrLong(tv).c_str());
+        }
+        EXPECT_FALSE(IsValidChan(tv));
     }
 }
 
 TEST_F(ChannelTest, GetValidCbw) {
+    struct TestVectorCbw {
+        uint8_t primary;
+        uint8_t cbw;
+        uint8_t want;
+    };
+
     std::vector<TestVectorCbw> tvs = {
         // clang-format off
         {1, CBW20, CBW20},
@@ -179,6 +175,65 @@
     }
 }
 
+TEST_F(ChannelTest, GetCenterChanIdx) {
+    struct TestVector {
+        wlan_channel_t ddk;
+        uint8_t want;
+    };
+
+    std::vector<TestVector> tvs = {
+        // clang-format off
+        {{  1, CBW20,      0},   1},
+        {{ 11, CBW20,      0},  11},
+        {{ 36, CBW20,      0},  36},
+        {{161, CBW20,      0}, 161},
+        {{  1, CBW40ABOVE, 0},   3},
+        {{  5, CBW40ABOVE, 0},   7},
+        {{  5, CBW40BELOW, 0},   3},
+        {{ 11, CBW40BELOW, 0},   9},
+        {{ 36, CBW40ABOVE, 0},  38},
+        {{ 36, CBW80,      0},  42},
+        {{104, CBW80,      0}, 106},
+        {{ 36, CBW80P80, 122},  42},
+        {{ 36, CBW160,     0},  50},
+        {{100, CBW160,     0}, 114}
+        // clang-format on
+    };
+
+    for (auto tv : tvs) {
+        auto got = GetCenterChanIdx(tv.ddk);
+        EXPECT_EQ(tv.want, got);
+    }
+}
+
+TEST_F(ChannelTest, GetCenterFreq) {
+    struct TestVector {
+        wlan_channel_t ddk;
+        Mhz want;
+    };
+
+    std::vector<TestVector> tvs = {
+        // clang-format off
+        {{  1, CBW20,      0}, 2412},
+        {{  1, CBW40ABOVE, 0}, 2422},
+        {{  6, CBW40ABOVE, 0}, 2447},
+        {{  6, CBW40BELOW, 0}, 2427},
+        {{ 11, CBW20,      0}, 2462},
+        {{ 11, CBW40BELOW, 0}, 2452},
+        {{ 36, CBW20,      0}, 5180},
+        {{ 36, CBW40ABOVE, 0}, 5190},
+        {{ 36, CBW80,      0}, 5210},
+        {{ 36, CBW160,     0}, 5250},
+        {{161, CBW20,      0}, 5805},
+        // clang-format on
+    };
+
+    for (auto tv : tvs) {
+        auto got = GetCenterFreq(tv.ddk);
+        EXPECT_EQ(tv.want, got);
+    }
+}
+
 }  // namespace
 }  // namespace common
 }  // namespace wlan
diff --git a/lib/wlan/protocol/include/wlan/protocol/info.h b/lib/wlan/protocol/include/wlan/protocol/info.h
index 48a1ca1..c30201c 100644
--- a/lib/wlan/protocol/include/wlan/protocol/info.h
+++ b/lib/wlan/protocol/include/wlan/protocol/info.h
@@ -38,8 +38,9 @@
 
 typedef struct wlan_channel {
     uint8_t primary;
-    uint8_t cbw;  // Channel band width. See enum CBW.
-    uint8_t secondary80;
+    uint8_t cbw;          // Channel Bandwidth
+    uint8_t secondary80;  // Channel index corresponding to the center frequency
+                          // of the secondary frequency segment
 } wlan_channel_t;
 
 enum {