blob: d43acc041225e86b0463b81e032adef1c8fd7cc1 [file] [log] [blame]
/*
* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*============================================================================
FILE: cds_reg_service.c
OVERVIEW: This source file contains definitions for CDS regulatory APIs
DEPENDENCIES: None
============================================================================*/
#include <net/cfg80211.h>
#include "qdf_types.h"
#include "cds_reg_service.h"
#include "qdf_trace.h"
#include "sme_api.h"
#include "cds_api.h"
#include "cds_reg_service.h"
#include "cds_regdomain.h"
const struct chan_map chan_mapping[NUM_CHANNELS] = {
[CHAN_ENUM_1] = {2412, 1},
[CHAN_ENUM_2] = {2417, 2},
[CHAN_ENUM_3] = {2422, 3},
[CHAN_ENUM_4] = {2427, 4},
[CHAN_ENUM_5] = {2432, 5},
[CHAN_ENUM_6] = {2437, 6},
[CHAN_ENUM_7] = {2442, 7},
[CHAN_ENUM_8] = {2447, 8},
[CHAN_ENUM_9] = {2452, 9},
[CHAN_ENUM_10] = {2457, 10},
[CHAN_ENUM_11] = {2462, 11},
[CHAN_ENUM_12] = {2467, 12},
[CHAN_ENUM_13] = {2472, 13},
[CHAN_ENUM_14] = {2484, 14},
[CHAN_ENUM_36] = {5180, 36},
[CHAN_ENUM_40] = {5200, 40},
[CHAN_ENUM_44] = {5220, 44},
[CHAN_ENUM_48] = {5240, 48},
[CHAN_ENUM_52] = {5260, 52},
[CHAN_ENUM_56] = {5280, 56},
[CHAN_ENUM_60] = {5300, 60},
[CHAN_ENUM_64] = {5320, 64},
[CHAN_ENUM_100] = {5500, 100},
[CHAN_ENUM_104] = {5520, 104},
[CHAN_ENUM_108] = {5540, 108},
[CHAN_ENUM_112] = {5560, 112},
[CHAN_ENUM_116] = {5580, 116},
[CHAN_ENUM_120] = {5600, 120},
[CHAN_ENUM_124] = {5620, 124},
[CHAN_ENUM_128] = {5640, 128},
[CHAN_ENUM_132] = {5660, 132},
[CHAN_ENUM_136] = {5680, 136},
[CHAN_ENUM_140] = {5700, 140},
[CHAN_ENUM_144] = {5720, 144},
[CHAN_ENUM_149] = {5745, 149},
[CHAN_ENUM_153] = {5765, 153},
[CHAN_ENUM_157] = {5785, 157},
[CHAN_ENUM_161] = {5805, 161},
[CHAN_ENUM_165] = {5825, 165},
[CHAN_ENUM_170] = {5852, 170},
[CHAN_ENUM_171] = {5855, 171},
[CHAN_ENUM_172] = {5860, 172},
[CHAN_ENUM_173] = {5865, 173},
[CHAN_ENUM_174] = {5870, 174},
[CHAN_ENUM_175] = {5875, 175},
[CHAN_ENUM_176] = {5880, 176},
[CHAN_ENUM_177] = {5885, 177},
[CHAN_ENUM_178] = {5890, 178},
[CHAN_ENUM_179] = {5895, 179},
[CHAN_ENUM_180] = {5900, 180},
[CHAN_ENUM_181] = {5905, 181},
[CHAN_ENUM_182] = {5910, 182},
[CHAN_ENUM_183] = {5915, 183},
[CHAN_ENUM_184] = {5920, 184},
};
struct regulatory_channel reg_channels[NUM_CHANNELS];
uint8_t default_country[CDS_COUNTRY_CODE_LEN + 1];
uint8_t dfs_region;
/**
* cds_get_channel_list_with_power() - retrieve channel list with power
* @base_channels: base channels
* @num_base_channels: number of base channels
*
* Return: QDF_STATUS_SUCCESS
*/
QDF_STATUS cds_get_channel_list_with_power(struct channel_power
*base_channels,
uint8_t *num_base_channels)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
int i, count;
if (base_channels && num_base_channels) {
count = 0;
for (i = 0; i <= CHAN_ENUM_14; i++) {
if (reg_channels[i].state) {
base_channels[count].chan_num =
chan_mapping[i].chan_num;
base_channels[count++].power =
reg_channels[i].pwr_limit;
}
}
for (i = CHAN_ENUM_36; i <= CHAN_ENUM_184; i++) {
if (reg_channels[i].state) {
base_channels[count].chan_num =
chan_mapping[i].chan_num;
base_channels[count++].power =
reg_channels[i].pwr_limit;
}
}
*num_base_channels = count;
}
return status;
}
/**
* cds_read_default_country() - set the default country
* @def_ctry: default country
*
* Return: QDF_STATUS
*/
QDF_STATUS cds_read_default_country(uint8_t *def_ctry)
{
memcpy(def_ctry,
default_country,
CDS_COUNTRY_CODE_LEN + 1);
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO,
"default country is %c%c\n",
def_ctry[0],
def_ctry[1]);
return QDF_STATUS_SUCCESS;
}
/**
* cds_get_channel_enum() - get the channel enumeration
* @chan_num: channel number
*
* Return: enum for the channel
*/
static enum channel_enum cds_get_channel_enum(uint32_t chan_num)
{
uint32_t loop;
for (loop = 0; loop <= CHAN_ENUM_184; loop++)
if (chan_mapping[loop].chan_num == chan_num)
return loop;
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
"invalid channel number %d", chan_num);
return INVALID_CHANNEL;
}
/**
* cds_get_channel_state() - get the channel state
* @chan_num: channel number
*
* Return: channel state
*/
enum channel_state cds_get_channel_state(uint32_t chan_num)
{
enum channel_enum chan_enum;
chan_enum = cds_get_channel_enum(chan_num);
if (INVALID_CHANNEL == chan_enum)
return CHANNEL_STATE_INVALID;
else
return reg_channels[chan_enum].state;
}
/**
* cds_get_bonded_channel_state() - get the bonded channel state
* @channel_num: channel number
*
* Return: channel state
*/
enum channel_state cds_get_bonded_channel_state(uint32_t chan_num,
enum phy_ch_width ch_width)
{
enum channel_enum chan_enum;
bool bw_enabled = false;
chan_enum = cds_get_channel_enum(chan_num);
if (INVALID_CHANNEL == chan_enum)
return CHANNEL_STATE_INVALID;
if (reg_channels[chan_enum].state) {
if (CH_WIDTH_5MHZ == ch_width)
bw_enabled = 1;
else if (CH_WIDTH_10MHZ == ch_width)
bw_enabled = !(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_10MHZ);
else if (CH_WIDTH_20MHZ == ch_width)
bw_enabled = !(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_20MHZ);
else if (CH_WIDTH_40MHZ == ch_width)
bw_enabled = !(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_HT40);
else if (CH_WIDTH_80MHZ == ch_width)
bw_enabled = !(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_80MHZ);
else if (CH_WIDTH_160MHZ == ch_width)
bw_enabled = !(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_160MHZ);
}
if (bw_enabled)
return reg_channels[chan_enum].state;
else
return CHANNEL_STATE_DISABLE;
}
/**
* cds_get_max_channel_bw() - get the max channel bandwidth
* @channel_num: channel number
*
* Return: channel_width
*/
enum phy_ch_width cds_get_max_channel_bw(uint32_t chan_num)
{
enum channel_enum chan_enum;
enum phy_ch_width chan_bw = CH_WIDTH_INVALID;
chan_enum = cds_get_channel_enum(chan_num);
if ((INVALID_CHANNEL != chan_enum) &&
(CHANNEL_STATE_DISABLE != reg_channels[chan_enum].state)) {
if (!(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_160MHZ))
chan_bw = CH_WIDTH_160MHZ;
else if (!(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_80MHZ))
chan_bw = CH_WIDTH_80MHZ;
else if (!(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_HT40))
chan_bw = CH_WIDTH_40MHZ;
else if (!(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_20MHZ))
chan_bw = CH_WIDTH_20MHZ;
else if (!(reg_channels[chan_enum].flags &
IEEE80211_CHAN_NO_10MHZ))
chan_bw = CH_WIDTH_10MHZ;
else
chan_bw = CH_WIDTH_5MHZ;
}
return chan_bw;
}
/**
* cds_get_dfs_region() - get the dfs_region
* @dfs_region: the dfs_region to return
*
* Return: QDF_STATUS
*/
QDF_STATUS cds_get_dfs_region(uint8_t *dfs_reg)
{
*dfs_reg = dfs_region;
return QDF_STATUS_SUCCESS;
}
/**
* cds_get_reg_domain_from_country_code() - get the regulatory domain
* @reg_domain_ptr: ptr to store regulatory domain
*
* Return: QDF_STATUS_SUCCESS on success
* QDF_STATUS_E_FAULT on error
* QDF_STATUS_E_EMPTY country table empty
*/
QDF_STATUS cds_get_reg_domain_from_country_code(v_REGDOMAIN_t *reg_domain_ptr,
const uint8_t *country_alpha2,
enum country_src source)
{
if (NULL == reg_domain_ptr) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
"Invalid reg domain pointer");
return QDF_STATUS_E_FAULT;
}
*reg_domain_ptr = 0;
if (SOURCE_QUERY == source)
return QDF_STATUS_SUCCESS;
if (NULL == country_alpha2) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
"Country code array is NULL");
return QDF_STATUS_E_FAULT;
}
if (cds_is_driver_recovering()) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
"SSR in progress, return");
return QDF_STATUS_SUCCESS;
}
if (SOURCE_11D == source || SOURCE_USERSPACE == source)
regulatory_hint_user(country_alpha2,
NL80211_USER_REG_HINT_USER);
return QDF_STATUS_SUCCESS;
}
/*
* cds_is_dsrc_channel() - is the channel DSRC
* @center_freq: center freq of the channel
*
* Return: true if dsrc channel
* false otherwise
*/
bool cds_is_dsrc_channel(uint16_t center_freq)
{
if (center_freq >= 5852 &&
center_freq <= 5920)
return 1;
return 0;
}
/**
* cds_set_reg_domain() - set regulatory domain
* @client_ctxt: client context
* @reg_domain: regulatory domain
*
* Return: QDF_STATUS
*/
QDF_STATUS cds_set_reg_domain(void *client_ctxt, v_REGDOMAIN_t reg_domain)
{
if (reg_domain >= REGDOMAIN_COUNT) {
QDF_TRACE(QDF_MODULE_ID_QDF_DEVICE, QDF_TRACE_LEVEL_ERROR,
"CDS set reg domain, invalid REG domain ID %d",
reg_domain);
return QDF_STATUS_E_INVAL;
}
return QDF_STATUS_SUCCESS;
}
/**
* cds_put_dfs_region() - save dfs region
* @dfs_reg: dfs region
*
* Return: QDF_STATUS
*/
QDF_STATUS cds_put_dfs_region(uint8_t dfs_reg)
{
dfs_region = dfs_reg;
return QDF_STATUS_SUCCESS;
}
/**
* cds_put_default_country() - save the default country
* @dfs_country: default country
*
* Return: QDF_STATUS
*/
QDF_STATUS cds_put_default_country(uint8_t *def_country)
{
default_country[0] = def_country[0];
default_country[1] = def_country[1];
return QDF_STATUS_SUCCESS;
}
/**
* cds_set_ch_params() - set channel parameters
* @ch: channel
* @phy_mode: physical mode
* @ch_param: channel parameters will be returned
*
* Return: None
*/
void cds_set_ch_params(uint8_t ch, uint32_t phy_mode,
struct ch_params_s *ch_params)
{
tHalHandle *hal_ctx = cds_get_context(QDF_MODULE_ID_PE);
if (!hal_ctx) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
("Invalid hal_ctx pointer"));
return;
}
/*
* TODO: remove SME call and move the SME set channel
* param functionality to CDS.
*/
sme_set_ch_params(hal_ctx, phy_mode, ch, 0, ch_params);
}