| /* 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" |
| |
| #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); |
| |
| /* 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; |
| } |
| |
| // 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; |
| } |