415 lines
12 KiB
C
415 lines
12 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright(c) 2009-2010 - 2017 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.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <drv_types.h>
|
|
|
|
#ifdef CONFIG_IOCTL_CFG80211
|
|
static void rtw_regd_overide_flags(struct wiphy *wiphy, struct rf_ctl_t *rfctl)
|
|
{
|
|
RT_CHANNEL_INFO *channel_set = rfctl->channel_set;
|
|
u8 max_chan_nums = rfctl->max_chan_nums;
|
|
struct ieee80211_supported_band *sband;
|
|
struct ieee80211_channel *ch;
|
|
unsigned int i, j;
|
|
u16 channel;
|
|
u32 freq;
|
|
|
|
/* all channels disable */
|
|
for (i = 0; i < NUM_NL80211_BANDS; i++) {
|
|
sband = wiphy->bands[i];
|
|
if (!sband)
|
|
continue;
|
|
for (j = 0; j < sband->n_channels; j++) {
|
|
ch = &sband->channels[j];
|
|
if (!ch)
|
|
continue;
|
|
ch->flags = IEEE80211_CHAN_DISABLED;
|
|
}
|
|
}
|
|
|
|
/* channels apply by channel plans. */
|
|
for (i = 0; i < max_chan_nums; i++) {
|
|
channel = channel_set[i].ChannelNum;
|
|
freq = rtw_ch2freq(channel);
|
|
ch = ieee80211_get_channel(wiphy, freq);
|
|
if (!ch) {
|
|
rtw_warn_on(1);
|
|
continue;
|
|
}
|
|
|
|
/* enable */
|
|
ch->flags = 0;
|
|
|
|
if (channel_set[i].flags & RTW_CHF_DFS) {
|
|
/*
|
|
* before integrating with nl80211 flow
|
|
* bypass IEEE80211_CHAN_RADAR when configured with radar detection
|
|
* to prevent from hostapd blocking DFS channels
|
|
*/
|
|
if (rtw_rfctl_dfs_domain_unknown(rfctl))
|
|
ch->flags |= IEEE80211_CHAN_RADAR;
|
|
}
|
|
|
|
if (channel_set[i].flags & RTW_CHF_NO_IR) {
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
|
|
ch->flags |= IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN;
|
|
#else
|
|
ch->flags |= IEEE80211_CHAN_NO_IR;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_REGD_SRC_FROM_OS
|
|
static void rtw_regd_apply_dfs_flags(struct rf_ctl_t *rfctl)
|
|
{
|
|
RT_CHANNEL_INFO *channel_set = rfctl->channel_set;
|
|
u8 max_chan_nums = rfctl->max_chan_nums;
|
|
unsigned int i;
|
|
struct ieee80211_channel *chan;
|
|
|
|
/* channels apply by channel plans. */
|
|
for (i = 0; i < max_chan_nums; i++) {
|
|
chan = channel_set[i].os_chan;
|
|
if (channel_set[i].flags & RTW_CHF_DFS) {
|
|
/*
|
|
* before integrating with nl80211 flow
|
|
* clear IEEE80211_CHAN_RADAR when configured with radar detection
|
|
* to prevent from hostapd blocking DFS channels
|
|
*/
|
|
if (!rtw_rfctl_dfs_domain_unknown(rfctl))
|
|
chan->flags &= ~IEEE80211_CHAN_RADAR;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void rtw_regd_apply_flags(struct wiphy *wiphy)
|
|
{
|
|
struct dvobj_priv *dvobj = wiphy_to_dvobj(wiphy);
|
|
struct rf_ctl_t *rfctl = dvobj_to_rfctl(dvobj);
|
|
|
|
if (rfctl->regd_src == REGD_SRC_RTK_PRIV)
|
|
rtw_regd_overide_flags(wiphy, rfctl);
|
|
#ifdef CONFIG_REGD_SRC_FROM_OS
|
|
else if (rfctl->regd_src == REGD_SRC_OS)
|
|
rtw_regd_apply_dfs_flags(rfctl);
|
|
#endif
|
|
else
|
|
rtw_warn_on(1);
|
|
}
|
|
|
|
#ifdef CONFIG_REGD_SRC_FROM_OS
|
|
/* init_channel_set_from_wiphy */
|
|
u8 rtw_os_init_channel_set(_adapter *padapter, RT_CHANNEL_INFO *channel_set)
|
|
{
|
|
struct wiphy *wiphy = adapter_to_wiphy(padapter);
|
|
struct rf_ctl_t *rfctl = adapter_to_rfctl(padapter);
|
|
struct registry_priv *regsty = adapter_to_regsty(padapter);
|
|
struct ieee80211_channel *chan;
|
|
u8 chanset_size = 0;
|
|
int i, j;
|
|
|
|
_rtw_memset(channel_set, 0, sizeof(RT_CHANNEL_INFO) * MAX_CHANNEL_NUM);
|
|
|
|
for (i = NL80211_BAND_2GHZ; i <= NL80211_BAND_5GHZ; i++) {
|
|
if (!wiphy->bands[i])
|
|
continue;
|
|
for (j = 0; j < wiphy->bands[i]->n_channels; j++) {
|
|
chan = &wiphy->bands[i]->channels[j];
|
|
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
|
continue;
|
|
if (rtw_regsty_is_excl_chs(regsty, chan->hw_value))
|
|
continue;
|
|
|
|
if (chanset_size >= MAX_CHANNEL_NUM) {
|
|
RTW_WARN("chset size can't exceed MAX_CHANNEL_NUM(%u)\n", MAX_CHANNEL_NUM);
|
|
i = NL80211_BAND_5GHZ + 1;
|
|
break;
|
|
}
|
|
|
|
channel_set[chanset_size].ChannelNum = chan->hw_value;
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
|
|
if (chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN))
|
|
#else
|
|
if (chan->flags & IEEE80211_CHAN_NO_IR)
|
|
#endif
|
|
channel_set[chanset_size].flags |= RTW_CHF_NO_IR;
|
|
if (chan->flags & IEEE80211_CHAN_RADAR)
|
|
channel_set[chanset_size].flags |= RTW_CHF_DFS;
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
|
|
if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
|
|
channel_set[chanset_size].flags |= RTW_CHF_NO_HT40U;
|
|
if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
|
|
channel_set[chanset_size].flags |= RTW_CHF_NO_HT40L;
|
|
#endif
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
|
|
if (chan->flags & IEEE80211_CHAN_NO_80MHZ)
|
|
channel_set[chanset_size].flags |= RTW_CHF_NO_80MHZ;
|
|
if (chan->flags & IEEE80211_CHAN_NO_160MHZ)
|
|
channel_set[chanset_size].flags |= RTW_CHF_NO_160MHZ;
|
|
#endif
|
|
channel_set[chanset_size].os_chan = chan;
|
|
chanset_size++;
|
|
}
|
|
}
|
|
|
|
#if CONFIG_IEEE80211_BAND_5GHZ
|
|
#ifdef CONFIG_DFS_MASTER
|
|
for (i = 0; i < chanset_size; i++)
|
|
channel_set[i].non_ocp_end_time = rtw_get_current_time();
|
|
#endif
|
|
#endif /* CONFIG_IEEE80211_BAND_5GHZ */
|
|
|
|
if (chanset_size)
|
|
RTW_INFO(FUNC_ADPT_FMT" ch num:%d\n"
|
|
, FUNC_ADPT_ARG(padapter), chanset_size);
|
|
else
|
|
RTW_WARN(FUNC_ADPT_FMT" final chset has no channel\n"
|
|
, FUNC_ADPT_ARG(padapter));
|
|
|
|
return chanset_size;
|
|
}
|
|
|
|
s16 rtw_os_get_total_txpwr_regd_lmt_mbm(_adapter *adapter, u8 cch, enum channel_width bw)
|
|
{
|
|
struct wiphy *wiphy = adapter_to_wiphy(adapter);
|
|
s16 mbm = UNSPECIFIED_MBM;
|
|
u8 *op_chs;
|
|
u8 op_ch_num;
|
|
u8 i;
|
|
u32 freq;
|
|
struct ieee80211_channel *ch;
|
|
|
|
if (!rtw_get_op_chs_by_cch_bw(cch, bw, &op_chs, &op_ch_num))
|
|
goto exit;
|
|
|
|
for (i = 0; i < op_ch_num; i++) {
|
|
freq = rtw_ch2freq(op_chs[i]);
|
|
ch = ieee80211_get_channel(wiphy, freq);
|
|
if (!ch) {
|
|
rtw_warn_on(1);
|
|
continue;
|
|
}
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))
|
|
mbm = rtw_min(mbm, ch->max_reg_power * MBM_PDBM);
|
|
#else
|
|
/* require max_power == 0 (therefore orig_mpwr set to 0) when wiphy registration */
|
|
mbm = rtw_min(mbm, ch->max_power * MBM_PDBM);
|
|
#endif
|
|
}
|
|
|
|
exit:
|
|
return mbm;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
|
|
static enum rtw_dfs_regd nl80211_dfs_regions_to_rtw_dfs_region(enum nl80211_dfs_regions region)
|
|
{
|
|
switch (region) {
|
|
case NL80211_DFS_FCC:
|
|
return RTW_DFS_REGD_FCC;
|
|
case NL80211_DFS_ETSI:
|
|
return RTW_DFS_REGD_ETSI;
|
|
case NL80211_DFS_JP:
|
|
return RTW_DFS_REGD_MKK;
|
|
case NL80211_DFS_UNSET:
|
|
default:
|
|
return RTW_DFS_REGD_NONE;
|
|
}
|
|
};
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) */
|
|
#endif /* CONFIG_REGD_SRC_FROM_OS */
|
|
|
|
#ifdef CONFIG_RTW_DEBUG
|
|
static const char *nl80211_reg_initiator_str(enum nl80211_reg_initiator initiator)
|
|
{
|
|
switch (initiator) {
|
|
case NL80211_REGDOM_SET_BY_DRIVER:
|
|
return "DRIVER";
|
|
case NL80211_REGDOM_SET_BY_CORE:
|
|
return "CORE";
|
|
case NL80211_REGDOM_SET_BY_USER:
|
|
return "USER";
|
|
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
|
|
return "COUNTRY_IE";
|
|
}
|
|
rtw_warn_on(1);
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
|
|
static const char *nl80211_user_reg_hint_type_str(enum nl80211_user_reg_hint_type type)
|
|
{
|
|
switch (type) {
|
|
case NL80211_USER_REG_HINT_USER:
|
|
return "USER";
|
|
case NL80211_USER_REG_HINT_CELL_BASE:
|
|
return "CELL_BASE";
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
|
|
case NL80211_USER_REG_HINT_INDOOR:
|
|
return "INDOOR";
|
|
#endif
|
|
}
|
|
rtw_warn_on(1);
|
|
return "UNKNOWN";
|
|
}
|
|
#endif
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
|
|
static const char *nl80211_dfs_regions_str(enum nl80211_dfs_regions region)
|
|
{
|
|
switch (region) {
|
|
case NL80211_DFS_UNSET:
|
|
return "UNSET";
|
|
case NL80211_DFS_FCC:
|
|
return "FCC";
|
|
case NL80211_DFS_ETSI:
|
|
return "ETSI";
|
|
case NL80211_DFS_JP:
|
|
return "JP";
|
|
}
|
|
rtw_warn_on(1);
|
|
return "UNKNOWN";
|
|
};
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) */
|
|
|
|
static const char *environment_cap_str(enum environment_cap cap)
|
|
{
|
|
switch (cap) {
|
|
case ENVIRON_ANY:
|
|
return "ANY";
|
|
case ENVIRON_INDOOR:
|
|
return "INDOOR";
|
|
case ENVIRON_OUTDOOR:
|
|
return "OUTDOOR";
|
|
}
|
|
rtw_warn_on(1);
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
static void dump_requlatory_request(void *sel, struct regulatory_request *request)
|
|
{
|
|
u8 alpha2_len;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0))
|
|
alpha2_len = 3;
|
|
#else
|
|
alpha2_len = 2;
|
|
#endif
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
|
|
RTW_PRINT_SEL(sel, "initiator:%s, wiphy_idx:%d, type:%s\n"
|
|
, nl80211_reg_initiator_str(request->initiator)
|
|
, request->wiphy_idx
|
|
, nl80211_user_reg_hint_type_str(request->user_reg_hint_type));
|
|
#else
|
|
RTW_PRINT_SEL(sel, "initiator:%s, wiphy_idx:%d\n"
|
|
, nl80211_reg_initiator_str(request->initiator)
|
|
, request->wiphy_idx);
|
|
#endif
|
|
|
|
RTW_PRINT_SEL(sel, "alpha2:%.*s\n", alpha2_len, request->alpha2);
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
|
|
RTW_PRINT_SEL(sel, "dfs_region:%s\n", nl80211_dfs_regions_str(request->dfs_region));
|
|
#endif
|
|
|
|
RTW_PRINT_SEL(sel, "intersect:%d\n", request->intersect);
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38))
|
|
RTW_PRINT_SEL(sel, "processed:%d\n", request->processed);
|
|
#endif
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36))
|
|
RTW_PRINT_SEL(sel, "country_ie_checksum:0x%08x\n", request->country_ie_checksum);
|
|
#endif
|
|
|
|
RTW_PRINT_SEL(sel, "country_ie_env:%s\n", environment_cap_str(request->country_ie_env));
|
|
}
|
|
#endif /* CONFIG_RTW_DEBUG */
|
|
|
|
static void rtw_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
|
|
{
|
|
struct dvobj_priv *dvobj = wiphy_to_dvobj(wiphy);
|
|
struct rf_ctl_t *rfctl = dvobj_to_rfctl(dvobj);
|
|
struct registry_priv *regsty = dvobj_to_regsty(dvobj);
|
|
|
|
#ifdef CONFIG_RTW_DEBUG
|
|
if (rtw_drv_log_level >= _DRV_INFO_) {
|
|
RTW_INFO(FUNC_WIPHY_FMT"\n", FUNC_WIPHY_ARG(wiphy));
|
|
dump_requlatory_request(RTW_DBGDUMP, request);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_REGD_SRC_FROM_OS
|
|
if (REGSTY_REGD_SRC_FROM_OS(regsty)) {
|
|
enum rtw_dfs_regd dfs_region = RTW_DFS_REGD_NONE;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
|
|
dfs_region = nl80211_dfs_regions_to_rtw_dfs_region(request->dfs_region);
|
|
#endif
|
|
|
|
/* trigger command to sync regulatory form OS */
|
|
rtw_sync_os_regd_cmd(wiphy_to_adapter(wiphy), RTW_CMDF_WAIT_ACK, request->alpha2, dfs_region);
|
|
} else
|
|
#endif
|
|
{
|
|
/* use alpha2 as input to select the corresponding channel plan settings defined by Realtek */
|
|
switch (request->initiator) {
|
|
case NL80211_REGDOM_SET_BY_DRIVER:
|
|
break;
|
|
case NL80211_REGDOM_SET_BY_CORE:
|
|
break;
|
|
case NL80211_REGDOM_SET_BY_USER:
|
|
rtw_set_country(wiphy_to_adapter(wiphy), request->alpha2);
|
|
break;
|
|
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
|
|
break;
|
|
}
|
|
|
|
rtw_regd_apply_flags(wiphy);
|
|
}
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))
|
|
static int rtw_reg_notifier_return(struct wiphy *wiphy, struct regulatory_request *request)
|
|
{
|
|
rtw_reg_notifier(wiphy, request);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int rtw_regd_init(struct wiphy *wiphy)
|
|
{
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))
|
|
wiphy->reg_notifier = rtw_reg_notifier_return;
|
|
#else
|
|
wiphy->reg_notifier = rtw_reg_notifier;
|
|
#endif
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
|
|
wiphy->flags &= ~WIPHY_FLAG_STRICT_REGULATORY;
|
|
wiphy->flags &= ~WIPHY_FLAG_DISABLE_BEACON_HINTS;
|
|
#else
|
|
wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG;
|
|
wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS;
|
|
#endif
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 39))
|
|
wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_IOCTL_CFG80211 */
|