/******************************************************************************
|
*
|
* Copyright(c) 2007 - 2022 Realtek Corporation.
|
*
|
* This program is free software; you can redistribute it and/or modify it
|
* under the terms of version 2 of the GNU General Public License as
|
* published by the Free Software Foundation.
|
*
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* more details.
|
*
|
*****************************************************************************/
|
#define _OS_CH_UTILS_C_
|
|
#include <drv_types.h>
|
|
#ifdef CONFIG_IOCTL_CFG80211
|
|
#define CHAN2G(_channel, _freq, _flags) { \
|
.band = NL80211_BAND_2GHZ, \
|
.center_freq = (_freq), \
|
.hw_value = (_channel), \
|
.flags = (_flags), \
|
.max_antenna_gain = 0, \
|
.max_power = 0, \
|
}
|
|
#define CHAN5G(_channel, _flags) { \
|
.band = NL80211_BAND_5GHZ, \
|
.center_freq = 5000 + (5 * (_channel)), \
|
.hw_value = (_channel), \
|
.flags = (_flags), \
|
.max_antenna_gain = 0, \
|
.max_power = 0, \
|
}
|
|
#define CHAN6G(_channel, _flags) { \
|
.band = NL80211_BAND_6GHZ, \
|
.center_freq = 5950 + (5 * (_channel)), \
|
.hw_value = (_channel), \
|
.flags = (_flags), \
|
.max_antenna_gain = 0, \
|
.max_power = 0, \
|
}
|
|
/* from center_ch_2g */
|
const struct ieee80211_channel rtw_2ghz_channels[MAX_CHANNEL_NUM_2G] = {
|
CHAN2G(1, 2412, 0),
|
CHAN2G(2, 2417, 0),
|
CHAN2G(3, 2422, 0),
|
CHAN2G(4, 2427, 0),
|
CHAN2G(5, 2432, 0),
|
CHAN2G(6, 2437, 0),
|
CHAN2G(7, 2442, 0),
|
CHAN2G(8, 2447, 0),
|
CHAN2G(9, 2452, 0),
|
CHAN2G(10, 2457, 0),
|
CHAN2G(11, 2462, 0),
|
CHAN2G(12, 2467, 0),
|
CHAN2G(13, 2472, 0),
|
CHAN2G(14, 2484, 0),
|
};
|
|
#if CONFIG_IEEE80211_BAND_5GHZ
|
/* from center_ch_5g_20m */
|
const struct ieee80211_channel rtw_5ghz_a_channels[MAX_CHANNEL_NUM_5G] = {
|
CHAN5G(36, 0), CHAN5G(40, 0), CHAN5G(44, 0), CHAN5G(48, 0),
|
|
CHAN5G(52, 0), CHAN5G(56, 0), CHAN5G(60, 0), CHAN5G(64, 0),
|
|
CHAN5G(100, 0), CHAN5G(104, 0), CHAN5G(108, 0), CHAN5G(112, 0),
|
CHAN5G(116, 0), CHAN5G(120, 0), CHAN5G(124, 0), CHAN5G(128, 0),
|
CHAN5G(132, 0), CHAN5G(136, 0), CHAN5G(140, 0), CHAN5G(144, 0),
|
|
CHAN5G(149, 0), CHAN5G(153, 0), CHAN5G(157, 0), CHAN5G(161, 0),
|
CHAN5G(165, 0), CHAN5G(169, 0), CHAN5G(173, 0), CHAN5G(177, 0),
|
};
|
#endif
|
|
#if CONFIG_IEEE80211_BAND_6GHZ
|
/* from center_ch_6g_20m */
|
const struct ieee80211_channel rtw_6ghz_channels[MAX_CHANNEL_NUM_6G] = {
|
CHAN6G(1, 0), CHAN6G(5, 0), CHAN6G(9, 0), CHAN6G(13, 0),
|
CHAN6G(17, 0), CHAN6G(21, 0), CHAN6G(25, 0), CHAN6G(29, 0),
|
CHAN6G(33, 0), CHAN6G(37, 0), CHAN6G(41, 0), CHAN6G(45, 0),
|
CHAN6G(49, 0), CHAN6G(53, 0), CHAN6G(57, 0), CHAN6G(61, 0),
|
CHAN6G(65, 0), CHAN6G(69, 0), CHAN6G(73, 0), CHAN6G(77, 0),
|
CHAN6G(81, 0), CHAN6G(85, 0), CHAN6G(89, 0), CHAN6G(93, 0),
|
|
CHAN6G(97, 0), CHAN6G(101, 0), CHAN6G(105, 0), CHAN6G(109, 0),
|
CHAN6G(113, 0), CHAN6G(117, 0),
|
|
CHAN6G(121, 0), CHAN6G(125, 0),
|
CHAN6G(129, 0), CHAN6G(133, 0), CHAN6G(137, 0), CHAN6G(141, 0),
|
CHAN6G(145, 0), CHAN6G(149, 0), CHAN6G(153, 0), CHAN6G(157, 0),
|
CHAN6G(161, 0), CHAN6G(165, 0), CHAN6G(169, 0), CHAN6G(173, 0),
|
CHAN6G(177, 0), CHAN6G(181, 0), CHAN6G(185, 0), CHAN6G(189, 0),
|
|
CHAN6G(193, 0), CHAN6G(197, 0), CHAN6G(201, 0), CHAN6G(205, 0),
|
CHAN6G(209, 0), CHAN6G(213, 0), CHAN6G(217, 0), CHAN6G(221, 0),
|
CHAN6G(225, 0), CHAN6G(229, 0), CHAN6G(233, 0), CHAN6G(237, 0),
|
|
CHAN6G(241, 0), CHAN6G(245, 0), CHAN6G(249, 0), CHAN6G(253, 0),
|
};
|
#endif /* CONFIG_IEEE80211_BAND_6GHZ */
|
|
const enum nl80211_band _rtw_band_to_nl80211_band[] = {
|
[BAND_ON_24G] = NL80211_BAND_2GHZ,
|
[BAND_ON_5G] = NL80211_BAND_5GHZ,
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
|
[BAND_ON_6G] = NL80211_BAND_6GHZ,
|
#else
|
[BAND_ON_6G] = NUM_NL80211_BANDS,
|
#endif
|
};
|
static_assert(ARRAY_SIZE(_rtw_band_to_nl80211_band) >= BAND_MAX);
|
|
const enum band_type _nl80211_band_to_rtw_band[] = {
|
[NL80211_BAND_2GHZ] = BAND_ON_24G,
|
[NL80211_BAND_5GHZ] = BAND_ON_5G,
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
|
[NL80211_BAND_60GHZ] = BAND_MAX,
|
#endif
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
|
#if CONFIG_IEEE80211_BAND_6GHZ
|
[NL80211_BAND_6GHZ] = BAND_ON_6G,
|
#else
|
[NL80211_BAND_6GHZ] = BAND_MAX,
|
#endif
|
#endif
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0))
|
[NL80211_BAND_S1GHZ] = BAND_MAX, /* around 900MHz, supported by S1G PHYs */
|
#endif
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)) || (defined(__ANDROID_COMMON_KERNEL__) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))
|
[NL80211_BAND_LC] = BAND_MAX, /* light communication band (placeholder) */
|
#endif
|
};
|
static_assert(ARRAY_SIZE(_nl80211_band_to_rtw_band) >= NUM_NL80211_BANDS);
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
|
const char *nl80211_chan_width_str(enum nl80211_chan_width cwidth)
|
{
|
switch (cwidth) {
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
return "20_NOHT";
|
case NL80211_CHAN_WIDTH_20:
|
return "20";
|
case NL80211_CHAN_WIDTH_40:
|
return "40";
|
case NL80211_CHAN_WIDTH_80:
|
return "80";
|
case NL80211_CHAN_WIDTH_80P80:
|
return "80+80";
|
case NL80211_CHAN_WIDTH_160:
|
return "160";
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
|
case NL80211_CHAN_WIDTH_5:
|
return "5";
|
case NL80211_CHAN_WIDTH_10:
|
return "10";
|
#endif
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0))
|
case NL80211_CHAN_WIDTH_1:
|
return "1";
|
case NL80211_CHAN_WIDTH_2:
|
return "2";
|
case NL80211_CHAN_WIDTH_4:
|
return "4";
|
case NL80211_CHAN_WIDTH_8:
|
return "8";
|
case NL80211_CHAN_WIDTH_16:
|
return "16";
|
#endif
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0))
|
case NL80211_CHAN_WIDTH_320:
|
return "320";
|
#endif
|
default:
|
return "INVALID";
|
};
|
}
|
|
u8 rtw_bchbw_to_cfg80211_chan_def(struct wiphy *wiphy, struct cfg80211_chan_def *chdef
|
, enum band_type band, u8 ch, u8 bw, u8 offset, u8 ht)
|
{
|
int freq, cfreq;
|
struct ieee80211_channel *chan;
|
u8 ret = _FAIL;
|
|
_rtw_memset(chdef, 0, sizeof(*chdef));
|
|
freq = rtw_bch2freq(band, ch);
|
if (!freq)
|
goto exit;
|
|
cfreq = rtw_get_center_ch_by_band(band, ch, bw, offset);
|
if (!cfreq)
|
goto exit;
|
cfreq = rtw_bch2freq(band, cfreq);
|
if (!cfreq)
|
goto exit;
|
|
chan = ieee80211_get_channel(wiphy, freq);
|
if (!chan)
|
goto exit;
|
|
if (bw == CHANNEL_WIDTH_20)
|
chdef->width = ht ? NL80211_CHAN_WIDTH_20 : NL80211_CHAN_WIDTH_20_NOHT;
|
else if (bw == CHANNEL_WIDTH_40)
|
chdef->width = NL80211_CHAN_WIDTH_40;
|
else if (bw == CHANNEL_WIDTH_80)
|
chdef->width = NL80211_CHAN_WIDTH_80;
|
else if (bw == CHANNEL_WIDTH_160)
|
chdef->width = NL80211_CHAN_WIDTH_160;
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
|
else if (bw == CHANNEL_WIDTH_5)
|
chdef->width = NL80211_CHAN_WIDTH_5;
|
else if (bw == CHANNEL_WIDTH_10)
|
chdef->width = NL80211_CHAN_WIDTH_10;
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) */
|
else {
|
rtw_warn_on(1);
|
goto exit;
|
}
|
|
chdef->chan = chan;
|
chdef->center_freq1 = cfreq;
|
|
ret = _SUCCESS;
|
|
exit:
|
return ret;
|
}
|
|
void rtw_get_bchbw_from_cfg80211_chan_def(struct cfg80211_chan_def *chdef
|
, u8 *ht, enum band_type *band, u8 *ch, u8 *bw, u8 *offset)
|
{
|
int pri_freq;
|
struct ieee80211_channel *chan = chdef->chan;
|
|
*ch = 0;
|
*band = nl80211_band_to_rtw_band(chan->band);
|
if (*band == BAND_MAX) {
|
RTW_WARN("invalid band:%d\n", chan->band);
|
rtw_warn_on(1);
|
return;
|
}
|
|
pri_freq = rtw_bch2freq(*band, chan->hw_value);
|
if (!pri_freq) {
|
RTW_WARN("invalid channel:%d on %s\n", chan->hw_value, band_str(*band));
|
rtw_warn_on(1);
|
return;
|
}
|
|
switch (chdef->width) {
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
*ht = 0;
|
*bw = CHANNEL_WIDTH_20;
|
*offset = CHAN_OFFSET_NO_EXT;
|
*ch = chan->hw_value;
|
break;
|
case NL80211_CHAN_WIDTH_20:
|
*ht = 1;
|
*bw = CHANNEL_WIDTH_20;
|
*offset = CHAN_OFFSET_NO_EXT;
|
*ch = chan->hw_value;
|
break;
|
case NL80211_CHAN_WIDTH_40:
|
*ht = 1;
|
*bw = CHANNEL_WIDTH_40;
|
*offset = pri_freq > chdef->center_freq1 ? CHAN_OFFSET_LOWER : CHAN_OFFSET_UPPER;
|
if (!rtw_get_offset_by_bchbw(*band, chan->hw_value, *bw, offset)) {
|
RTW_WARN("invalid channel:%d bw:%s on %s\n", chan->hw_value, ch_width_str(*bw), band_str(*band));
|
rtw_warn_on(1);
|
return;
|
}
|
*ch = chan->hw_value;
|
break;
|
case NL80211_CHAN_WIDTH_80:
|
*ht = 1;
|
*bw = CHANNEL_WIDTH_80;
|
if (!rtw_get_offset_by_bchbw(*band, chan->hw_value, *bw, offset)) {
|
RTW_WARN("invalid channel:%d bw:%s on %s\n", chan->hw_value, ch_width_str(*bw), band_str(*band));
|
rtw_warn_on(1);
|
return;
|
}
|
*ch = chan->hw_value;
|
break;
|
case NL80211_CHAN_WIDTH_160:
|
*ht = 1;
|
*bw = CHANNEL_WIDTH_160;
|
if (!rtw_get_offset_by_bchbw(*band, chan->hw_value, *bw, offset)) {
|
RTW_WARN("invalid channel:%d bw:%s on %s\n", chan->hw_value, ch_width_str(*bw), band_str(*band));
|
rtw_warn_on(1);
|
return;
|
}
|
*ch = chan->hw_value;
|
break;
|
case NL80211_CHAN_WIDTH_80P80:
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
|
case NL80211_CHAN_WIDTH_5:
|
case NL80211_CHAN_WIDTH_10:
|
#endif
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0))
|
case NL80211_CHAN_WIDTH_1:
|
case NL80211_CHAN_WIDTH_2:
|
case NL80211_CHAN_WIDTH_4:
|
case NL80211_CHAN_WIDTH_8:
|
case NL80211_CHAN_WIDTH_16:
|
#endif
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0))
|
case NL80211_CHAN_WIDTH_320:
|
#endif
|
default:
|
*ht = 0;
|
*bw = CHANNEL_WIDTH_20;
|
*offset = CHAN_OFFSET_NO_EXT;
|
RTW_INFO("unsupported cwidth:%s\n", nl80211_chan_width_str(chdef->width));
|
rtw_warn_on(1);
|
};
|
}
|
|
#if CONFIG_ALLOW_FUNC_2G_5G_ONLY
|
RTW_FUNC_2G_5G_ONLY u8 rtw_chbw_to_cfg80211_chan_def(struct wiphy *wiphy, struct cfg80211_chan_def *chdef
|
, u8 ch, u8 bw, u8 offset, u8 ht)
|
{
|
return rtw_bchbw_to_cfg80211_chan_def(wiphy, chdef, rtw_is_2g_ch(ch) ? BAND_ON_24G : BAND_ON_5G, ch, bw, offset, ht);
|
}
|
|
RTW_FUNC_2G_5G_ONLY void rtw_get_chbw_from_cfg80211_chan_def(struct cfg80211_chan_def *chdef
|
, u8 *ht, u8 *ch, u8 *bw, u8 *offset)
|
{
|
enum band_type band;
|
|
rtw_get_bchbw_from_cfg80211_chan_def(chdef, ht, &band, ch, bw, offset);
|
}
|
#endif
|
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
|
const char *nl80211_channel_type_str(enum nl80211_channel_type ctype)
|
{
|
switch (ctype) {
|
case NL80211_CHAN_NO_HT:
|
return "NO_HT";
|
case NL80211_CHAN_HT20:
|
return "HT20";
|
case NL80211_CHAN_HT40MINUS:
|
return "HT40-";
|
case NL80211_CHAN_HT40PLUS:
|
return "HT40+";
|
default:
|
return "INVALID";
|
};
|
}
|
|
enum nl80211_channel_type rtw_bchbw_to_nl80211_channel_type(enum band_type band, u8 ch, u8 bw, u8 offset, u8 ht)
|
{
|
rtw_warn_on(!ht && (bw >= CHANNEL_WIDTH_40 || offset != CHAN_OFFSET_NO_EXT));
|
|
if (!ht)
|
return NL80211_CHAN_NO_HT;
|
if (bw >= CHANNEL_WIDTH_40) {
|
if (offset == CHAN_OFFSET_LOWER)
|
return NL80211_CHAN_HT40MINUS;
|
else if (offset == CHAN_OFFSET_UPPER)
|
return NL80211_CHAN_HT40PLUS;
|
else
|
rtw_warn_on(1);
|
}
|
return NL80211_CHAN_HT20;
|
}
|
|
void rtw_get_bchbw_from_nl80211_channel_type(struct ieee80211_channel *chan, enum nl80211_channel_type ctype
|
, enum band_type *band, u8 *ht, u8 *ch, u8 *bw, u8 *offset)
|
{
|
int pri_freq;
|
|
*ch = 0;
|
*band = nl80211_band_to_rtw_band(chan->band);
|
if (*band == BAND_MAX) {
|
RTW_WARN("invalid band:%d\n", chan->band);
|
rtw_warn_on(1);
|
return;
|
}
|
|
pri_freq = rtw_bch2freq(*band, chan->hw_value);
|
if (!pri_freq) {
|
RTW_WARN("invalid channel:%d on %s\n", chan->hw_value, band_str(*band));
|
rtw_warn_on(1);
|
return;
|
}
|
*ch = chan->hw_value;
|
|
switch (ctype) {
|
case NL80211_CHAN_NO_HT:
|
*ht = 0;
|
*bw = CHANNEL_WIDTH_20;
|
*offset = CHAN_OFFSET_NO_EXT;
|
break;
|
case NL80211_CHAN_HT20:
|
*ht = 1;
|
*bw = CHANNEL_WIDTH_20;
|
*offset = CHAN_OFFSET_NO_EXT;
|
break;
|
case NL80211_CHAN_HT40MINUS:
|
*ht = 1;
|
*bw = CHANNEL_WIDTH_40;
|
*offset = CHAN_OFFSET_LOWER;
|
break;
|
case NL80211_CHAN_HT40PLUS:
|
*ht = 1;
|
*bw = CHANNEL_WIDTH_40;
|
*offset = CHAN_OFFSET_UPPER;
|
break;
|
default:
|
*ht = 0;
|
*bw = CHANNEL_WIDTH_20;
|
*offset = CHAN_OFFSET_NO_EXT;
|
RTW_INFO("unsupported ctype:%s\n", nl80211_channel_type_str(ctype));
|
rtw_warn_on(1);
|
};
|
}
|
|
#if CONFIG_ALLOW_FUNC_2G_5G_ONLY
|
RTW_FUNC_2G_5G_ONLY enum nl80211_channel_type rtw_chbw_to_nl80211_channel_type(u8 ch, u8 bw, u8 offset, u8 ht)
|
{
|
return rtw_bchbw_to_nl80211_channel_type(rtw_is_2g_ch(ch) ? BAND_ON_24G : BAND_ON_5G, ch, bw, offset, ht);
|
}
|
|
RTW_FUNC_2G_5G_ONLY void rtw_get_chbw_from_nl80211_channel_type(struct ieee80211_channel *chan, enum nl80211_channel_type ctype
|
, u8 *ht, u8 *ch, u8 *bw, u8 *offset)
|
{
|
enum band_type band;
|
|
rtw_get_bchbw_from_nl80211_channel_type(chan, ctype, &band, ht, ch, bw, offset);
|
}
|
#endif
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) */
|
|
#endif /* CONFIG_IOCTL_CFG80211 */
|