blob: 742d629cd0bc5c96f15ec469032ba8ff5748dc03 [file] [log] [blame]
/* 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;
}