summaryrefslogtreecommitdiff
path: root/fwcmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'fwcmd.c')
-rw-r--r--fwcmd.c358
1 files changed, 347 insertions, 11 deletions
diff --git a/fwcmd.c b/fwcmd.c
index e89f202..15bf20e 100644
--- a/fwcmd.c
+++ b/fwcmd.c
@@ -85,6 +85,7 @@ static char *mwl_fwcmd_get_cmd_string(unsigned short cmd)
{ HOSTCMD_CMD_SET_INFRA_MODE, "SetInfraMode" },
{ HOSTCMD_CMD_802_11_RTS_THSD, "80211RtsThreshold" },
{ HOSTCMD_CMD_SET_EDCA_PARAMS, "SetEDCAParams" },
+ { HOSTCMD_CMD_802_11H_DETECT_RADAR, "80211hDetectRadar" },
{ HOSTCMD_CMD_SET_WMM_MODE, "SetWMMMode" },
{ HOSTCMD_CMD_HT_GUARD_INTERVAL, "HtGuardInterval" },
{ HOSTCMD_CMD_SET_FIXED_RATE, "SetFixedRate" },
@@ -98,12 +99,18 @@ static char *mwl_fwcmd_get_cmd_string(unsigned short cmd)
{ HOSTCMD_CMD_AP_BEACON, "SetApBeacon" },
{ HOSTCMD_CMD_SET_NEW_STN, "SetNewStation" },
{ HOSTCMD_CMD_SET_APMODE, "SetApMode" },
+ { HOSTCMD_CMD_SET_SWITCH_CHANNEL, "SetSwitchChannel" },
{ HOSTCMD_CMD_UPDATE_ENCRYPTION, "UpdateEncryption" },
{ HOSTCMD_CMD_BASTREAM, "BAStream" },
+ { HOSTCMD_CMD_SET_SPECTRUM_MGMT, "SetSpectrumMgmt" },
+ { HOSTCMD_CMD_SET_POWER_CONSTRAINT, "SetPowerConstraint" },
+ { HOSTCMD_CMD_SET_COUNTRY_CODE, "SetCountryCode" },
{ HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL, "SetOptimizationLevel"},
{ HOSTCMD_CMD_DWDS_ENABLE, "DwdsEnable" },
{ HOSTCMD_CMD_FW_FLUSH_TIMER, "FwFlushTimer" },
{ HOSTCMD_CMD_SET_CDD, "SetCDD" },
+ { HOSTCMD_CMD_GET_TEMP, "GetTemp" },
+ { HOSTCMD_CMD_QUIET_MODE, "QuietMode" },
};
max_entries = ARRAY_SIZE(cmds);
@@ -272,7 +279,7 @@ static int mwl_fwcmd_set_tx_powers(struct mwl_priv *priv, u16 txpow[],
return 0;
}
-static u8 mwl_fwcmd_get_80m_pri_chnl_offset(u8 channel)
+static u8 mwl_fwcmd_get_80m_pri_chnl(u8 channel)
{
u8 act_primary = ACT_PRIMARY_CHAN_0;
@@ -377,6 +384,7 @@ static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv,
beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];
beacon_info->cap_info = le16_to_cpu(mgmt->u.beacon.capab_info);
+ beacon_info->power_constraint = 0;
pos = (u8 *)mgmt->u.beacon.variable;
left = len - baselen;
@@ -396,6 +404,10 @@ static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv,
}
switch (id) {
+ case WLAN_EID_COUNTRY:
+ beacon_info->ie_country_len = (elen + 2);
+ beacon_info->ie_country_ptr = (pos - 2);
+ break;
case WLAN_EID_SUPP_RATES:
case WLAN_EID_EXT_SUPP_RATES:
{
@@ -435,6 +447,10 @@ static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv,
}
}
break;
+ case WLAN_EID_PWR_CONSTRAINT:
+ if (elen == 1)
+ beacon_info->power_constraint = *pos;
+ break;
case WLAN_EID_RSN:
beacon_info->ie_rsn48_len = (elen + 2);
beacon_info->ie_rsn48_ptr = (pos - 2);
@@ -517,9 +533,6 @@ static int mwl_fwcmd_set_ies(struct mwl_priv *priv, struct mwl_vif *mwl_vif)
struct beacon_info *beacon = &mwl_vif->beacon_info;
u16 ie_list_len_proprietary = 0;
- if (!beacon->valid)
- return -EINVAL;
-
if (beacon->ie_ht_len > sizeof(pcmd->ie_list_ht))
goto einval;
@@ -587,9 +600,6 @@ static int mwl_fwcmd_set_ap_beacon(struct mwl_priv *priv,
struct hostcmd_cmd_ap_beacon *pcmd;
struct ds_params *phy_ds_param_set;
- if (!mwl_vif->beacon_info.valid)
- return -EINVAL;
-
/* wmm structure of start command is defined less one byte,
* due to following field country is not used, add byte one
* to bypass the check.
@@ -660,6 +670,120 @@ ielenerr:
return -EINVAL;
}
+static int mwl_fwcmd_set_spectrum_mgmt(struct mwl_priv *priv, bool enable)
+{
+ struct hostcmd_cmd_set_spectrum_mgmt *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_spectrum_mgmt *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_SPECTRUM_MGMT);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->spectrum_mgmt = cpu_to_le32(enable);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_SPECTRUM_MGMT)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+static int mwl_fwcmd_set_power_constraint(struct mwl_priv *priv,
+ u32 power_constraint)
+{
+ struct hostcmd_cmd_set_power_constraint *pcmd;
+
+ pcmd = (struct hostcmd_cmd_set_power_constraint *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_POWER_CONSTRAINT);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->power_constraint = cpu_to_le32(power_constraint);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_POWER_CONSTRAINT)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+static int mwl_fwcmd_set_country_code(struct mwl_priv *priv,
+ struct mwl_vif *mwl_vif,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ struct hostcmd_cmd_set_country_code *pcmd;
+ struct beacon_info *b_inf = &mwl_vif->beacon_info;
+ u8 chnl_len;
+ bool a_band;
+ bool enable = false;
+
+ if (b_inf->ie_country_ptr) {
+ if (bss_conf->chandef.chan->band == IEEE80211_BAND_2GHZ)
+ a_band = false;
+ else if (bss_conf->chandef.chan->band == IEEE80211_BAND_5GHZ)
+ a_band = true;
+ else
+ return -EINVAL;
+
+ chnl_len = b_inf->ie_country_len - 5;
+ if (a_band) {
+ if (chnl_len > sizeof(pcmd->domain_info.domain_entry_a))
+ return -EINVAL;
+ } else {
+ if (chnl_len > sizeof(pcmd->domain_info.domain_entry_g))
+ return -EINVAL;
+ }
+
+ enable = true;
+ }
+
+ pcmd = (struct hostcmd_cmd_set_country_code *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_COUNTRY_CODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le32(enable);
+ if (enable) {
+ memcpy(pcmd->domain_info.country_string,
+ b_inf->ie_country_ptr + 2, 3);
+ if (a_band) {
+ pcmd->domain_info.g_chnl_len = 0;
+ pcmd->domain_info.a_chnl_len = chnl_len;
+ memcpy(pcmd->domain_info.domain_entry_a,
+ b_inf->ie_country_ptr + 5, chnl_len);
+ } else {
+ pcmd->domain_info.a_chnl_len = 0;
+ pcmd->domain_info.g_chnl_len = chnl_len;
+ memcpy(pcmd->domain_info.domain_entry_g,
+ b_inf->ie_country_ptr + 5, chnl_len);
+ }
+ }
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_COUNTRY_CODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
static int mwl_fwcmd_encryption_set_cmd_info(struct hostcmd_cmd_set_key *cmd,
u8 *addr,
struct ieee80211_key_conf *key)
@@ -1255,7 +1379,7 @@ int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw,
case NL80211_CHAN_WIDTH_80:
chnl_width = CH_80_MHZ_WIDTH;
act_primary =
- mwl_fwcmd_get_80m_pri_chnl_offset(pcmd->curr_chnl);
+ mwl_fwcmd_get_80m_pri_chnl(pcmd->curr_chnl);
break;
default:
mutex_unlock(&priv->fwcmd_mutex);
@@ -1406,6 +1530,50 @@ int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index,
return 0;
}
+int mwl_fwcmd_set_radar_detect(struct ieee80211_hw *hw, u16 action)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_802_11h_detect_radar *pcmd;
+ u16 radar_type = RADAR_TYPE_CODE_0;
+ u8 channel = hw->conf.chandef.chan->hw_value;
+
+ pcmd = (struct hostcmd_cmd_802_11h_detect_radar *)&priv->pcmd_buf[0];
+
+ if (priv->dfs_region == NL80211_DFS_JP) {
+ if (channel >= 52 && channel <= 64)
+ radar_type = RADAR_TYPE_CODE_53;
+ else if (channel >= 100 && channel <= 140)
+ radar_type = RADAR_TYPE_CODE_56;
+ else
+ radar_type = RADAR_TYPE_CODE_0;
+ } else if (priv->dfs_region == NL80211_DFS_ETSI) {
+ radar_type = RADAR_TYPE_CODE_ETSI;
+ }
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11H_DETECT_RADAR);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(action);
+ pcmd->radar_type_code = cpu_to_le16(radar_type);
+ pcmd->min_chirp_cnt = cpu_to_le16(priv->dfs_chirp_count_min);
+ pcmd->chirp_time_intvl = cpu_to_le16(priv->dfs_chirp_time_interval);
+ pcmd->pw_filter = cpu_to_le16(priv->dfs_pw_filter);
+ pcmd->min_num_radar = cpu_to_le16(priv->dfs_min_num_radar);
+ pcmd->pri_min_num = cpu_to_le16(priv->dfs_min_pri_count);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11H_DETECT_RADAR)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
{
struct mwl_priv *priv = hw->priv;
@@ -1678,24 +1846,49 @@ int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw,
{
struct mwl_priv *priv = hw->priv;
struct mwl_vif *mwl_vif;
+ struct beacon_info *b_inf;
+ int rc;
mwl_vif = mwl_dev_get_vif(vif);
+ b_inf = &mwl_vif->beacon_info;
mwl_fwcmd_parse_beacon(priv, mwl_vif, beacon, len);
+ if (!b_inf->valid)
+ goto err;
+
if (mwl_fwcmd_set_ies(priv, mwl_vif))
goto err;
if (mwl_fwcmd_set_ap_beacon(priv, mwl_vif, &vif->bss_conf))
goto err;
- mwl_vif->beacon_info.valid = false;
+ if (mwl_fwcmd_bss_start(hw, vif, true))
+ goto err;
+
+ if (b_inf->cap_info & WLAN_CAPABILITY_SPECTRUM_MGMT)
+ rc = mwl_fwcmd_set_spectrum_mgmt(priv, true);
+ else
+ rc = mwl_fwcmd_set_spectrum_mgmt(priv, false);
+ if (rc)
+ goto err;
+
+ if (b_inf->power_constraint)
+ rc = mwl_fwcmd_set_power_constraint(priv,
+ b_inf->power_constraint);
+ if (rc)
+ goto err;
+
+ if (mwl_fwcmd_set_country_code(priv, mwl_vif, &vif->bss_conf))
+ goto err;
+
+ b_inf->valid = false;
return 0;
err:
- mwl_vif->beacon_info.valid = false;
+ b_inf->valid = false;
return -EIO;
}
@@ -1755,13 +1948,19 @@ int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw,
pcmd->peer_info.vht_rx_channel_width = sta->bandwidth;
}
- /* Patch mesh interface for VHT based on 88W8897. Once if mac80211
+ /* Patch mesh interface for VHT based on chip type. Once if mac80211
* supports VHT for mesh interface, following code should be removed.
*/
if (vif->type == NL80211_IFTYPE_MESH_POINT) {
pcmd->peer_info.vht_max_rx_mcs = cpu_to_le32(0x0000fffa);
pcmd->peer_info.vht_cap = cpu_to_le32(0x33801931);
pcmd->peer_info.vht_rx_channel_width = 2;
+ if (priv->chip_type == MWL8864) {
+ if (priv->antenna_rx == ANTENNA_RX_4_AUTO) {
+ pcmd->peer_info.vht_max_rx_mcs =
+ cpu_to_le32(0x0000ffea);
+ }
+ }
}
pcmd->is_qos_sta = sta->wme;
@@ -1887,6 +2086,85 @@ int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode)
return 0;
}
+int mwl_fwcmd_set_switch_channel(struct mwl_priv *priv,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ struct hostcmd_cmd_set_switch_channel *pcmd;
+ struct cfg80211_chan_def *chandef = &ch_switch->chandef;
+ struct ieee80211_channel *channel = chandef->chan;
+ u32 chnl_flags, freq_band, chnl_width, act_primary, sec_chnl_offset;
+
+ if (priv->csa_active)
+ return 0;
+
+ if (channel->band == IEEE80211_BAND_2GHZ)
+ freq_band = FREQ_BAND_2DOT4GHZ;
+ else if (channel->band == IEEE80211_BAND_5GHZ)
+ freq_band = FREQ_BAND_5GHZ;
+ else
+ return -EINVAL;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ chnl_width = CH_20_MHZ_WIDTH;
+ act_primary = ACT_PRIMARY_CHAN_0;
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ chnl_width = CH_40_MHZ_WIDTH;
+ if (chandef->center_freq1 > channel->center_freq) {
+ act_primary = ACT_PRIMARY_CHAN_0;
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ } else {
+ act_primary = ACT_PRIMARY_CHAN_1;
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ }
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ chnl_width = CH_80_MHZ_WIDTH;
+ act_primary =
+ mwl_fwcmd_get_80m_pri_chnl(channel->hw_value);
+ if ((act_primary == ACT_PRIMARY_CHAN_0) ||
+ (act_primary == ACT_PRIMARY_CHAN_2))
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ else
+ sec_chnl_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ chnl_flags = (freq_band & FREQ_BAND_MASK) |
+ ((chnl_width << CHNL_WIDTH_SHIFT) & CHNL_WIDTH_MASK) |
+ ((act_primary << ACT_PRIMARY_SHIFT) & ACT_PRIMARY_MASK);
+
+ pcmd = (struct hostcmd_cmd_set_switch_channel *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_SWITCH_CHANNEL);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->next_11h_chnl = cpu_to_le32(channel->hw_value);
+ pcmd->mode = cpu_to_le32(ch_switch->block_tx);
+ pcmd->init_count = cpu_to_le32(ch_switch->count + 1);
+ pcmd->chnl_flags = cpu_to_le32(chnl_flags);
+ pcmd->next_ht_extchnl_offset = cpu_to_le32(sec_chnl_offset);
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_SWITCH_CHANNEL)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(priv->hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ priv->csa_active = true;
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u8 *addr, u8 encr_type)
@@ -2412,6 +2690,64 @@ int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw)
return 0;
}
+int mwl_fwcmd_get_temp(struct ieee80211_hw *hw, u32 *temp)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_get_temp *pcmd;
+
+ pcmd = (struct hostcmd_cmd_get_temp *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_TEMP);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_TEMP)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ *temp = le32_to_cpu(pcmd->celcius);
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
+int mwl_fwcmd_quiet_mode(struct ieee80211_hw *hw, bool enable, u32 period,
+ u32 duration, u32 next_offset)
+{
+ struct mwl_priv *priv = hw->priv;
+ struct hostcmd_cmd_quiet_mode *pcmd;
+
+ pcmd = (struct hostcmd_cmd_quiet_mode *)&priv->pcmd_buf[0];
+
+ mutex_lock(&priv->fwcmd_mutex);
+
+ memset(pcmd, 0x00, sizeof(*pcmd));
+ pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_QUIET_MODE);
+ pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+ pcmd->action = cpu_to_le16(WL_SET);
+ pcmd->enable = cpu_to_le32(enable);
+ if (enable) {
+ pcmd->period = cpu_to_le32(period);
+ pcmd->duration = cpu_to_le32(duration);
+ pcmd->next_offset = cpu_to_le32(next_offset);
+ }
+
+ if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_QUIET_MODE)) {
+ mutex_unlock(&priv->fwcmd_mutex);
+ wiphy_err(hw->wiphy, "failed execution\n");
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->fwcmd_mutex);
+
+ return 0;
+}
+
int mwl_fwcmd_send_mfg_cmd(struct mwl_priv *priv, char *mfgcmd)
{
struct hostcmd_header *pcmd;