18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Scan implementation for ST-Ericsson CW1200 mac80211 drivers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010, ST-Ericsson 68c2ecf20Sopenharmony_ci * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/sched.h> 108c2ecf20Sopenharmony_ci#include "cw1200.h" 118c2ecf20Sopenharmony_ci#include "scan.h" 128c2ecf20Sopenharmony_ci#include "sta.h" 138c2ecf20Sopenharmony_ci#include "pm.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic void cw1200_scan_restart_delayed(struct cw1200_common *priv); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci int ret, i; 208c2ecf20Sopenharmony_ci int tmo = 2000; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci switch (priv->join_status) { 238c2ecf20Sopenharmony_ci case CW1200_JOIN_STATUS_PRE_STA: 248c2ecf20Sopenharmony_ci case CW1200_JOIN_STATUS_JOINING: 258c2ecf20Sopenharmony_ci return -EBUSY; 268c2ecf20Sopenharmony_ci default: 278c2ecf20Sopenharmony_ci break; 288c2ecf20Sopenharmony_ci } 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n", 318c2ecf20Sopenharmony_ci scan->type, scan->num_channels, scan->flags); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci for (i = 0; i < scan->num_channels; ++i) 348c2ecf20Sopenharmony_ci tmo += scan->ch[i].max_chan_time + 10; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->clear_recent_scan_work); 378c2ecf20Sopenharmony_ci atomic_set(&priv->scan.in_progress, 1); 388c2ecf20Sopenharmony_ci atomic_set(&priv->recent_scan, 1); 398c2ecf20Sopenharmony_ci cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo)); 408c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, &priv->scan.timeout, 418c2ecf20Sopenharmony_ci msecs_to_jiffies(tmo)); 428c2ecf20Sopenharmony_ci ret = wsm_scan(priv, scan); 438c2ecf20Sopenharmony_ci if (ret) { 448c2ecf20Sopenharmony_ci atomic_set(&priv->scan.in_progress, 0); 458c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->scan.timeout); 468c2ecf20Sopenharmony_ci cw1200_scan_restart_delayed(priv); 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci return ret; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciint cw1200_hw_scan(struct ieee80211_hw *hw, 528c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 538c2ecf20Sopenharmony_ci struct ieee80211_scan_request *hw_req) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct cw1200_common *priv = hw->priv; 568c2ecf20Sopenharmony_ci struct cfg80211_scan_request *req = &hw_req->req; 578c2ecf20Sopenharmony_ci struct wsm_template_frame frame = { 588c2ecf20Sopenharmony_ci .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, 598c2ecf20Sopenharmony_ci }; 608c2ecf20Sopenharmony_ci int i, ret; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (!priv->vif) 638c2ecf20Sopenharmony_ci return -EINVAL; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* Scan when P2P_GO corrupt firmware MiniAP mode */ 668c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_AP) 678c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (req->n_ssids == 1 && !req->ssids[0].ssid_len) 708c2ecf20Sopenharmony_ci req->n_ssids = 0; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", 738c2ecf20Sopenharmony_ci req->n_ssids); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) 768c2ecf20Sopenharmony_ci return -EINVAL; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, 798c2ecf20Sopenharmony_ci req->ie_len); 808c2ecf20Sopenharmony_ci if (!frame.skb) 818c2ecf20Sopenharmony_ci return -ENOMEM; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (req->ie_len) 848c2ecf20Sopenharmony_ci skb_put_data(frame.skb, req->ie, req->ie_len); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* will be unlocked in cw1200_scan_work() */ 878c2ecf20Sopenharmony_ci down(&priv->scan.lock); 888c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = wsm_set_template_frame(priv, &frame); 918c2ecf20Sopenharmony_ci if (!ret) { 928c2ecf20Sopenharmony_ci /* Host want to be the probe responder. */ 938c2ecf20Sopenharmony_ci ret = wsm_set_probe_responder(priv, true); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci if (ret) { 968c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 978c2ecf20Sopenharmony_ci up(&priv->scan.lock); 988c2ecf20Sopenharmony_ci dev_kfree_skb(frame.skb); 998c2ecf20Sopenharmony_ci return ret; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci BUG_ON(priv->scan.req); 1058c2ecf20Sopenharmony_ci priv->scan.req = req; 1068c2ecf20Sopenharmony_ci priv->scan.n_ssids = 0; 1078c2ecf20Sopenharmony_ci priv->scan.status = 0; 1088c2ecf20Sopenharmony_ci priv->scan.begin = &req->channels[0]; 1098c2ecf20Sopenharmony_ci priv->scan.curr = priv->scan.begin; 1108c2ecf20Sopenharmony_ci priv->scan.end = &req->channels[req->n_channels]; 1118c2ecf20Sopenharmony_ci priv->scan.output_power = priv->output_power; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci for (i = 0; i < req->n_ssids; ++i) { 1148c2ecf20Sopenharmony_ci struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids]; 1158c2ecf20Sopenharmony_ci memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); 1168c2ecf20Sopenharmony_ci dst->length = req->ssids[i].ssid_len; 1178c2ecf20Sopenharmony_ci ++priv->scan.n_ssids; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 1218c2ecf20Sopenharmony_ci dev_kfree_skb(frame.skb); 1228c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->scan.work); 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_civoid cw1200_scan_work(struct work_struct *work) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct cw1200_common *priv = container_of(work, struct cw1200_common, 1298c2ecf20Sopenharmony_ci scan.work); 1308c2ecf20Sopenharmony_ci struct ieee80211_channel **it; 1318c2ecf20Sopenharmony_ci struct wsm_scan scan = { 1328c2ecf20Sopenharmony_ci .type = WSM_SCAN_TYPE_FOREGROUND, 1338c2ecf20Sopenharmony_ci .flags = WSM_SCAN_FLAG_SPLIT_METHOD, 1348c2ecf20Sopenharmony_ci }; 1358c2ecf20Sopenharmony_ci bool first_run = (priv->scan.begin == priv->scan.curr && 1368c2ecf20Sopenharmony_ci priv->scan.begin != priv->scan.end); 1378c2ecf20Sopenharmony_ci int i; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (first_run) { 1408c2ecf20Sopenharmony_ci /* Firmware gets crazy if scan request is sent 1418c2ecf20Sopenharmony_ci * when STA is joined but not yet associated. 1428c2ecf20Sopenharmony_ci * Force unjoin in this case. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci if (cancel_delayed_work_sync(&priv->join_timeout) > 0) 1458c2ecf20Sopenharmony_ci cw1200_join_timeout(&priv->join_timeout.work); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (first_run) { 1518c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_STA && 1528c2ecf20Sopenharmony_ci !(priv->powersave_mode.mode & WSM_PSM_PS)) { 1538c2ecf20Sopenharmony_ci struct wsm_set_pm pm = priv->powersave_mode; 1548c2ecf20Sopenharmony_ci pm.mode = WSM_PSM_PS; 1558c2ecf20Sopenharmony_ci cw1200_set_pm(priv, &pm); 1568c2ecf20Sopenharmony_ci } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { 1578c2ecf20Sopenharmony_ci /* FW bug: driver has to restart p2p-dev mode 1588c2ecf20Sopenharmony_ci * after scan 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci cw1200_disable_listening(priv); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { 1658c2ecf20Sopenharmony_ci struct cfg80211_scan_info info = { 1668c2ecf20Sopenharmony_ci .aborted = priv->scan.status ? 1 : 0, 1678c2ecf20Sopenharmony_ci }; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (priv->scan.output_power != priv->output_power) 1708c2ecf20Sopenharmony_ci wsm_set_output_power(priv, priv->output_power * 10); 1718c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_STA && 1728c2ecf20Sopenharmony_ci !(priv->powersave_mode.mode & WSM_PSM_PS)) 1738c2ecf20Sopenharmony_ci cw1200_set_pm(priv, &priv->powersave_mode); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (priv->scan.status < 0) 1768c2ecf20Sopenharmony_ci wiphy_warn(priv->hw->wiphy, 1778c2ecf20Sopenharmony_ci "[SCAN] Scan failed (%d).\n", 1788c2ecf20Sopenharmony_ci priv->scan.status); 1798c2ecf20Sopenharmony_ci else if (priv->scan.req) 1808c2ecf20Sopenharmony_ci wiphy_dbg(priv->hw->wiphy, 1818c2ecf20Sopenharmony_ci "[SCAN] Scan completed.\n"); 1828c2ecf20Sopenharmony_ci else 1838c2ecf20Sopenharmony_ci wiphy_dbg(priv->hw->wiphy, 1848c2ecf20Sopenharmony_ci "[SCAN] Scan canceled.\n"); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci priv->scan.req = NULL; 1878c2ecf20Sopenharmony_ci cw1200_scan_restart_delayed(priv); 1888c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 1898c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 1908c2ecf20Sopenharmony_ci ieee80211_scan_completed(priv->hw, &info); 1918c2ecf20Sopenharmony_ci up(&priv->scan.lock); 1928c2ecf20Sopenharmony_ci return; 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci struct ieee80211_channel *first = *priv->scan.curr; 1958c2ecf20Sopenharmony_ci for (it = priv->scan.curr + 1, i = 1; 1968c2ecf20Sopenharmony_ci it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; 1978c2ecf20Sopenharmony_ci ++it, ++i) { 1988c2ecf20Sopenharmony_ci if ((*it)->band != first->band) 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci if (((*it)->flags ^ first->flags) & 2018c2ecf20Sopenharmony_ci IEEE80211_CHAN_NO_IR) 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci if (!(first->flags & IEEE80211_CHAN_NO_IR) && 2048c2ecf20Sopenharmony_ci (*it)->max_power != first->max_power) 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci scan.band = first->band; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (priv->scan.req->no_cck) 2108c2ecf20Sopenharmony_ci scan.max_tx_rate = WSM_TRANSMIT_RATE_6; 2118c2ecf20Sopenharmony_ci else 2128c2ecf20Sopenharmony_ci scan.max_tx_rate = WSM_TRANSMIT_RATE_1; 2138c2ecf20Sopenharmony_ci scan.num_probes = 2148c2ecf20Sopenharmony_ci (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2; 2158c2ecf20Sopenharmony_ci scan.num_ssids = priv->scan.n_ssids; 2168c2ecf20Sopenharmony_ci scan.ssids = &priv->scan.ssids[0]; 2178c2ecf20Sopenharmony_ci scan.num_channels = it - priv->scan.curr; 2188c2ecf20Sopenharmony_ci /* TODO: Is it optimal? */ 2198c2ecf20Sopenharmony_ci scan.probe_delay = 100; 2208c2ecf20Sopenharmony_ci /* It is not stated in WSM specification, however 2218c2ecf20Sopenharmony_ci * FW team says that driver may not use FG scan 2228c2ecf20Sopenharmony_ci * when joined. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_STA) { 2258c2ecf20Sopenharmony_ci scan.type = WSM_SCAN_TYPE_BACKGROUND; 2268c2ecf20Sopenharmony_ci scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci scan.ch = kcalloc(it - priv->scan.curr, 2298c2ecf20Sopenharmony_ci sizeof(struct wsm_scan_ch), 2308c2ecf20Sopenharmony_ci GFP_KERNEL); 2318c2ecf20Sopenharmony_ci if (!scan.ch) { 2328c2ecf20Sopenharmony_ci priv->scan.status = -ENOMEM; 2338c2ecf20Sopenharmony_ci goto fail; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci for (i = 0; i < scan.num_channels; ++i) { 2368c2ecf20Sopenharmony_ci scan.ch[i].number = priv->scan.curr[i]->hw_value; 2378c2ecf20Sopenharmony_ci if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) { 2388c2ecf20Sopenharmony_ci scan.ch[i].min_chan_time = 50; 2398c2ecf20Sopenharmony_ci scan.ch[i].max_chan_time = 100; 2408c2ecf20Sopenharmony_ci } else { 2418c2ecf20Sopenharmony_ci scan.ch[i].min_chan_time = 10; 2428c2ecf20Sopenharmony_ci scan.ch[i].max_chan_time = 25; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci if (!(first->flags & IEEE80211_CHAN_NO_IR) && 2468c2ecf20Sopenharmony_ci priv->scan.output_power != first->max_power) { 2478c2ecf20Sopenharmony_ci priv->scan.output_power = first->max_power; 2488c2ecf20Sopenharmony_ci wsm_set_output_power(priv, 2498c2ecf20Sopenharmony_ci priv->scan.output_power * 10); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci priv->scan.status = cw1200_scan_start(priv, &scan); 2528c2ecf20Sopenharmony_ci kfree(scan.ch); 2538c2ecf20Sopenharmony_ci if (priv->scan.status) 2548c2ecf20Sopenharmony_ci goto fail; 2558c2ecf20Sopenharmony_ci priv->scan.curr = it; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 2588c2ecf20Sopenharmony_ci return; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cifail: 2618c2ecf20Sopenharmony_ci priv->scan.curr = priv->scan.end; 2628c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 2638c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->scan.work); 2648c2ecf20Sopenharmony_ci return; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void cw1200_scan_restart_delayed(struct cw1200_common *priv) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci /* FW bug: driver has to restart p2p-dev mode after scan. */ 2708c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { 2718c2ecf20Sopenharmony_ci cw1200_enable_listening(priv); 2728c2ecf20Sopenharmony_ci cw1200_update_filtering(priv); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (priv->delayed_unjoin) { 2768c2ecf20Sopenharmony_ci priv->delayed_unjoin = false; 2778c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 2788c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 2798c2ecf20Sopenharmony_ci } else if (priv->delayed_link_loss) { 2808c2ecf20Sopenharmony_ci wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n"); 2818c2ecf20Sopenharmony_ci priv->delayed_link_loss = 0; 2828c2ecf20Sopenharmony_ci cw1200_cqm_bssloss_sm(priv, 1, 0, 0); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void cw1200_scan_complete(struct cw1200_common *priv) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ); 2898c2ecf20Sopenharmony_ci if (priv->scan.direct_probe) { 2908c2ecf20Sopenharmony_ci wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n"); 2918c2ecf20Sopenharmony_ci cw1200_scan_restart_delayed(priv); 2928c2ecf20Sopenharmony_ci priv->scan.direct_probe = 0; 2938c2ecf20Sopenharmony_ci up(&priv->scan.lock); 2948c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 2958c2ecf20Sopenharmony_ci } else { 2968c2ecf20Sopenharmony_ci cw1200_scan_work(&priv->scan.work); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_civoid cw1200_scan_failed_cb(struct cw1200_common *priv) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) 3038c2ecf20Sopenharmony_ci /* STA is stopped. */ 3048c2ecf20Sopenharmony_ci return; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { 3078c2ecf20Sopenharmony_ci priv->scan.status = -EIO; 3088c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_civoid cw1200_scan_complete_cb(struct cw1200_common *priv, 3148c2ecf20Sopenharmony_ci struct wsm_scan_complete *arg) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) 3178c2ecf20Sopenharmony_ci /* STA is stopped. */ 3188c2ecf20Sopenharmony_ci return; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { 3218c2ecf20Sopenharmony_ci priv->scan.status = 1; 3228c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_civoid cw1200_clear_recent_scan_work(struct work_struct *work) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct cw1200_common *priv = 3298c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, 3308c2ecf20Sopenharmony_ci clear_recent_scan_work.work); 3318c2ecf20Sopenharmony_ci atomic_xchg(&priv->recent_scan, 0); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_civoid cw1200_scan_timeout(struct work_struct *work) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct cw1200_common *priv = 3378c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, scan.timeout.work); 3388c2ecf20Sopenharmony_ci if (atomic_xchg(&priv->scan.in_progress, 0)) { 3398c2ecf20Sopenharmony_ci if (priv->scan.status > 0) { 3408c2ecf20Sopenharmony_ci priv->scan.status = 0; 3418c2ecf20Sopenharmony_ci } else if (!priv->scan.status) { 3428c2ecf20Sopenharmony_ci wiphy_warn(priv->hw->wiphy, 3438c2ecf20Sopenharmony_ci "Timeout waiting for scan complete notification.\n"); 3448c2ecf20Sopenharmony_ci priv->scan.status = -ETIMEDOUT; 3458c2ecf20Sopenharmony_ci priv->scan.curr = priv->scan.end; 3468c2ecf20Sopenharmony_ci wsm_stop_scan(priv); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci cw1200_scan_complete(priv); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_civoid cw1200_probe_work(struct work_struct *work) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct cw1200_common *priv = 3558c2ecf20Sopenharmony_ci container_of(work, struct cw1200_common, scan.probe_work.work); 3568c2ecf20Sopenharmony_ci u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); 3578c2ecf20Sopenharmony_ci struct cw1200_queue *queue = &priv->tx_queue[queue_id]; 3588c2ecf20Sopenharmony_ci const struct cw1200_txpriv *txpriv; 3598c2ecf20Sopenharmony_ci struct wsm_tx *wsm; 3608c2ecf20Sopenharmony_ci struct wsm_template_frame frame = { 3618c2ecf20Sopenharmony_ci .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, 3628c2ecf20Sopenharmony_ci }; 3638c2ecf20Sopenharmony_ci struct wsm_ssid ssids[1] = {{ 3648c2ecf20Sopenharmony_ci .length = 0, 3658c2ecf20Sopenharmony_ci } }; 3668c2ecf20Sopenharmony_ci struct wsm_scan_ch ch[1] = {{ 3678c2ecf20Sopenharmony_ci .min_chan_time = 0, 3688c2ecf20Sopenharmony_ci .max_chan_time = 10, 3698c2ecf20Sopenharmony_ci } }; 3708c2ecf20Sopenharmony_ci struct wsm_scan scan = { 3718c2ecf20Sopenharmony_ci .type = WSM_SCAN_TYPE_FOREGROUND, 3728c2ecf20Sopenharmony_ci .num_probes = 1, 3738c2ecf20Sopenharmony_ci .probe_delay = 0, 3748c2ecf20Sopenharmony_ci .num_channels = 1, 3758c2ecf20Sopenharmony_ci .ssids = ssids, 3768c2ecf20Sopenharmony_ci .ch = ch, 3778c2ecf20Sopenharmony_ci }; 3788c2ecf20Sopenharmony_ci u8 *ies; 3798c2ecf20Sopenharmony_ci size_t ies_len; 3808c2ecf20Sopenharmony_ci int ret; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n"); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 3858c2ecf20Sopenharmony_ci if (down_trylock(&priv->scan.lock)) { 3868c2ecf20Sopenharmony_ci /* Scan is already in progress. Requeue self. */ 3878c2ecf20Sopenharmony_ci schedule(); 3888c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, &priv->scan.probe_work, 3898c2ecf20Sopenharmony_ci msecs_to_jiffies(100)); 3908c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 3918c2ecf20Sopenharmony_ci return; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Make sure we still have a pending probe req */ 3958c2ecf20Sopenharmony_ci if (cw1200_queue_get_skb(queue, priv->pending_frame_id, 3968c2ecf20Sopenharmony_ci &frame.skb, &txpriv)) { 3978c2ecf20Sopenharmony_ci up(&priv->scan.lock); 3988c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 3998c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 4008c2ecf20Sopenharmony_ci return; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci wsm = (struct wsm_tx *)frame.skb->data; 4038c2ecf20Sopenharmony_ci scan.max_tx_rate = wsm->max_tx_rate; 4048c2ecf20Sopenharmony_ci scan.band = (priv->channel->band == NL80211_BAND_5GHZ) ? 4058c2ecf20Sopenharmony_ci WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; 4068c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_STA || 4078c2ecf20Sopenharmony_ci priv->join_status == CW1200_JOIN_STATUS_IBSS) { 4088c2ecf20Sopenharmony_ci scan.type = WSM_SCAN_TYPE_BACKGROUND; 4098c2ecf20Sopenharmony_ci scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci ch[0].number = priv->channel->hw_value; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci skb_pull(frame.skb, txpriv->offset); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; 4168c2ecf20Sopenharmony_ci ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (ies_len) { 4198c2ecf20Sopenharmony_ci u8 *ssidie = 4208c2ecf20Sopenharmony_ci (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); 4218c2ecf20Sopenharmony_ci if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { 4228c2ecf20Sopenharmony_ci u8 *nextie = &ssidie[2 + ssidie[1]]; 4238c2ecf20Sopenharmony_ci /* Remove SSID from the IE list. It has to be provided 4248c2ecf20Sopenharmony_ci * as a separate argument in cw1200_scan_start call 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Store SSID localy */ 4288c2ecf20Sopenharmony_ci ssids[0].length = ssidie[1]; 4298c2ecf20Sopenharmony_ci memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); 4308c2ecf20Sopenharmony_ci scan.num_ssids = 1; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Remove SSID from IE list */ 4338c2ecf20Sopenharmony_ci ssidie[1] = 0; 4348c2ecf20Sopenharmony_ci memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); 4358c2ecf20Sopenharmony_ci skb_trim(frame.skb, frame.skb->len - ssids[0].length); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* FW bug: driver has to restart p2p-dev mode after scan */ 4408c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) 4418c2ecf20Sopenharmony_ci cw1200_disable_listening(priv); 4428c2ecf20Sopenharmony_ci ret = wsm_set_template_frame(priv, &frame); 4438c2ecf20Sopenharmony_ci priv->scan.direct_probe = 1; 4448c2ecf20Sopenharmony_ci if (!ret) { 4458c2ecf20Sopenharmony_ci wsm_flush_tx(priv); 4468c2ecf20Sopenharmony_ci ret = cw1200_scan_start(priv, &scan); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci skb_push(frame.skb, txpriv->offset); 4518c2ecf20Sopenharmony_ci if (!ret) 4528c2ecf20Sopenharmony_ci IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; 4538c2ecf20Sopenharmony_ci BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id)); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (ret) { 4568c2ecf20Sopenharmony_ci priv->scan.direct_probe = 0; 4578c2ecf20Sopenharmony_ci up(&priv->scan.lock); 4588c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return; 4628c2ecf20Sopenharmony_ci} 463