162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NXP Wireless LAN device driver: 802.11h 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2011-2020 NXP 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "main.h" 962306a36Sopenharmony_ci#include "fw.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_civoid mwifiex_init_11h_params(struct mwifiex_private *priv) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci priv->state_11h.is_11h_enabled = true; 1562306a36Sopenharmony_ci priv->state_11h.is_11h_active = false; 1662306a36Sopenharmony_ci} 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciinline int mwifiex_is_11h_active(struct mwifiex_private *priv) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci return priv->state_11h.is_11h_active; 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ci/* This function appends 11h info to a buffer while joining an 2362306a36Sopenharmony_ci * infrastructure BSS 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_cistatic void 2662306a36Sopenharmony_cimwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer, 2762306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct mwifiex_ie_types_header *ie_header; 3062306a36Sopenharmony_ci struct mwifiex_ie_types_pwr_capability *cap; 3162306a36Sopenharmony_ci struct mwifiex_ie_types_local_pwr_constraint *constraint; 3262306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 3362306a36Sopenharmony_ci u8 radio_type; 3462306a36Sopenharmony_ci int i; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (!buffer || !(*buffer)) 3762306a36Sopenharmony_ci return; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); 4062306a36Sopenharmony_ci sband = priv->wdev.wiphy->bands[radio_type]; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci cap = (struct mwifiex_ie_types_pwr_capability *)*buffer; 4362306a36Sopenharmony_ci cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY); 4462306a36Sopenharmony_ci cap->header.len = cpu_to_le16(2); 4562306a36Sopenharmony_ci cap->min_pwr = 0; 4662306a36Sopenharmony_ci cap->max_pwr = 0; 4762306a36Sopenharmony_ci *buffer += sizeof(*cap); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer; 5062306a36Sopenharmony_ci constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT); 5162306a36Sopenharmony_ci constraint->header.len = cpu_to_le16(2); 5262306a36Sopenharmony_ci constraint->chan = bss_desc->channel; 5362306a36Sopenharmony_ci constraint->constraint = bss_desc->local_constraint; 5462306a36Sopenharmony_ci *buffer += sizeof(*constraint); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci ie_header = (struct mwifiex_ie_types_header *)*buffer; 5762306a36Sopenharmony_ci ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); 5862306a36Sopenharmony_ci ie_header->len = cpu_to_le16(2 * sband->n_channels + 2); 5962306a36Sopenharmony_ci *buffer += sizeof(*ie_header); 6062306a36Sopenharmony_ci *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS; 6162306a36Sopenharmony_ci *(*buffer)++ = 2 * sband->n_channels; 6262306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 6362306a36Sopenharmony_ci *(*buffer)++ = ieee80211_frequency_to_channel( 6462306a36Sopenharmony_ci sband->channels[i].center_freq); 6562306a36Sopenharmony_ci *(*buffer)++ = 1; /* one channel in the subband */ 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Enable or disable the 11h extensions in the firmware */ 7062306a36Sopenharmony_ciint mwifiex_11h_activate(struct mwifiex_private *priv, bool flag) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci u32 enable = flag; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* enable master mode radar detection on AP interface */ 7562306a36Sopenharmony_ci if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && enable) 7662306a36Sopenharmony_ci enable |= MWIFIEX_MASTER_RADAR_DET_MASK; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, 7962306a36Sopenharmony_ci HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* This functions processes TLV buffer for a pending BSS Join command. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * Activate 11h functionality in the firmware if the spectrum management 8562306a36Sopenharmony_ci * capability bit is found in the network we are joining. Also, necessary 8662306a36Sopenharmony_ci * TLVs are set based on requested network's 11h capability. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_civoid mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, 8962306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci if (bss_desc->sensed_11h) { 9262306a36Sopenharmony_ci /* Activate 11h functions in firmware, turns on capability 9362306a36Sopenharmony_ci * bit 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci mwifiex_11h_activate(priv, true); 9662306a36Sopenharmony_ci priv->state_11h.is_11h_active = true; 9762306a36Sopenharmony_ci bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT; 9862306a36Sopenharmony_ci mwifiex_11h_process_infra_join(priv, buffer, bss_desc); 9962306a36Sopenharmony_ci } else { 10062306a36Sopenharmony_ci /* Deactivate 11h functions in the firmware */ 10162306a36Sopenharmony_ci mwifiex_11h_activate(priv, false); 10262306a36Sopenharmony_ci priv->state_11h.is_11h_active = false; 10362306a36Sopenharmony_ci bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* This is DFS CAC work queue function. 10862306a36Sopenharmony_ci * This delayed work emits CAC finished event for cfg80211 if 10962306a36Sopenharmony_ci * CAC was started earlier. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_civoid mwifiex_dfs_cac_work_queue(struct work_struct *work) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct cfg80211_chan_def chandef; 11462306a36Sopenharmony_ci struct delayed_work *delayed_work = to_delayed_work(work); 11562306a36Sopenharmony_ci struct mwifiex_private *priv = 11662306a36Sopenharmony_ci container_of(delayed_work, struct mwifiex_private, 11762306a36Sopenharmony_ci dfs_cac_work); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci chandef = priv->dfs_chandef; 12062306a36Sopenharmony_ci if (priv->wdev.cac_started) { 12162306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, MSG, 12262306a36Sopenharmony_ci "CAC timer finished; No radar detected\n"); 12362306a36Sopenharmony_ci cfg80211_cac_event(priv->netdev, &chandef, 12462306a36Sopenharmony_ci NL80211_RADAR_CAC_FINISHED, 12562306a36Sopenharmony_ci GFP_KERNEL); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* This function prepares channel report request command to FW for 13062306a36Sopenharmony_ci * starting radar detection. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ciint mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, 13362306a36Sopenharmony_ci struct host_cmd_ds_command *cmd, 13462306a36Sopenharmony_ci void *data_buf) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req; 13762306a36Sopenharmony_ci struct mwifiex_radar_params *radar_params = (void *)data_buf; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST); 14062306a36Sopenharmony_ci cmd->size = cpu_to_le16(S_DS_GEN); 14162306a36Sopenharmony_ci le16_unaligned_add_cpu(&cmd->size, 14262306a36Sopenharmony_ci sizeof(struct host_cmd_ds_chan_rpt_req)); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ); 14562306a36Sopenharmony_ci cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value; 14662306a36Sopenharmony_ci cr_req->chan_desc.chan_width = radar_params->chandef->width; 14762306a36Sopenharmony_ci cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (radar_params->cac_time_ms) 15062306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, MSG, 15162306a36Sopenharmony_ci "11h: issuing DFS Radar check for channel=%d\n", 15262306a36Sopenharmony_ci radar_params->chandef->chan->hw_value); 15362306a36Sopenharmony_ci else 15462306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, MSG, "cancelling CAC\n"); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ciint mwifiex_stop_radar_detection(struct mwifiex_private *priv, 16062306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct mwifiex_radar_params radar_params; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); 16562306a36Sopenharmony_ci radar_params.chandef = chandef; 16662306a36Sopenharmony_ci radar_params.cac_time_ms = 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, 16962306a36Sopenharmony_ci HostCmd_ACT_GEN_SET, 0, &radar_params, true); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* This function is to abort ongoing CAC upon stopping AP operations 17362306a36Sopenharmony_ci * or during unload. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_civoid mwifiex_abort_cac(struct mwifiex_private *priv) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci if (priv->wdev.cac_started) { 17862306a36Sopenharmony_ci if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) 17962306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 18062306a36Sopenharmony_ci "failed to stop CAC in FW\n"); 18162306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, MSG, 18262306a36Sopenharmony_ci "Aborting delayed work for CAC.\n"); 18362306a36Sopenharmony_ci cancel_delayed_work_sync(&priv->dfs_cac_work); 18462306a36Sopenharmony_ci cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, 18562306a36Sopenharmony_ci NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* This function handles channel report event from FW during CAC period. 19062306a36Sopenharmony_ci * If radar is detected during CAC, driver indicates the same to cfg80211 19162306a36Sopenharmony_ci * and also cancels ongoing delayed work. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ciint mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, 19462306a36Sopenharmony_ci struct sk_buff *skb) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct host_cmd_ds_chan_rpt_event *rpt_event; 19762306a36Sopenharmony_ci struct mwifiex_ie_types_chan_rpt_data *rpt; 19862306a36Sopenharmony_ci u16 event_len, tlv_len; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci rpt_event = (void *)(skb->data + sizeof(u32)); 20162306a36Sopenharmony_ci event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event)+ 20262306a36Sopenharmony_ci sizeof(u32)); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (le32_to_cpu(rpt_event->result) != HostCmd_RESULT_OK) { 20562306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 20662306a36Sopenharmony_ci "Error in channel report event\n"); 20762306a36Sopenharmony_ci return -1; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci while (event_len >= sizeof(struct mwifiex_ie_types_header)) { 21162306a36Sopenharmony_ci rpt = (void *)&rpt_event->tlvbuf; 21262306a36Sopenharmony_ci tlv_len = le16_to_cpu(rpt->header.len); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci switch (le16_to_cpu(rpt->header.type)) { 21562306a36Sopenharmony_ci case TLV_TYPE_CHANRPT_11H_BASIC: 21662306a36Sopenharmony_ci if (rpt->map.radar) { 21762306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, MSG, 21862306a36Sopenharmony_ci "RADAR Detected on channel %d!\n", 21962306a36Sopenharmony_ci priv->dfs_chandef.chan->hw_value); 22062306a36Sopenharmony_ci cancel_delayed_work_sync(&priv->dfs_cac_work); 22162306a36Sopenharmony_ci cfg80211_cac_event(priv->netdev, 22262306a36Sopenharmony_ci &priv->dfs_chandef, 22362306a36Sopenharmony_ci NL80211_RADAR_DETECTED, 22462306a36Sopenharmony_ci GFP_KERNEL); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci default: 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci event_len -= (tlv_len + sizeof(rpt->header)); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* Handler for radar detected event from FW.*/ 23862306a36Sopenharmony_ciint mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, 23962306a36Sopenharmony_ci struct sk_buff *skb) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct mwifiex_radar_det_event *rdr_event; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci rdr_event = (void *)(skb->data + sizeof(u32)); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, MSG, 24662306a36Sopenharmony_ci "radar detected; indicating kernel\n"); 24762306a36Sopenharmony_ci if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) 24862306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 24962306a36Sopenharmony_ci "Failed to stop CAC in FW\n"); 25062306a36Sopenharmony_ci cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef, 25162306a36Sopenharmony_ci GFP_KERNEL); 25262306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, MSG, "regdomain: %d\n", 25362306a36Sopenharmony_ci rdr_event->reg_domain); 25462306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, MSG, "radar detection type: %d\n", 25562306a36Sopenharmony_ci rdr_event->det_type); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* This is work queue function for channel switch handling. 26162306a36Sopenharmony_ci * This function takes care of updating new channel definitin to 26262306a36Sopenharmony_ci * bss config structure, restart AP and indicate channel switch success 26362306a36Sopenharmony_ci * to cfg80211. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_civoid mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct mwifiex_uap_bss_param *bss_cfg; 26862306a36Sopenharmony_ci struct delayed_work *delayed_work = to_delayed_work(work); 26962306a36Sopenharmony_ci struct mwifiex_private *priv = 27062306a36Sopenharmony_ci container_of(delayed_work, struct mwifiex_private, 27162306a36Sopenharmony_ci dfs_chan_sw_work); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci bss_cfg = &priv->bss_cfg; 27462306a36Sopenharmony_ci if (!bss_cfg->beacon_period) { 27562306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 27662306a36Sopenharmony_ci "channel switch: AP already stopped\n"); 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci mwifiex_uap_set_channel(priv, bss_cfg, priv->dfs_chandef); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (mwifiex_config_start_uap(priv, bss_cfg)) { 28362306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 28462306a36Sopenharmony_ci "Failed to start AP after channel switch\n"); 28562306a36Sopenharmony_ci return; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, MSG, 28962306a36Sopenharmony_ci "indicating channel switch completion to kernel\n"); 29062306a36Sopenharmony_ci mutex_lock(&priv->wdev.mtx); 29162306a36Sopenharmony_ci cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef, 0, 0); 29262306a36Sopenharmony_ci mutex_unlock(&priv->wdev.mtx); 29362306a36Sopenharmony_ci} 294