162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. 562306a36Sopenharmony_ci * Copyright(c) 2018 Intel Corporation 662306a36Sopenharmony_ci *****************************************************************************/ 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/etherdevice.h> 1062306a36Sopenharmony_ci#include <net/mac80211.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "dev.h" 1362306a36Sopenharmony_ci#include "agn.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after 1662306a36Sopenharmony_ci * sending probe req. This should be set long enough to hear probe responses 1762306a36Sopenharmony_ci * from more than one AP. */ 1862306a36Sopenharmony_ci#define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ 1962306a36Sopenharmony_ci#define IWL_ACTIVE_DWELL_TIME_52 (20) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3) 2262306a36Sopenharmony_ci#define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. 2562306a36Sopenharmony_ci * Must be set longer than active dwell time. 2662306a36Sopenharmony_ci * For the most reliable scan, set > AP beacon interval (typically 100msec). */ 2762306a36Sopenharmony_ci#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ 2862306a36Sopenharmony_ci#define IWL_PASSIVE_DWELL_TIME_52 (10) 2962306a36Sopenharmony_ci#define IWL_PASSIVE_DWELL_BASE (100) 3062306a36Sopenharmony_ci#define IWL_CHANNEL_TUNE_TIME 5 3162306a36Sopenharmony_ci#define MAX_SCAN_CHANNEL 50 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* For reset radio, need minimal dwell time only */ 3462306a36Sopenharmony_ci#define IWL_RADIO_RESET_DWELL_TIME 5 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int iwl_send_scan_abort(struct iwl_priv *priv) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci int ret; 3962306a36Sopenharmony_ci struct iwl_host_cmd cmd = { 4062306a36Sopenharmony_ci .id = REPLY_SCAN_ABORT_CMD, 4162306a36Sopenharmony_ci .flags = CMD_WANT_SKB, 4262306a36Sopenharmony_ci }; 4362306a36Sopenharmony_ci __le32 *status; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* Exit instantly with error when device is not ready 4662306a36Sopenharmony_ci * to receive scan abort command or it does not perform 4762306a36Sopenharmony_ci * hardware scan currently */ 4862306a36Sopenharmony_ci if (!test_bit(STATUS_READY, &priv->status) || 4962306a36Sopenharmony_ci !test_bit(STATUS_SCAN_HW, &priv->status) || 5062306a36Sopenharmony_ci test_bit(STATUS_FW_ERROR, &priv->status)) 5162306a36Sopenharmony_ci return -EIO; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &cmd); 5462306a36Sopenharmony_ci if (ret) 5562306a36Sopenharmony_ci return ret; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci status = (void *)cmd.resp_pkt->data; 5862306a36Sopenharmony_ci if (*status != CAN_ABORT_STATUS) { 5962306a36Sopenharmony_ci /* The scan abort will return 1 for success or 6062306a36Sopenharmony_ci * 2 for "failure". A failure condition can be 6162306a36Sopenharmony_ci * due to simply not being in an active scan which 6262306a36Sopenharmony_ci * can occur if we send the scan abort before we 6362306a36Sopenharmony_ci * the microcode has notified us that a scan is 6462306a36Sopenharmony_ci * completed. */ 6562306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", 6662306a36Sopenharmony_ci le32_to_cpu(*status)); 6762306a36Sopenharmony_ci ret = -EIO; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci iwl_free_resp(&cmd); 7162306a36Sopenharmony_ci return ret; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void iwl_complete_scan(struct iwl_priv *priv, bool aborted) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct cfg80211_scan_info info = { 7762306a36Sopenharmony_ci .aborted = aborted, 7862306a36Sopenharmony_ci }; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* check if scan was requested from mac80211 */ 8162306a36Sopenharmony_ci if (priv->scan_request) { 8262306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n"); 8362306a36Sopenharmony_ci ieee80211_scan_completed(priv->hw, &info); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci priv->scan_type = IWL_SCAN_NORMAL; 8762306a36Sopenharmony_ci priv->scan_vif = NULL; 8862306a36Sopenharmony_ci priv->scan_request = NULL; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void iwl_process_scan_complete(struct iwl_priv *priv) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci bool aborted; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->status)) 9862306a36Sopenharmony_ci return; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Completed scan.\n"); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci cancel_delayed_work(&priv->scan_check); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status); 10562306a36Sopenharmony_ci if (aborted) 10662306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) { 10962306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); 11062306a36Sopenharmony_ci goto out_settings; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { 11462306a36Sopenharmony_ci int err; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Check if mac80211 requested scan during our internal scan */ 11762306a36Sopenharmony_ci if (priv->scan_request == NULL) 11862306a36Sopenharmony_ci goto out_complete; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* If so request a new scan */ 12162306a36Sopenharmony_ci err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL, 12262306a36Sopenharmony_ci priv->scan_request->channels[0]->band); 12362306a36Sopenharmony_ci if (err) { 12462306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, 12562306a36Sopenharmony_ci "failed to initiate pending scan: %d\n", err); 12662306a36Sopenharmony_ci aborted = true; 12762306a36Sopenharmony_ci goto out_complete; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ciout_complete: 13462306a36Sopenharmony_ci iwl_complete_scan(priv, aborted); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciout_settings: 13762306a36Sopenharmony_ci /* Can we still talk to firmware ? */ 13862306a36Sopenharmony_ci if (!iwl_is_ready_rf(priv)) 13962306a36Sopenharmony_ci return; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci iwlagn_post_scan(priv); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid iwl_force_scan_end(struct iwl_priv *priv) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!test_bit(STATUS_SCANNING, &priv->status)) { 14962306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n"); 15062306a36Sopenharmony_ci return; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Forcing scan end\n"); 15462306a36Sopenharmony_ci clear_bit(STATUS_SCANNING, &priv->status); 15562306a36Sopenharmony_ci clear_bit(STATUS_SCAN_HW, &priv->status); 15662306a36Sopenharmony_ci clear_bit(STATUS_SCAN_ABORTING, &priv->status); 15762306a36Sopenharmony_ci clear_bit(STATUS_SCAN_COMPLETE, &priv->status); 15862306a36Sopenharmony_ci iwl_complete_scan(priv, true); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void iwl_do_scan_abort(struct iwl_priv *priv) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci int ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!test_bit(STATUS_SCANNING, &priv->status)) { 16862306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n"); 16962306a36Sopenharmony_ci return; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) { 17362306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan abort in progress\n"); 17462306a36Sopenharmony_ci return; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = iwl_send_scan_abort(priv); 17862306a36Sopenharmony_ci if (ret) { 17962306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret); 18062306a36Sopenharmony_ci iwl_force_scan_end(priv); 18162306a36Sopenharmony_ci } else 18262306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Successfully send scan abort\n"); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * iwl_scan_cancel - Cancel any currently executing HW scan 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ciint iwl_scan_cancel(struct iwl_priv *priv) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Queuing abort scan\n"); 19162306a36Sopenharmony_ci queue_work(priv->workqueue, &priv->abort_scan); 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * iwl_scan_cancel_timeout - Cancel any currently executing HW scan 19762306a36Sopenharmony_ci * @ms: amount of time to wait (in milliseconds) for scan to abort 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_civoid iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(ms); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n"); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci iwl_do_scan_abort(priv); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci while (time_before_eq(jiffies, timeout)) { 21062306a36Sopenharmony_ci if (!test_bit(STATUS_SCAN_HW, &priv->status)) 21162306a36Sopenharmony_ci goto finished; 21262306a36Sopenharmony_ci msleep(20); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci finished: 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * Now STATUS_SCAN_HW is clear. This means that the 22062306a36Sopenharmony_ci * device finished, but the background work is going 22162306a36Sopenharmony_ci * to execute at best as soon as we release the mutex. 22262306a36Sopenharmony_ci * Since we need to be able to issue a new scan right 22362306a36Sopenharmony_ci * after this function returns, run the complete here. 22462306a36Sopenharmony_ci * The STATUS_SCAN_COMPLETE bit will then be cleared 22562306a36Sopenharmony_ci * and prevent the background work from "completing" 22662306a36Sopenharmony_ci * a possible new scan. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci iwl_process_scan_complete(priv); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* Service response to REPLY_SCAN_CMD (0x80) */ 23262306a36Sopenharmony_cistatic void iwl_rx_reply_scan(struct iwl_priv *priv, 23362306a36Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 23662306a36Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 23762306a36Sopenharmony_ci struct iwl_scanreq_notification *notif = (void *)pkt->data; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status); 24062306a36Sopenharmony_ci#endif 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* Service SCAN_START_NOTIFICATION (0x82) */ 24462306a36Sopenharmony_cistatic void iwl_rx_scan_start_notif(struct iwl_priv *priv, 24562306a36Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 24862306a36Sopenharmony_ci struct iwl_scanstart_notification *notif = (void *)pkt->data; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci priv->scan_start_tsf = le32_to_cpu(notif->tsf_low); 25162306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan start: " 25262306a36Sopenharmony_ci "%d [802.11%s] " 25362306a36Sopenharmony_ci "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", 25462306a36Sopenharmony_ci notif->channel, 25562306a36Sopenharmony_ci notif->band ? "bg" : "a", 25662306a36Sopenharmony_ci le32_to_cpu(notif->tsf_high), 25762306a36Sopenharmony_ci le32_to_cpu(notif->tsf_low), 25862306a36Sopenharmony_ci notif->status, notif->beacon_timer); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* Service SCAN_RESULTS_NOTIFICATION (0x83) */ 26262306a36Sopenharmony_cistatic void iwl_rx_scan_results_notif(struct iwl_priv *priv, 26362306a36Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 26662306a36Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 26762306a36Sopenharmony_ci struct iwl_scanresults_notification *notif = (void *)pkt->data; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan ch.res: " 27062306a36Sopenharmony_ci "%d [802.11%s] " 27162306a36Sopenharmony_ci "probe status: %u:%u " 27262306a36Sopenharmony_ci "(TSF: 0x%08X:%08X) - %d " 27362306a36Sopenharmony_ci "elapsed=%lu usec\n", 27462306a36Sopenharmony_ci notif->channel, 27562306a36Sopenharmony_ci notif->band ? "bg" : "a", 27662306a36Sopenharmony_ci notif->probe_status, notif->num_probe_not_sent, 27762306a36Sopenharmony_ci le32_to_cpu(notif->tsf_high), 27862306a36Sopenharmony_ci le32_to_cpu(notif->tsf_low), 27962306a36Sopenharmony_ci le32_to_cpu(notif->statistics[0]), 28062306a36Sopenharmony_ci le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); 28162306a36Sopenharmony_ci#endif 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ 28562306a36Sopenharmony_cistatic void iwl_rx_scan_complete_notif(struct iwl_priv *priv, 28662306a36Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 28962306a36Sopenharmony_ci struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", 29262306a36Sopenharmony_ci scan_notif->scanned_channels, 29362306a36Sopenharmony_ci scan_notif->tsf_low, 29462306a36Sopenharmony_ci scan_notif->tsf_high, scan_notif->status); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", 29762306a36Sopenharmony_ci (priv->scan_band == NL80211_BAND_2GHZ) ? "2.4" : "5.2", 29862306a36Sopenharmony_ci jiffies_to_msecs(jiffies - priv->scan_start)); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * When aborting, we run the scan completed background work inline 30262306a36Sopenharmony_ci * and the background work must then do nothing. The SCAN_COMPLETE 30362306a36Sopenharmony_ci * bit helps implement that logic and thus needs to be set before 30462306a36Sopenharmony_ci * queueing the work. Also, since the scan abort waits for SCAN_HW 30562306a36Sopenharmony_ci * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW 30662306a36Sopenharmony_ci * to avoid a race there. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci set_bit(STATUS_SCAN_COMPLETE, &priv->status); 30962306a36Sopenharmony_ci clear_bit(STATUS_SCAN_HW, &priv->status); 31062306a36Sopenharmony_ci queue_work(priv->workqueue, &priv->scan_completed); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (priv->iw_mode != NL80211_IFTYPE_ADHOC && 31362306a36Sopenharmony_ci iwl_advanced_bt_coexist(priv) && 31462306a36Sopenharmony_ci priv->bt_status != scan_notif->bt_status) { 31562306a36Sopenharmony_ci if (scan_notif->bt_status) { 31662306a36Sopenharmony_ci /* BT on */ 31762306a36Sopenharmony_ci if (!priv->bt_ch_announce) 31862306a36Sopenharmony_ci priv->bt_traffic_load = 31962306a36Sopenharmony_ci IWL_BT_COEX_TRAFFIC_LOAD_HIGH; 32062306a36Sopenharmony_ci /* 32162306a36Sopenharmony_ci * otherwise, no traffic load information provided 32262306a36Sopenharmony_ci * no changes made 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci /* BT off */ 32662306a36Sopenharmony_ci priv->bt_traffic_load = 32762306a36Sopenharmony_ci IWL_BT_COEX_TRAFFIC_LOAD_NONE; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci priv->bt_status = scan_notif->bt_status; 33062306a36Sopenharmony_ci queue_work(priv->workqueue, 33162306a36Sopenharmony_ci &priv->bt_traffic_change_work); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_civoid iwl_setup_rx_scan_handlers(struct iwl_priv *priv) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci /* scan handlers */ 33862306a36Sopenharmony_ci priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan; 33962306a36Sopenharmony_ci priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif; 34062306a36Sopenharmony_ci priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = 34162306a36Sopenharmony_ci iwl_rx_scan_results_notif; 34262306a36Sopenharmony_ci priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = 34362306a36Sopenharmony_ci iwl_rx_scan_complete_notif; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic u16 iwl_get_active_dwell_time(struct iwl_priv *priv, 34762306a36Sopenharmony_ci enum nl80211_band band, u8 n_probes) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 35062306a36Sopenharmony_ci return IWL_ACTIVE_DWELL_TIME_52 + 35162306a36Sopenharmony_ci IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); 35262306a36Sopenharmony_ci else 35362306a36Sopenharmony_ci return IWL_ACTIVE_DWELL_TIME_24 + 35462306a36Sopenharmony_ci IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct iwl_rxon_context *ctx; 36062306a36Sopenharmony_ci int limits[NUM_IWL_RXON_CTX] = {}; 36162306a36Sopenharmony_ci int n_active = 0; 36262306a36Sopenharmony_ci u16 limit; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* 36762306a36Sopenharmony_ci * If we're associated, we clamp the dwell time 98% 36862306a36Sopenharmony_ci * of the beacon interval (minus 2 * channel tune time) 36962306a36Sopenharmony_ci * If both contexts are active, we have to restrict to 37062306a36Sopenharmony_ci * 1/2 of the minimum of them, because they might be in 37162306a36Sopenharmony_ci * lock-step with the time inbetween only half of what 37262306a36Sopenharmony_ci * time we'd have in each of them. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci for_each_context(priv, ctx) { 37562306a36Sopenharmony_ci switch (ctx->staging.dev_type) { 37662306a36Sopenharmony_ci case RXON_DEV_TYPE_P2P: 37762306a36Sopenharmony_ci /* no timing constraints */ 37862306a36Sopenharmony_ci continue; 37962306a36Sopenharmony_ci case RXON_DEV_TYPE_ESS: 38062306a36Sopenharmony_ci default: 38162306a36Sopenharmony_ci /* timing constraints if associated */ 38262306a36Sopenharmony_ci if (!iwl_is_associated_ctx(ctx)) 38362306a36Sopenharmony_ci continue; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci case RXON_DEV_TYPE_CP: 38662306a36Sopenharmony_ci case RXON_DEV_TYPE_2STA: 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * These seem to always have timers for TBTT 38962306a36Sopenharmony_ci * active in uCode even when not associated yet. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci limits[n_active++] = ctx->beacon_int ?: IWL_PASSIVE_DWELL_BASE; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci switch (n_active) { 39862306a36Sopenharmony_ci case 0: 39962306a36Sopenharmony_ci return dwell_time; 40062306a36Sopenharmony_ci case 2: 40162306a36Sopenharmony_ci limit = (limits[1] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; 40262306a36Sopenharmony_ci limit /= 2; 40362306a36Sopenharmony_ci dwell_time = min(limit, dwell_time); 40462306a36Sopenharmony_ci fallthrough; 40562306a36Sopenharmony_ci case 1: 40662306a36Sopenharmony_ci limit = (limits[0] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; 40762306a36Sopenharmony_ci limit /= n_active; 40862306a36Sopenharmony_ci return min(limit, dwell_time); 40962306a36Sopenharmony_ci default: 41062306a36Sopenharmony_ci WARN_ON_ONCE(1); 41162306a36Sopenharmony_ci return dwell_time; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, 41662306a36Sopenharmony_ci enum nl80211_band band) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci u16 passive = (band == NL80211_BAND_2GHZ) ? 41962306a36Sopenharmony_ci IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 : 42062306a36Sopenharmony_ci IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return iwl_limit_dwell(priv, passive); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/* Return valid, unused, channel for a passive scan to reset the RF */ 42662306a36Sopenharmony_cistatic u8 iwl_get_single_channel_number(struct iwl_priv *priv, 42762306a36Sopenharmony_ci enum nl80211_band band) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct ieee80211_supported_band *sband = priv->hw->wiphy->bands[band]; 43062306a36Sopenharmony_ci struct iwl_rxon_context *ctx; 43162306a36Sopenharmony_ci int i; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 43462306a36Sopenharmony_ci bool busy = false; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci for_each_context(priv, ctx) { 43762306a36Sopenharmony_ci busy = sband->channels[i].hw_value == 43862306a36Sopenharmony_ci le16_to_cpu(ctx->staging.channel); 43962306a36Sopenharmony_ci if (busy) 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (busy) 44462306a36Sopenharmony_ci continue; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (!(sband->channels[i].flags & IEEE80211_CHAN_DISABLED)) 44762306a36Sopenharmony_ci return sband->channels[i].hw_value; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic int iwl_get_channel_for_reset_scan(struct iwl_priv *priv, 45462306a36Sopenharmony_ci struct ieee80211_vif *vif, 45562306a36Sopenharmony_ci enum nl80211_band band, 45662306a36Sopenharmony_ci struct iwl_scan_channel *scan_ch) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci const struct ieee80211_supported_band *sband; 45962306a36Sopenharmony_ci u16 channel; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci sband = iwl_get_hw_mode(priv, band); 46262306a36Sopenharmony_ci if (!sband) { 46362306a36Sopenharmony_ci IWL_ERR(priv, "invalid band\n"); 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci channel = iwl_get_single_channel_number(priv, band); 46862306a36Sopenharmony_ci if (channel) { 46962306a36Sopenharmony_ci scan_ch->channel = cpu_to_le16(channel); 47062306a36Sopenharmony_ci scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; 47162306a36Sopenharmony_ci scan_ch->active_dwell = 47262306a36Sopenharmony_ci cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); 47362306a36Sopenharmony_ci scan_ch->passive_dwell = 47462306a36Sopenharmony_ci cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); 47562306a36Sopenharmony_ci /* Set txpower levels to defaults */ 47662306a36Sopenharmony_ci scan_ch->dsp_atten = 110; 47762306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 47862306a36Sopenharmony_ci scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; 47962306a36Sopenharmony_ci else 48062306a36Sopenharmony_ci scan_ch->tx_gain = ((1 << 5) | (5 << 3)); 48162306a36Sopenharmony_ci return 1; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci IWL_ERR(priv, "no valid channel found\n"); 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic int iwl_get_channels_for_scan(struct iwl_priv *priv, 48962306a36Sopenharmony_ci struct ieee80211_vif *vif, 49062306a36Sopenharmony_ci enum nl80211_band band, 49162306a36Sopenharmony_ci u8 is_active, u8 n_probes, 49262306a36Sopenharmony_ci struct iwl_scan_channel *scan_ch) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct ieee80211_channel *chan; 49562306a36Sopenharmony_ci const struct ieee80211_supported_band *sband; 49662306a36Sopenharmony_ci u16 passive_dwell = 0; 49762306a36Sopenharmony_ci u16 active_dwell = 0; 49862306a36Sopenharmony_ci int added, i; 49962306a36Sopenharmony_ci u16 channel; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci sband = iwl_get_hw_mode(priv, band); 50262306a36Sopenharmony_ci if (!sband) 50362306a36Sopenharmony_ci return 0; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); 50662306a36Sopenharmony_ci passive_dwell = iwl_get_passive_dwell_time(priv, band); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (passive_dwell <= active_dwell) 50962306a36Sopenharmony_ci passive_dwell = active_dwell + 1; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { 51262306a36Sopenharmony_ci chan = priv->scan_request->channels[i]; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (chan->band != band) 51562306a36Sopenharmony_ci continue; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci channel = chan->hw_value; 51862306a36Sopenharmony_ci scan_ch->channel = cpu_to_le16(channel); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!is_active || (chan->flags & IEEE80211_CHAN_NO_IR)) 52162306a36Sopenharmony_ci scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; 52262306a36Sopenharmony_ci else 52362306a36Sopenharmony_ci scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (n_probes) 52662306a36Sopenharmony_ci scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci scan_ch->active_dwell = cpu_to_le16(active_dwell); 52962306a36Sopenharmony_ci scan_ch->passive_dwell = cpu_to_le16(passive_dwell); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Set txpower levels to defaults */ 53262306a36Sopenharmony_ci scan_ch->dsp_atten = 110; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* NOTE: if we were doing 6Mb OFDM for scans we'd use 53562306a36Sopenharmony_ci * power level: 53662306a36Sopenharmony_ci * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 53962306a36Sopenharmony_ci scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; 54062306a36Sopenharmony_ci else 54162306a36Sopenharmony_ci scan_ch->tx_gain = ((1 << 5) | (5 << 3)); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n", 54462306a36Sopenharmony_ci channel, le32_to_cpu(scan_ch->type), 54562306a36Sopenharmony_ci (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? 54662306a36Sopenharmony_ci "ACTIVE" : "PASSIVE", 54762306a36Sopenharmony_ci (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? 54862306a36Sopenharmony_ci active_dwell : passive_dwell); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci scan_ch++; 55162306a36Sopenharmony_ci added++; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added); 55562306a36Sopenharmony_ci return added; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/* 55962306a36Sopenharmony_ci * iwl_fill_probe_req - fill in all required fields and IE for probe request 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_cistatic u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, 56262306a36Sopenharmony_ci const u8 *ies, int ie_len, const u8 *ssid, 56362306a36Sopenharmony_ci u8 ssid_len, int left) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci int len = 0; 56662306a36Sopenharmony_ci u8 *pos = NULL; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* Make sure there is enough space for the probe request, 56962306a36Sopenharmony_ci * two mandatory IEs and the data */ 57062306a36Sopenharmony_ci left -= 24; 57162306a36Sopenharmony_ci if (left < 0) 57262306a36Sopenharmony_ci return 0; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); 57562306a36Sopenharmony_ci eth_broadcast_addr(frame->da); 57662306a36Sopenharmony_ci memcpy(frame->sa, ta, ETH_ALEN); 57762306a36Sopenharmony_ci eth_broadcast_addr(frame->bssid); 57862306a36Sopenharmony_ci frame->seq_ctrl = 0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci len += 24; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* ...next IE... */ 58362306a36Sopenharmony_ci pos = &frame->u.probe_req.variable[0]; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* fill in our SSID IE */ 58662306a36Sopenharmony_ci left -= ssid_len + 2; 58762306a36Sopenharmony_ci if (left < 0) 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci *pos++ = WLAN_EID_SSID; 59062306a36Sopenharmony_ci *pos++ = ssid_len; 59162306a36Sopenharmony_ci if (ssid && ssid_len) { 59262306a36Sopenharmony_ci memcpy(pos, ssid, ssid_len); 59362306a36Sopenharmony_ci pos += ssid_len; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci len += ssid_len + 2; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (WARN_ON(left < ie_len)) 59962306a36Sopenharmony_ci return len; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (ies && ie_len) { 60262306a36Sopenharmony_ci memcpy(pos, ies, ie_len); 60362306a36Sopenharmony_ci len += ie_len; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return (u16)len; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct iwl_host_cmd cmd = { 61262306a36Sopenharmony_ci .id = REPLY_SCAN_CMD, 61362306a36Sopenharmony_ci .len = { sizeof(struct iwl_scan_cmd), }, 61462306a36Sopenharmony_ci }; 61562306a36Sopenharmony_ci struct iwl_scan_cmd *scan; 61662306a36Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 61762306a36Sopenharmony_ci u32 rate_flags = 0; 61862306a36Sopenharmony_ci u16 cmd_len = 0; 61962306a36Sopenharmony_ci u16 rx_chain = 0; 62062306a36Sopenharmony_ci enum nl80211_band band; 62162306a36Sopenharmony_ci u8 n_probes = 0; 62262306a36Sopenharmony_ci u8 rx_ant = priv->nvm_data->valid_rx_ant; 62362306a36Sopenharmony_ci u8 rate; 62462306a36Sopenharmony_ci bool is_active = false; 62562306a36Sopenharmony_ci int chan_mod; 62662306a36Sopenharmony_ci u8 active_chains; 62762306a36Sopenharmony_ci u8 scan_tx_antennas = priv->nvm_data->valid_tx_ant; 62862306a36Sopenharmony_ci int ret; 62962306a36Sopenharmony_ci size_t scan_cmd_size = sizeof(struct iwl_scan_cmd) + 63062306a36Sopenharmony_ci MAX_SCAN_CHANNEL * sizeof(struct iwl_scan_channel) + 63162306a36Sopenharmony_ci priv->fw->ucode_capa.max_probe_length; 63262306a36Sopenharmony_ci const u8 *ssid = NULL; 63362306a36Sopenharmony_ci u8 ssid_len = 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (WARN_ON(priv->scan_type == IWL_SCAN_NORMAL && 63662306a36Sopenharmony_ci (!priv->scan_request || 63762306a36Sopenharmony_ci priv->scan_request->n_channels > MAX_SCAN_CHANNEL))) 63862306a36Sopenharmony_ci return -EINVAL; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (vif) 64362306a36Sopenharmony_ci ctx = iwl_rxon_ctx_from_vif(vif); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (!priv->scan_cmd) { 64662306a36Sopenharmony_ci priv->scan_cmd = kmalloc(scan_cmd_size, GFP_KERNEL); 64762306a36Sopenharmony_ci if (!priv->scan_cmd) { 64862306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, 64962306a36Sopenharmony_ci "fail to allocate memory for scan\n"); 65062306a36Sopenharmony_ci return -ENOMEM; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci priv->scan_cmd_size = scan_cmd_size; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci if (priv->scan_cmd_size < scan_cmd_size) { 65562306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, 65662306a36Sopenharmony_ci "memory needed for scan grew unexpectedly\n"); 65762306a36Sopenharmony_ci return -ENOMEM; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci scan = priv->scan_cmd; 66062306a36Sopenharmony_ci memset(scan, 0, priv->scan_cmd_size); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; 66362306a36Sopenharmony_ci scan->quiet_time = IWL_ACTIVE_QUIET_TIME; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (iwl_is_any_associated(priv)) { 66662306a36Sopenharmony_ci u16 interval = 0; 66762306a36Sopenharmony_ci u32 extra; 66862306a36Sopenharmony_ci u32 suspend_time = 100; 66962306a36Sopenharmony_ci u32 scan_suspend_time = 100; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); 67262306a36Sopenharmony_ci switch (priv->scan_type) { 67362306a36Sopenharmony_ci case IWL_SCAN_RADIO_RESET: 67462306a36Sopenharmony_ci interval = 0; 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci case IWL_SCAN_NORMAL: 67762306a36Sopenharmony_ci interval = vif->bss_conf.beacon_int; 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci scan->suspend_time = 0; 68262306a36Sopenharmony_ci scan->max_out_time = cpu_to_le32(200 * 1024); 68362306a36Sopenharmony_ci if (!interval) 68462306a36Sopenharmony_ci interval = suspend_time; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci extra = (suspend_time / interval) << 22; 68762306a36Sopenharmony_ci scan_suspend_time = (extra | 68862306a36Sopenharmony_ci ((suspend_time % interval) * 1024)); 68962306a36Sopenharmony_ci scan->suspend_time = cpu_to_le32(scan_suspend_time); 69062306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", 69162306a36Sopenharmony_ci scan_suspend_time, interval); 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci switch (priv->scan_type) { 69562306a36Sopenharmony_ci case IWL_SCAN_RADIO_RESET: 69662306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); 69762306a36Sopenharmony_ci /* 69862306a36Sopenharmony_ci * Override quiet time as firmware checks that active 69962306a36Sopenharmony_ci * dwell is >= quiet; since we use passive scan it'll 70062306a36Sopenharmony_ci * not actually be used. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci scan->quiet_time = cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); 70362306a36Sopenharmony_ci break; 70462306a36Sopenharmony_ci case IWL_SCAN_NORMAL: 70562306a36Sopenharmony_ci if (priv->scan_request->n_ssids) { 70662306a36Sopenharmony_ci int i, p = 0; 70762306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); 70862306a36Sopenharmony_ci /* 70962306a36Sopenharmony_ci * The highest priority SSID is inserted to the 71062306a36Sopenharmony_ci * probe request template. 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_ci ssid_len = priv->scan_request->ssids[0].ssid_len; 71362306a36Sopenharmony_ci ssid = priv->scan_request->ssids[0].ssid; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* 71662306a36Sopenharmony_ci * Invert the order of ssids, the firmware will invert 71762306a36Sopenharmony_ci * it back. 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_ci for (i = priv->scan_request->n_ssids - 1; i >= 1; i--) { 72062306a36Sopenharmony_ci scan->direct_scan[p].id = WLAN_EID_SSID; 72162306a36Sopenharmony_ci scan->direct_scan[p].len = 72262306a36Sopenharmony_ci priv->scan_request->ssids[i].ssid_len; 72362306a36Sopenharmony_ci memcpy(scan->direct_scan[p].ssid, 72462306a36Sopenharmony_ci priv->scan_request->ssids[i].ssid, 72562306a36Sopenharmony_ci priv->scan_request->ssids[i].ssid_len); 72662306a36Sopenharmony_ci n_probes++; 72762306a36Sopenharmony_ci p++; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci is_active = true; 73062306a36Sopenharmony_ci } else 73162306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); 73262306a36Sopenharmony_ci break; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; 73662306a36Sopenharmony_ci scan->tx_cmd.sta_id = ctx->bcast_sta_id; 73762306a36Sopenharmony_ci scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci switch (priv->scan_band) { 74062306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 74162306a36Sopenharmony_ci scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; 74262306a36Sopenharmony_ci chan_mod = le32_to_cpu( 74362306a36Sopenharmony_ci priv->contexts[IWL_RXON_CTX_BSS].active.flags & 74462306a36Sopenharmony_ci RXON_FLG_CHANNEL_MODE_MSK) 74562306a36Sopenharmony_ci >> RXON_FLG_CHANNEL_MODE_POS; 74662306a36Sopenharmony_ci if ((priv->scan_request && priv->scan_request->no_cck) || 74762306a36Sopenharmony_ci chan_mod == CHANNEL_MODE_PURE_40) { 74862306a36Sopenharmony_ci rate = IWL_RATE_6M_PLCP; 74962306a36Sopenharmony_ci } else { 75062306a36Sopenharmony_ci rate = IWL_RATE_1M_PLCP; 75162306a36Sopenharmony_ci rate_flags = RATE_MCS_CCK_MSK; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci /* 75462306a36Sopenharmony_ci * Internal scans are passive, so we can indiscriminately set 75562306a36Sopenharmony_ci * the BT ignore flag on 2.4 GHz since it applies to TX only. 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci if (priv->lib->bt_params && 75862306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist) 75962306a36Sopenharmony_ci scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; 76062306a36Sopenharmony_ci break; 76162306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 76262306a36Sopenharmony_ci rate = IWL_RATE_6M_PLCP; 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci default: 76562306a36Sopenharmony_ci IWL_WARN(priv, "Invalid scan band\n"); 76662306a36Sopenharmony_ci return -EIO; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* 77062306a36Sopenharmony_ci * If active scanning is requested but a certain channel is 77162306a36Sopenharmony_ci * marked passive, we can do active scanning if we detect 77262306a36Sopenharmony_ci * transmissions. 77362306a36Sopenharmony_ci * 77462306a36Sopenharmony_ci * There is an issue with some firmware versions that triggers 77562306a36Sopenharmony_ci * a sysassert on a "good CRC threshold" of zero (== disabled), 77662306a36Sopenharmony_ci * on a radar channel even though this means that we should NOT 77762306a36Sopenharmony_ci * send probes. 77862306a36Sopenharmony_ci * 77962306a36Sopenharmony_ci * The "good CRC threshold" is the number of frames that we 78062306a36Sopenharmony_ci * need to receive during our dwell time on a channel before 78162306a36Sopenharmony_ci * sending out probes -- setting this to a huge value will 78262306a36Sopenharmony_ci * mean we never reach it, but at the same time work around 78362306a36Sopenharmony_ci * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER 78462306a36Sopenharmony_ci * here instead of IWL_GOOD_CRC_TH_DISABLED. 78562306a36Sopenharmony_ci * 78662306a36Sopenharmony_ci * This was fixed in later versions along with some other 78762306a36Sopenharmony_ci * scan changes, and the threshold behaves as a flag in those 78862306a36Sopenharmony_ci * versions. 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_ci if (priv->new_scan_threshold_behaviour) 79162306a36Sopenharmony_ci scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : 79262306a36Sopenharmony_ci IWL_GOOD_CRC_TH_DISABLED; 79362306a36Sopenharmony_ci else 79462306a36Sopenharmony_ci scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : 79562306a36Sopenharmony_ci IWL_GOOD_CRC_TH_NEVER; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci band = priv->scan_band; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (band == NL80211_BAND_2GHZ && 80062306a36Sopenharmony_ci priv->lib->bt_params && 80162306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist) { 80262306a36Sopenharmony_ci /* transmit 2.4 GHz probes only on first antenna */ 80362306a36Sopenharmony_ci scan_tx_antennas = first_antenna(scan_tx_antennas); 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, 80762306a36Sopenharmony_ci priv->scan_tx_ant[band], 80862306a36Sopenharmony_ci scan_tx_antennas); 80962306a36Sopenharmony_ci rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]); 81062306a36Sopenharmony_ci scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* 81362306a36Sopenharmony_ci * In power save mode while associated use one chain, 81462306a36Sopenharmony_ci * otherwise use all chains 81562306a36Sopenharmony_ci */ 81662306a36Sopenharmony_ci if (test_bit(STATUS_POWER_PMI, &priv->status) && 81762306a36Sopenharmony_ci !(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { 81862306a36Sopenharmony_ci /* rx_ant has been set to all valid chains previously */ 81962306a36Sopenharmony_ci active_chains = rx_ant & 82062306a36Sopenharmony_ci ((u8)(priv->chain_noise_data.active_chains)); 82162306a36Sopenharmony_ci if (!active_chains) 82262306a36Sopenharmony_ci active_chains = rx_ant; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", 82562306a36Sopenharmony_ci priv->chain_noise_data.active_chains); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci rx_ant = first_antenna(active_chains); 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci if (priv->lib->bt_params && 83062306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist && 83162306a36Sopenharmony_ci priv->bt_full_concurrent) { 83262306a36Sopenharmony_ci /* operated as 1x1 in full concurrency mode */ 83362306a36Sopenharmony_ci rx_ant = first_antenna(rx_ant); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* MIMO is not used here, but value is required */ 83762306a36Sopenharmony_ci rx_chain |= 83862306a36Sopenharmony_ci priv->nvm_data->valid_rx_ant << RXON_RX_CHAIN_VALID_POS; 83962306a36Sopenharmony_ci rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; 84062306a36Sopenharmony_ci rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; 84162306a36Sopenharmony_ci rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; 84262306a36Sopenharmony_ci scan->rx_chain = cpu_to_le16(rx_chain); 84362306a36Sopenharmony_ci switch (priv->scan_type) { 84462306a36Sopenharmony_ci case IWL_SCAN_NORMAL: 84562306a36Sopenharmony_ci cmd_len = iwl_fill_probe_req( 84662306a36Sopenharmony_ci (struct ieee80211_mgmt *)scan->data, 84762306a36Sopenharmony_ci vif->addr, 84862306a36Sopenharmony_ci priv->scan_request->ie, 84962306a36Sopenharmony_ci priv->scan_request->ie_len, 85062306a36Sopenharmony_ci ssid, ssid_len, 85162306a36Sopenharmony_ci scan_cmd_size - sizeof(*scan)); 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci case IWL_SCAN_RADIO_RESET: 85462306a36Sopenharmony_ci /* use bcast addr, will not be transmitted but must be valid */ 85562306a36Sopenharmony_ci cmd_len = iwl_fill_probe_req( 85662306a36Sopenharmony_ci (struct ieee80211_mgmt *)scan->data, 85762306a36Sopenharmony_ci iwl_bcast_addr, NULL, 0, 85862306a36Sopenharmony_ci NULL, 0, 85962306a36Sopenharmony_ci scan_cmd_size - sizeof(*scan)); 86062306a36Sopenharmony_ci break; 86162306a36Sopenharmony_ci default: 86262306a36Sopenharmony_ci BUG(); 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci scan->tx_cmd.len = cpu_to_le16(cmd_len); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | 86762306a36Sopenharmony_ci RXON_FILTER_BCON_AWARE_MSK); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci switch (priv->scan_type) { 87062306a36Sopenharmony_ci case IWL_SCAN_RADIO_RESET: 87162306a36Sopenharmony_ci scan->channel_count = 87262306a36Sopenharmony_ci iwl_get_channel_for_reset_scan(priv, vif, band, 87362306a36Sopenharmony_ci (void *)&scan->data[cmd_len]); 87462306a36Sopenharmony_ci break; 87562306a36Sopenharmony_ci case IWL_SCAN_NORMAL: 87662306a36Sopenharmony_ci scan->channel_count = 87762306a36Sopenharmony_ci iwl_get_channels_for_scan(priv, vif, band, 87862306a36Sopenharmony_ci is_active, n_probes, 87962306a36Sopenharmony_ci (void *)&scan->data[cmd_len]); 88062306a36Sopenharmony_ci break; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (scan->channel_count == 0) { 88462306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); 88562306a36Sopenharmony_ci return -EIO; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) + 88962306a36Sopenharmony_ci scan->channel_count * sizeof(struct iwl_scan_channel); 89062306a36Sopenharmony_ci cmd.data[0] = scan; 89162306a36Sopenharmony_ci cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; 89262306a36Sopenharmony_ci scan->len = cpu_to_le16(cmd.len[0]); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* set scan bit here for PAN params */ 89562306a36Sopenharmony_ci set_bit(STATUS_SCAN_HW, &priv->status); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci ret = iwlagn_set_pan_params(priv); 89862306a36Sopenharmony_ci if (ret) { 89962306a36Sopenharmony_ci clear_bit(STATUS_SCAN_HW, &priv->status); 90062306a36Sopenharmony_ci return ret; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &cmd); 90462306a36Sopenharmony_ci if (ret) { 90562306a36Sopenharmony_ci clear_bit(STATUS_SCAN_HW, &priv->status); 90662306a36Sopenharmony_ci iwlagn_set_pan_params(priv); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci return ret; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_civoid iwl_init_scan_params(struct iwl_priv *priv) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci u8 ant_idx = fls(priv->nvm_data->valid_tx_ant) - 1; 91562306a36Sopenharmony_ci if (!priv->scan_tx_ant[NL80211_BAND_5GHZ]) 91662306a36Sopenharmony_ci priv->scan_tx_ant[NL80211_BAND_5GHZ] = ant_idx; 91762306a36Sopenharmony_ci if (!priv->scan_tx_ant[NL80211_BAND_2GHZ]) 91862306a36Sopenharmony_ci priv->scan_tx_ant[NL80211_BAND_2GHZ] = ant_idx; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ciint __must_check iwl_scan_initiate(struct iwl_priv *priv, 92262306a36Sopenharmony_ci struct ieee80211_vif *vif, 92362306a36Sopenharmony_ci enum iwl_scan_type scan_type, 92462306a36Sopenharmony_ci enum nl80211_band band) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci int ret; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci cancel_delayed_work(&priv->scan_check); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (!iwl_is_ready_rf(priv)) { 93362306a36Sopenharmony_ci IWL_WARN(priv, "Request scan called when driver not ready.\n"); 93462306a36Sopenharmony_ci return -EIO; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (test_bit(STATUS_SCAN_HW, &priv->status)) { 93862306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, 93962306a36Sopenharmony_ci "Multiple concurrent scan requests in parallel.\n"); 94062306a36Sopenharmony_ci return -EBUSY; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { 94462306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n"); 94562306a36Sopenharmony_ci return -EBUSY; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", 94962306a36Sopenharmony_ci scan_type == IWL_SCAN_NORMAL ? "" : 95062306a36Sopenharmony_ci "internal short "); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci set_bit(STATUS_SCANNING, &priv->status); 95362306a36Sopenharmony_ci priv->scan_type = scan_type; 95462306a36Sopenharmony_ci priv->scan_start = jiffies; 95562306a36Sopenharmony_ci priv->scan_band = band; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci ret = iwlagn_request_scan(priv, vif); 95862306a36Sopenharmony_ci if (ret) { 95962306a36Sopenharmony_ci clear_bit(STATUS_SCANNING, &priv->status); 96062306a36Sopenharmony_ci priv->scan_type = IWL_SCAN_NORMAL; 96162306a36Sopenharmony_ci return ret; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci queue_delayed_work(priv->workqueue, &priv->scan_check, 96562306a36Sopenharmony_ci IWL_SCAN_CHECK_WATCHDOG); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci return 0; 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci/* 97262306a36Sopenharmony_ci * internal short scan, this function should only been called while associated. 97362306a36Sopenharmony_ci * It will reset and tune the radio to prevent possible RF related problem 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_civoid iwl_internal_short_hw_scan(struct iwl_priv *priv) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci queue_work(priv->workqueue, &priv->start_internal_scan); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic void iwl_bg_start_internal_scan(struct work_struct *work) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct iwl_priv *priv = 98362306a36Sopenharmony_ci container_of(work, struct iwl_priv, start_internal_scan); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Start internal scan\n"); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci mutex_lock(&priv->mutex); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (priv->scan_type == IWL_SCAN_RADIO_RESET) { 99062306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n"); 99162306a36Sopenharmony_ci goto unlock; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (test_bit(STATUS_SCANNING, &priv->status)) { 99562306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); 99662306a36Sopenharmony_ci goto unlock; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band)) 100062306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n"); 100162306a36Sopenharmony_ci unlock: 100262306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic void iwl_bg_scan_check(struct work_struct *data) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct iwl_priv *priv = 100862306a36Sopenharmony_ci container_of(data, struct iwl_priv, scan_check.work); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Scan check work\n"); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* Since we are here firmware does not finish scan and 101362306a36Sopenharmony_ci * most likely is in bad shape, so we don't bother to 101462306a36Sopenharmony_ci * send abort command, just force scan complete to mac80211 */ 101562306a36Sopenharmony_ci mutex_lock(&priv->mutex); 101662306a36Sopenharmony_ci iwl_force_scan_end(priv); 101762306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic void iwl_bg_abort_scan(struct work_struct *work) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci IWL_DEBUG_SCAN(priv, "Abort scan work\n"); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci /* We keep scan_check work queued in case when firmware will not 102762306a36Sopenharmony_ci * report back scan completed notification */ 102862306a36Sopenharmony_ci mutex_lock(&priv->mutex); 102962306a36Sopenharmony_ci iwl_scan_cancel_timeout(priv, 200); 103062306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic void iwl_bg_scan_completed(struct work_struct *work) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct iwl_priv *priv = 103662306a36Sopenharmony_ci container_of(work, struct iwl_priv, scan_completed); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci mutex_lock(&priv->mutex); 103962306a36Sopenharmony_ci iwl_process_scan_complete(priv); 104062306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_civoid iwl_setup_scan_deferred_work(struct iwl_priv *priv) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); 104662306a36Sopenharmony_ci INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan); 104762306a36Sopenharmony_ci INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan); 104862306a36Sopenharmony_ci INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check); 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_civoid iwl_cancel_scan_deferred_work(struct iwl_priv *priv) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci cancel_work_sync(&priv->start_internal_scan); 105462306a36Sopenharmony_ci cancel_work_sync(&priv->abort_scan); 105562306a36Sopenharmony_ci cancel_work_sync(&priv->scan_completed); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (cancel_delayed_work_sync(&priv->scan_check)) { 105862306a36Sopenharmony_ci mutex_lock(&priv->mutex); 105962306a36Sopenharmony_ci iwl_force_scan_end(priv); 106062306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci} 1063