blob: 355887933d4d488ecdc86a83d6206c9d0b940312 [file] [log] [blame]
/*
* Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* DOC: contains scan north bound interface definitions
*/
#include <scheduler_api.h>
#include <wlan_scan_ucfg_api.h>
#include <wlan_objmgr_global_obj.h>
#include <wlan_objmgr_cmn.h>
#include <wlan_serialization_api.h>
#include <wlan_scan_tgt_api.h>
#include <wlan_scan_utils_api.h>
#include <wlan_reg_ucfg_api.h>
#include <wlan_reg_services_api.h>
#include <wlan_utility.h>
#include "../../core/src/wlan_scan_main.h"
#include "../../core/src/wlan_scan_manager.h"
#include "../../core/src/wlan_scan_cache_db.h"
#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD
#include <wlan_pmo_obj_mgmt_api.h>
#endif
#ifdef WLAN_POLICY_MGR_ENABLE
#include <wlan_dfs_utils_api.h>
#include <wlan_policy_mgr_api.h>
#endif
#include "cfg_ucfg_api.h"
#include "wlan_extscan_api.h"
QDF_STATUS ucfg_scan_register_bcn_cb(struct wlan_objmgr_psoc *psoc,
update_beacon_cb cb, enum scan_cb_type type)
{
return scm_scan_register_bcn_cb(psoc, cb, type);
}
qdf_list_t *ucfg_scan_get_result(struct wlan_objmgr_pdev *pdev,
struct scan_filter *filter)
{
return scm_get_scan_result(pdev, filter);
}
QDF_STATUS ucfg_scan_db_iterate(struct wlan_objmgr_pdev *pdev,
scan_iterator_func func, void *arg)
{
return scm_iterate_scan_db(pdev, func, arg);
}
QDF_STATUS ucfg_scan_purge_results(qdf_list_t *scan_list)
{
return scm_purge_scan_results(scan_list);
}
QDF_STATUS ucfg_scan_flush_results(struct wlan_objmgr_pdev *pdev,
struct scan_filter *filter)
{
return scm_flush_results(pdev, filter);
}
void ucfg_scan_filter_valid_channel(struct wlan_objmgr_pdev *pdev,
uint32_t *chan_freq_list, uint32_t num_chan)
{
scm_filter_valid_channel(pdev, chan_freq_list, num_chan);
}
QDF_STATUS ucfg_scan_init(void)
{
QDF_STATUS status;
status = wlan_objmgr_register_psoc_create_handler(WLAN_UMAC_COMP_SCAN,
wlan_scan_psoc_created_notification, NULL);
if (QDF_IS_STATUS_ERROR(status)) {
scm_err("Failed to register psoc create handler");
goto fail_create_psoc;
}
status = wlan_objmgr_register_psoc_destroy_handler(WLAN_UMAC_COMP_SCAN,
wlan_scan_psoc_destroyed_notification, NULL);
if (QDF_IS_STATUS_ERROR(status)) {
scm_err("Failed to create psoc delete handler");
goto fail_psoc_destroy;
}
scm_debug("scan psoc create and delete handler registered with objmgr");
status = wlan_objmgr_register_vdev_create_handler(WLAN_UMAC_COMP_SCAN,
wlan_scan_vdev_created_notification, NULL);
if (QDF_IS_STATUS_ERROR(status)) {
scm_err("Failed to register vdev create handler");
goto fail_pdev_create;
}
status = wlan_objmgr_register_vdev_destroy_handler(WLAN_UMAC_COMP_SCAN,
wlan_scan_vdev_destroyed_notification, NULL);
if (QDF_IS_STATUS_SUCCESS(status)) {
scm_debug("scan vdev create and delete handler registered with objmgr");
return QDF_STATUS_SUCCESS;
}
scm_err("Failed to destroy vdev delete handler");
wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_SCAN,
wlan_scan_vdev_created_notification, NULL);
fail_pdev_create:
wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_SCAN,
wlan_scan_psoc_destroyed_notification, NULL);
fail_psoc_destroy:
wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_SCAN,
wlan_scan_psoc_created_notification, NULL);
fail_create_psoc:
return status;
}
QDF_STATUS ucfg_scan_deinit(void)
{
QDF_STATUS status;
status = wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_SCAN,
wlan_scan_psoc_created_notification, NULL);
if (status != QDF_STATUS_SUCCESS)
scm_err("Failed to unregister psoc create handler");
status = wlan_objmgr_unregister_psoc_destroy_handler(
WLAN_UMAC_COMP_SCAN,
wlan_scan_psoc_destroyed_notification, NULL);
if (status != QDF_STATUS_SUCCESS)
scm_err("Failed to unregister psoc delete handler");
status = wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_SCAN,
wlan_scan_vdev_created_notification, NULL);
if (status != QDF_STATUS_SUCCESS)
scm_err("Failed to unregister vdev create handler");
status = wlan_objmgr_unregister_vdev_destroy_handler(
WLAN_UMAC_COMP_SCAN,
wlan_scan_vdev_destroyed_notification, NULL);
if (status != QDF_STATUS_SUCCESS)
scm_err("Failed to unregister vdev delete handler");
return status;
}
#ifdef FEATURE_WLAN_SCAN_PNO
bool
ucfg_is_6ghz_pno_scan_optimization_supported(struct wlan_objmgr_psoc *psoc)
{
return wlan_psoc_nif_fw_ext_cap_get(psoc,
WLAN_SOC_PNO_SCAN_CONFIG_PER_CHANNEL);
}
QDF_STATUS ucfg_scan_pno_start(struct wlan_objmgr_vdev *vdev,
struct pno_scan_req_params *req)
{
struct scan_vdev_obj *scan_vdev_obj;
QDF_STATUS status;
scan_vdev_obj = wlan_get_vdev_scan_obj(vdev);
if (!scan_vdev_obj) {
scm_err("null scan_vdev_obj");
return QDF_STATUS_E_INVAL;
}
if (scan_vdev_obj->pno_in_progress) {
scm_err("pno already in progress");
return QDF_STATUS_E_ALREADY;
}
status = tgt_scan_pno_start(vdev, req);
if (QDF_IS_STATUS_ERROR(status))
scm_err("pno start failed");
else
scan_vdev_obj->pno_in_progress = true;
return status;
}
void ucfg_scan_add_flags_to_pno_chan_list(struct wlan_objmgr_vdev *vdev,
struct pno_scan_req_params *req,
uint8_t *num_chan,
uint32_t short_ssid,
int list_idx)
{
struct chan_list *pno_chan_list =
&req->networks_list[list_idx].pno_chan_list;
/* Add RNR flags to channels based on scan_mode_6g ini */
scm_add_channel_flags(vdev, pno_chan_list, num_chan,
req->scan_policy_colocated_6ghz, true);
/* Filter RNR flags in pno channel list based on short ssid entry in
* RNR db cache.
*/
scm_filter_rnr_flag_pno(vdev, short_ssid,
&req->networks_list[0].pno_chan_list);
}
QDF_STATUS ucfg_scan_pno_stop(struct wlan_objmgr_vdev *vdev)
{
struct scan_vdev_obj *scan_vdev_obj;
QDF_STATUS status;
scan_vdev_obj = wlan_get_vdev_scan_obj(vdev);
if (!scan_vdev_obj) {
scm_err("null scan_vdev_obj");
return QDF_STATUS_E_INVAL;
}
if (!scan_vdev_obj->pno_in_progress) {
scm_debug("pno already stopped");
return QDF_STATUS_SUCCESS;
}
status = tgt_scan_pno_stop(vdev, wlan_vdev_get_id(vdev));
if (QDF_IS_STATUS_ERROR(status))
scm_err("pno stop failed");
else
scan_vdev_obj->pno_in_progress = false;
return status;
}
bool ucfg_scan_get_pno_in_progress(struct wlan_objmgr_vdev *vdev)
{
struct scan_vdev_obj *scan_vdev_obj;
scan_vdev_obj = wlan_get_vdev_scan_obj(vdev);
if (!scan_vdev_obj) {
scm_err("null scan_vdev_obj");
return false;
}
return scan_vdev_obj->pno_in_progress;
}
bool ucfg_scan_get_pno_match(struct wlan_objmgr_vdev *vdev)
{
struct scan_vdev_obj *scan_vdev_obj;
scan_vdev_obj = wlan_get_vdev_scan_obj(vdev);
if (!scan_vdev_obj) {
scm_err("null scan_vdev_obj");
return false;
}
return scan_vdev_obj->pno_match_evt_received;
}
static QDF_STATUS
wlan_pno_global_init(struct wlan_objmgr_psoc *psoc,
struct wlan_scan_obj *scan_obj)
{
struct nlo_mawc_params *mawc_cfg;
struct pno_def_config *pno_def;
pno_def = &scan_obj->pno_cfg;
qdf_wake_lock_create(&pno_def->pno_wake_lock, "wlan_pno_wl");
qdf_runtime_lock_init(&pno_def->pno_runtime_pm_lock);
mawc_cfg = &pno_def->mawc_params;
pno_def->channel_prediction = cfg_get(psoc, CFG_PNO_CHANNEL_PREDICTION);
pno_def->top_k_num_of_channels =
cfg_get(psoc, CFG_TOP_K_NUM_OF_CHANNELS);
pno_def->stationary_thresh = cfg_get(psoc, CFG_STATIONARY_THRESHOLD);
pno_def->channel_prediction_full_scan =
cfg_get(psoc, CFG_CHANNEL_PREDICTION_SCAN_TIMER);
pno_def->adaptive_dwell_mode =
cfg_get(psoc, CFG_ADAPTIVE_PNOSCAN_DWELL_MODE);
pno_def->dfs_chnl_scan_enabled =
cfg_get(psoc, CFG_ENABLE_DFS_PNO_CHNL_SCAN);
pno_def->scan_support_enabled =
cfg_get(psoc, CFG_PNO_SCAN_SUPPORT);
pno_def->scan_timer_repeat_value =
cfg_get(psoc, CFG_PNO_SCAN_TIMER_REPEAT_VALUE);
pno_def->slow_scan_multiplier =
cfg_get(psoc, CFG_PNO_SLOW_SCAN_MULTIPLIER);
pno_def->scan_backoff_multiplier =
cfg_get(psoc, CFG_SCAN_BACKOFF_MULTIPLIER);
pno_def->max_sched_scan_plan_interval =
cfg_get(psoc, CFG_MAX_SCHED_SCAN_PLAN_INTERVAL);
pno_def->max_sched_scan_plan_iterations =
cfg_get(psoc, CFG_MAX_SCHED_SCAN_PLAN_ITERATIONS);
pno_def->user_config_sched_scan_plan =
cfg_get(psoc, CFG_USER_CONFIG_SCHED_SCAN_PLAN);
mawc_cfg->enable = cfg_get(psoc, CFG_MAWC_NLO_ENABLED);
mawc_cfg->exp_backoff_ratio =
cfg_get(psoc, CFG_MAWC_NLO_EXP_BACKOFF_RATIO);
mawc_cfg->init_scan_interval =
cfg_get(psoc, CFG_MAWC_NLO_INIT_SCAN_INTERVAL);
mawc_cfg->max_scan_interval =
cfg_get(psoc, CFG_MAWC_NLO_MAX_SCAN_INTERVAL);
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS
wlan_pno_global_deinit(struct wlan_scan_obj *scan_obj)
{
qdf_runtime_lock_deinit(&scan_obj->pno_cfg.pno_runtime_pm_lock);
qdf_wake_lock_destroy(&scan_obj->pno_cfg.pno_wake_lock);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
ucfg_scan_get_pno_def_params(struct wlan_objmgr_vdev *vdev,
struct pno_scan_req_params *req)
{
struct scan_default_params *scan_def;
struct wlan_scan_obj *scan;
struct pno_def_config *pno_def;
if (!vdev || !req) {
scm_err("vdev: 0x%pK, req: 0x%pK",
vdev, req);
return QDF_STATUS_E_INVAL;
}
scan = wlan_vdev_get_scan_obj(vdev);
if (!scan) {
scm_err("scan is NULL");
return QDF_STATUS_E_INVAL;
}
scan_def = wlan_vdev_get_def_scan_params(vdev);
if (!scan_def) {
scm_err("wlan_vdev_get_def_scan_params returned NULL");
return QDF_STATUS_E_NULL_VALUE;
}
pno_def = &scan->pno_cfg;
req->active_dwell_time = scan_def->active_dwell;
req->passive_dwell_time = scan_def->passive_dwell;
req->scan_random.randomize = scan_def->enable_mac_spoofing;
/*
* Update active and passive dwell time depending
* upon the present active concurrency mode
*/
wlan_scan_update_pno_dwell_time(vdev, req, scan_def);
req->adaptive_dwell_mode = pno_def->adaptive_dwell_mode;
req->pno_channel_prediction = pno_def->channel_prediction;
req->top_k_num_of_channels = pno_def->top_k_num_of_channels;
req->stationary_thresh = pno_def->stationary_thresh;
req->channel_prediction_full_scan =
pno_def->channel_prediction_full_scan;
req->mawc_params.vdev_id = wlan_vdev_get_id(vdev);
qdf_mem_copy(&req->mawc_params, &pno_def->mawc_params,
sizeof(req->mawc_params));
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
ucfg_scan_register_pno_cb(struct wlan_objmgr_psoc *psoc,
scan_event_handler event_cb, void *arg)
{
struct wlan_scan_obj *scan;
if (!psoc) {
scm_err("null psoc");
return QDF_STATUS_E_INVAL;
}
scan = wlan_psoc_get_scan_obj(psoc);
if (!scan) {
scm_err("scan object null");
return QDF_STATUS_E_INVAL;
}
qdf_spin_lock_bh(&scan->lock);
scan->pno_cfg.pno_cb.func = event_cb;
scan->pno_cfg.pno_cb.arg = arg;
qdf_spin_unlock_bh(&scan->lock);
scm_debug("event_cb: 0x%pK, arg: 0x%pK", event_cb, arg);
return QDF_STATUS_SUCCESS;
}
#else
static inline QDF_STATUS
wlan_pno_global_init(struct wlan_objmgr_psoc *psoc,
struct wlan_scan_obj *scan_obj)
{
return QDF_STATUS_SUCCESS;
}
static inline QDF_STATUS
wlan_pno_global_deinit(struct wlan_scan_obj *scan_obj)
{
return QDF_STATUS_SUCCESS;
}
#endif
QDF_STATUS
ucfg_scan_set_custom_scan_chan_list(struct wlan_objmgr_pdev *pdev,
struct chan_list *chan_list)
{
uint8_t pdev_id;
struct wlan_scan_obj *scan_obj;
if (!pdev || !chan_list) {
scm_warn("pdev: 0x%pK, chan_list: 0x%pK", pdev, chan_list);
return QDF_STATUS_E_NULL_VALUE;
}
pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
scan_obj = wlan_pdev_get_scan_obj(pdev);
qdf_mem_copy(&scan_obj->pdev_info[pdev_id].custom_chan_list,
chan_list, sizeof(*chan_list));
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
ucfg_scm_scan_free_scan_request_mem(struct scan_start_request *req)
{
return scm_scan_free_scan_request_mem(req);
}
QDF_STATUS ucfg_scan_psoc_set_enable(struct wlan_objmgr_psoc *psoc,
enum scan_disable_reason reason)
{
return wlan_scan_psoc_set_enable(psoc, reason);
}
QDF_STATUS ucfg_scan_psoc_set_disable(struct wlan_objmgr_psoc *psoc,
enum scan_disable_reason reason)
{
return wlan_scan_psoc_set_disable(psoc, reason);
}
QDF_STATUS ucfg_scan_vdev_set_enable(struct wlan_objmgr_vdev *vdev,
enum scan_disable_reason reason)
{
struct scan_vdev_obj *scan_vdev_obj;
scan_vdev_obj = wlan_get_vdev_scan_obj(vdev);
if (!scan_vdev_obj) {
scm_err("null scan_vdev_obj");
return QDF_STATUS_E_NULL_VALUE;
}
scan_vdev_obj->scan_disabled &= ~reason;
scm_debug("Vdev scan_disabled %x", scan_vdev_obj->scan_disabled);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS ucfg_scan_vdev_set_disable(struct wlan_objmgr_vdev *vdev,
enum scan_disable_reason reason)
{
struct scan_vdev_obj *scan_vdev_obj;
scan_vdev_obj = wlan_get_vdev_scan_obj(vdev);
if (!scan_vdev_obj) {
scm_err("null scan_vdev_obj");
return QDF_STATUS_E_NULL_VALUE;
}
scan_vdev_obj->scan_disabled |= reason;
scm_debug("Vdev scan_disabled %x", scan_vdev_obj->scan_disabled);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS ucfg_scan_set_miracast(
struct wlan_objmgr_psoc *psoc, bool enable)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return QDF_STATUS_E_NULL_VALUE;
}
scan_obj->miracast_enabled = enable;
scm_debug("set miracast_enable to %d", scan_obj->miracast_enabled);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
ucfg_scan_set_wide_band_scan(struct wlan_objmgr_pdev *pdev, bool enable)
{
uint8_t pdev_id;
struct wlan_scan_obj *scan_obj;
if (!pdev) {
scm_warn("null vdev");
return QDF_STATUS_E_NULL_VALUE;
}
pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
scan_obj = wlan_pdev_get_scan_obj(pdev);
if (!scan_obj)
return QDF_STATUS_E_FAILURE;
scm_debug("set wide_band_scan to %d", enable);
scan_obj->pdev_info[pdev_id].wide_band_scan = enable;
return QDF_STATUS_SUCCESS;
}
bool ucfg_scan_get_wide_band_scan(struct wlan_objmgr_pdev *pdev)
{
uint8_t pdev_id;
struct wlan_scan_obj *scan_obj;
if (!pdev) {
scm_warn("null vdev");
return QDF_STATUS_E_NULL_VALUE;
}
pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
scan_obj = wlan_pdev_get_scan_obj(pdev);
if (!scan_obj)
return QDF_STATUS_E_FAILURE;
return scan_obj->pdev_info[pdev_id].wide_band_scan;
}
#ifdef WLAN_DFS_CHAN_HIDDEN_SSID
QDF_STATUS
ucfg_scan_config_hidden_ssid_for_bssid(struct wlan_objmgr_pdev *pdev,
uint8_t *bssid, struct wlan_ssid *ssid)
{
uint8_t pdev_id;
struct wlan_scan_obj *scan_obj;
if (!pdev) {
scm_warn("null vdev");
return QDF_STATUS_E_NULL_VALUE;
}
pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
scan_obj = wlan_pdev_get_scan_obj(pdev);
if (!scan_obj)
return QDF_STATUS_E_FAILURE;
scm_debug("Configure bsssid:"QDF_MAC_ADDR_FMT" ssid:%.*s",
QDF_MAC_ADDR_REF(bssid), ssid->length, ssid->ssid);
qdf_mem_copy(scan_obj->pdev_info[pdev_id].conf_bssid,
bssid, QDF_MAC_ADDR_SIZE);
scan_obj->pdev_info[pdev_id].conf_ssid.length = ssid->length;
qdf_mem_copy(scan_obj->pdev_info[pdev_id].conf_ssid.ssid,
ssid->ssid,
scan_obj->pdev_info[pdev_id].conf_ssid.length);
return QDF_STATUS_SUCCESS;
}
#endif /* WLAN_DFS_CHAN_HIDDEN_SSID */
QDF_STATUS
ucfg_scan_cancel_sync(struct scan_cancel_request *req)
{
QDF_STATUS status;
bool cancel_vdev = false, cancel_pdev = false;
struct wlan_objmgr_vdev *vdev;
struct wlan_objmgr_pdev *pdev;
uint32_t max_wait_iterations = SCM_CANCEL_SCAN_WAIT_ITERATION;
if (!req || !req->vdev) {
scm_err("req or vdev within req is NULL");
if (req)
qdf_mem_free(req);
return QDF_STATUS_E_NULL_VALUE;
}
if (req->cancel_req.req_type == WLAN_SCAN_CANCEL_PDEV_ALL)
cancel_pdev = true;
else if (req->cancel_req.req_type == WLAN_SCAN_CANCEL_VDEV_ALL ||
req->cancel_req.req_type == WLAN_SCAN_CANCEL_HOST_VDEV_ALL)
cancel_vdev = true;
vdev = req->vdev;
status = wlan_scan_cancel(req);
if (QDF_IS_STATUS_ERROR(status))
return status;
if (cancel_pdev) {
pdev = wlan_vdev_get_pdev(vdev);
while ((wlan_get_pdev_status(pdev) !=
SCAN_NOT_IN_PROGRESS) && max_wait_iterations) {
scm_debug("wait for all pdev scan to get complete");
qdf_sleep(SCM_CANCEL_SCAN_WAIT_TIME);
max_wait_iterations--;
}
} else if (cancel_vdev) {
while ((wlan_get_vdev_status(vdev) !=
SCAN_NOT_IN_PROGRESS) && max_wait_iterations) {
scm_debug("wait for all vdev scan to get complete");
qdf_sleep(SCM_CANCEL_SCAN_WAIT_TIME);
max_wait_iterations--;
}
}
if (!max_wait_iterations) {
scm_err("Failed to wait for scans to get complete");
return QDF_STATUS_E_TIMEOUT;
}
return status;
}
uint8_t*
ucfg_get_scan_requester_name(struct wlan_objmgr_psoc *psoc,
wlan_scan_requester requester)
{
int idx = requester & WLAN_SCAN_REQUESTER_ID_MASK;
struct wlan_scan_obj *scan;
struct scan_requester_info *requesters;
if (!psoc) {
scm_err("null psoc");
return "null";
}
scan = wlan_psoc_get_scan_obj(psoc);
if (!scan)
return "null";
requesters = scan->requesters;
if ((idx < WLAN_MAX_REQUESTORS) &&
(requesters[idx].requester == requester)) {
return requesters[idx].module;
}
return (uint8_t *)"unknown";
}
static QDF_STATUS
scm_add_scan_event_handler(struct pdev_scan_ev_handler *pdev_ev_handler,
scan_event_handler event_cb, void *arg)
{
struct cb_handler *cb_handler;
uint32_t handler_cnt = pdev_ev_handler->handler_cnt;
/* Assign next available slot to this registration request */
cb_handler = &(pdev_ev_handler->cb_handlers[handler_cnt]);
cb_handler->func = event_cb;
cb_handler->arg = arg;
pdev_ev_handler->handler_cnt++;
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
ucfg_scan_register_event_handler(struct wlan_objmgr_pdev *pdev,
scan_event_handler event_cb, void *arg)
{
uint32_t idx;
struct wlan_scan_obj *scan;
struct pdev_scan_ev_handler *pdev_ev_handler;
struct cb_handler *cb_handler;
/* scan event handler call back can't be NULL */
if (!pdev || !event_cb) {
scm_err("pdev: %pK, event_cb: %pK", pdev, event_cb);
return QDF_STATUS_E_NULL_VALUE;
}
scm_debug("pdev: %pK, event_cb: %pK, arg: %pK\n", pdev, event_cb, arg);
scan = wlan_pdev_get_scan_obj(pdev);
pdev_ev_handler = wlan_pdev_get_pdev_scan_ev_handlers(pdev);
if (!pdev_ev_handler) {
scm_err("null pdev_ev_handler");
return QDF_STATUS_E_NULL_VALUE;
}
cb_handler = &(pdev_ev_handler->cb_handlers[0]);
qdf_spin_lock_bh(&scan->lock);
/* Ensure its not a duplicate registration request */
for (idx = 0; idx < MAX_SCAN_EVENT_HANDLERS_PER_PDEV;
idx++, cb_handler++) {
if ((cb_handler->func == event_cb) &&
(cb_handler->arg == arg)) {
qdf_spin_unlock_bh(&scan->lock);
scm_debug("func: %pK, arg: %pK already exists",
event_cb, arg);
return QDF_STATUS_SUCCESS;
}
}
QDF_ASSERT(pdev_ev_handler->handler_cnt <
MAX_SCAN_EVENT_HANDLERS_PER_PDEV);
if (pdev_ev_handler->handler_cnt >= MAX_SCAN_EVENT_HANDLERS_PER_PDEV) {
qdf_spin_unlock_bh(&scan->lock);
scm_warn("No more registrations possible");
return QDF_STATUS_E_NOMEM;
}
scm_add_scan_event_handler(pdev_ev_handler, event_cb, arg);
qdf_spin_unlock_bh(&scan->lock);
scm_debug("event_cb: 0x%pK, arg: 0x%pK", event_cb, arg);
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS
wlan_scan_global_init(struct wlan_objmgr_psoc *psoc,
struct wlan_scan_obj *scan_obj)
{
scan_obj->scan_disabled = 0;
scan_obj->drop_bcn_on_chan_mismatch =
cfg_get(psoc, CFG_DROP_BCN_ON_CHANNEL_MISMATCH);
scan_obj->drop_bcn_on_invalid_freq =
cfg_get(psoc, CFG_DROP_BCN_ON_INVALID_FREQ);
scan_obj->disable_timeout = false;
scan_obj->obss_scan_offload = false;
scan_obj->scan_def.active_dwell =
cfg_get(psoc, CFG_ACTIVE_MAX_CHANNEL_TIME);
/* the ini is disallow DFS channel scan if ini is 1, so negate that */
scan_obj->scan_def.allow_dfs_chan_in_first_scan =
!cfg_get(psoc, CFG_INITIAL_NO_DFS_SCAN);
scan_obj->scan_def.allow_dfs_chan_in_scan =
cfg_get(psoc, CFG_ENABLE_DFS_SCAN);
scan_obj->scan_def.skip_dfs_chan_in_p2p_search =
cfg_get(psoc, CFG_ENABLE_SKIP_DFS_IN_P2P_SEARCH);
scan_obj->scan_def.use_wake_lock_in_user_scan =
cfg_get(psoc, CFG_ENABLE_WAKE_LOCK_IN_SCAN);
scan_obj->scan_def.active_dwell_2g =
cfg_get(psoc, CFG_ACTIVE_MAX_2G_CHANNEL_TIME);
scan_obj->scan_def.min_dwell_time_6g =
cfg_get(psoc, CFG_MIN_6G_CHANNEL_TIME);
scan_obj->scan_def.active_dwell_6g =
cfg_get(psoc, CFG_ACTIVE_MAX_6G_CHANNEL_TIME);
scan_obj->scan_def.passive_dwell_6g =
cfg_get(psoc, CFG_PASSIVE_MAX_6G_CHANNEL_TIME);
scan_obj->scan_def.active_dwell_time_6g_conc =
cfg_get(psoc, CFG_ACTIVE_MAX_6G_CHANNEL_TIME_CONC);
scan_obj->scan_def.passive_dwell_time_6g_conc =
cfg_get(psoc, CFG_PASSIVE_MAX_6G_CHANNEL_TIME_CONC);
scan_obj->scan_def.passive_dwell =
cfg_get(psoc, CFG_PASSIVE_MAX_CHANNEL_TIME);
scan_obj->scan_def.max_rest_time = SCAN_MAX_REST_TIME;
scan_obj->scan_def.sta_miracast_mcc_rest_time =
SCAN_STA_MIRACAST_MCC_REST_TIME;
scan_obj->scan_def.min_rest_time = SCAN_MIN_REST_TIME;
scan_obj->scan_def.conc_active_dwell =
cfg_get(psoc, CFG_ACTIVE_MAX_CHANNEL_TIME_CONC);
scan_obj->scan_def.conc_passive_dwell =
cfg_get(psoc, CFG_PASSIVE_MAX_CHANNEL_TIME_CONC);
scan_obj->scan_def.conc_max_rest_time =
cfg_get(psoc, CFG_MAX_REST_TIME_CONC);
scan_obj->scan_def.conc_min_rest_time =
cfg_get(psoc, CFG_MIN_REST_TIME_CONC);
scan_obj->scan_def.conc_idle_time =
cfg_get(psoc, CFG_IDLE_TIME_CONC);
scan_obj->scan_def.repeat_probe_time =
cfg_get(psoc, CFG_SCAN_PROBE_REPEAT_TIME);
scan_obj->scan_def.probe_spacing_time = SCAN_PROBE_SPACING_TIME;
scan_obj->scan_def.probe_delay = SCAN_PROBE_DELAY;
scan_obj->scan_def.burst_duration = SCAN_BURST_DURATION;
scan_obj->scan_def.max_scan_time = SCAN_MAX_SCAN_TIME;
scan_obj->scan_def.num_probes = cfg_get(psoc, CFG_SCAN_NUM_PROBES);
scan_obj->scan_def.scan_cache_aging_time =
(cfg_get(psoc, CFG_SCAN_AGING_TIME) * 1000);
scan_obj->scan_def.max_bss_per_pdev = SCAN_MAX_BSS_PDEV;
scan_obj->scan_def.scan_priority = SCAN_PRIORITY;
scan_obj->scan_def.idle_time = SCAN_NETWORK_IDLE_TIMEOUT;
scan_obj->scan_def.adaptive_dwell_time_mode =
cfg_get(psoc, CFG_ADAPTIVE_SCAN_DWELL_MODE);
scan_obj->scan_def.adaptive_dwell_time_mode_nc =
cfg_get(psoc, CFG_ADAPTIVE_SCAN_DWELL_MODE_NC);
scan_obj->scan_def.honour_nl_scan_policy_flags =
cfg_get(psoc, CFG_HONOUR_NL_SCAN_POLICY_FLAGS);
scan_obj->scan_def.enable_mac_spoofing =
cfg_get(psoc, CFG_ENABLE_MAC_ADDR_SPOOFING);
scan_obj->scan_def.extscan_adaptive_dwell_mode =
cfg_get(psoc, CFG_ADAPTIVE_EXTSCAN_DWELL_MODE);
/* init burst durations */
scan_obj->scan_def.sta_scan_burst_duration =
cfg_get(psoc, CFG_STA_SCAN_BURST_DURATION);
scan_obj->scan_def.p2p_scan_burst_duration =
cfg_get(psoc, CFG_P2P_SCAN_BURST_DURATION);
scan_obj->scan_def.go_scan_burst_duration =
cfg_get(psoc, CFG_GO_SCAN_BURST_DURATION);
scan_obj->scan_def.ap_scan_burst_duration =
cfg_get(psoc, CFG_AP_SCAN_BURST_DURATION);
/* scan contrl flags */
scan_obj->scan_def.scan_f_passive = true;
scan_obj->scan_def.scan_f_ofdm_rates = true;
scan_obj->scan_def.scan_f_2ghz = true;
scan_obj->scan_def.scan_f_5ghz = true;
scan_obj->scan_def.scan_f_chan_stat_evnt =
cfg_get(psoc, CFG_ENABLE_SNR_MONITORING);
/* scan event flags */
scan_obj->scan_def.scan_ev_started = true;
scan_obj->scan_def.scan_ev_completed = true;
scan_obj->scan_def.scan_ev_bss_chan = true;
scan_obj->scan_def.scan_ev_foreign_chan = true;
scan_obj->scan_def.scan_ev_foreign_chn_exit = true;
scan_obj->scan_def.scan_ev_dequeued = true;
scan_obj->scan_def.scan_ev_preempted = true;
scan_obj->scan_def.scan_ev_start_failed = true;
scan_obj->scan_def.scan_ev_restarted = true;
scan_obj->scan_def.enable_connected_scan =
cfg_get(psoc, CFG_ENABLE_CONNECTED_SCAN);
scan_obj->scan_def.scan_mode_6g = cfg_get(psoc, CFG_6GHZ_SCAN_MODE);
scan_obj->scan_def.duty_cycle_6ghz =
cfg_get(psoc, CFG_6GHZ_SCAN_MODE_DUTY_CYCLE);
scan_obj->allow_bss_with_incomplete_ie =
cfg_get(psoc, CFG_SCAN_ALLOW_BSS_WITH_CORRUPTED_IE);
scan_obj->scan_def.skip_6g_and_indoor_freq =
cfg_get(psoc, CFG_SKIP_6GHZ_AND_INDOOR_FREQ_SCAN);
/* init scan id seed */
qdf_atomic_init(&scan_obj->scan_ids);
/* init extscan */
wlan_extscan_global_init(psoc, scan_obj);
return wlan_pno_global_init(psoc, scan_obj);
}
static void
wlan_scan_global_deinit(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
wlan_pno_global_deinit(scan_obj);
wlan_extscan_global_deinit();
}
static QDF_STATUS
scm_remove_scan_event_handler(struct pdev_scan_ev_handler *pdev_ev_handler,
struct cb_handler *entry)
{
struct cb_handler *last_entry;
uint32_t handler_cnt = pdev_ev_handler->handler_cnt;
/* Replace event handler being deleted
* with the last one in the list.
*/
last_entry = &(pdev_ev_handler->cb_handlers[handler_cnt - 1]);
entry->func = last_entry->func;
entry->arg = last_entry->arg;
/* Clear our last entry */
last_entry->func = NULL;
last_entry->arg = NULL;
pdev_ev_handler->handler_cnt--;
return QDF_STATUS_SUCCESS;
}
void
ucfg_scan_unregister_event_handler(struct wlan_objmgr_pdev *pdev,
scan_event_handler event_cb, void *arg)
{
uint8_t found = false;
uint32_t idx;
uint32_t handler_cnt;
struct wlan_scan_obj *scan;
struct cb_handler *cb_handler;
struct pdev_scan_ev_handler *pdev_ev_handler;
scm_debug("pdev: %pK, event_cb: 0x%pK, arg: 0x%pK", pdev, event_cb,
arg);
if (!pdev) {
scm_err("null pdev");
return;
}
scan = wlan_pdev_get_scan_obj(pdev);
if (!scan)
return;
pdev_ev_handler = wlan_pdev_get_pdev_scan_ev_handlers(pdev);
if (!pdev_ev_handler)
return;
cb_handler = &(pdev_ev_handler->cb_handlers[0]);
qdf_spin_lock_bh(&scan->lock);
handler_cnt = pdev_ev_handler->handler_cnt;
if (!handler_cnt) {
qdf_spin_unlock_bh(&scan->lock);
scm_info("No event handlers registered");
return;
}
for (idx = 0; idx < MAX_SCAN_EVENT_HANDLERS_PER_PDEV;
idx++, cb_handler++) {
if ((cb_handler->func == event_cb) &&
(cb_handler->arg == arg)) {
/* Event handler found, remove it
* from event handler list.
*/
found = true;
scm_remove_scan_event_handler(pdev_ev_handler,
cb_handler);
handler_cnt--;
break;
}
}
qdf_spin_unlock_bh(&scan->lock);
scm_debug("event handler %s, remaining handlers: %d",
(found ? "removed" : "not found"), handler_cnt);
}
QDF_STATUS
ucfg_scan_init_ssid_params(struct scan_start_request *req,
uint32_t num_ssid, struct wlan_ssid *ssid_list)
{
uint32_t max_ssid = sizeof(req->scan_req.ssid) /
sizeof(req->scan_req.ssid[0]);
if (!req) {
scm_err("null request");
return QDF_STATUS_E_NULL_VALUE;
}
if (!num_ssid) {
/* empty channel list provided */
req->scan_req.num_ssids = 0;
qdf_mem_zero(&req->scan_req.ssid[0],
sizeof(req->scan_req.ssid));
return QDF_STATUS_SUCCESS;
}
if (!ssid_list) {
scm_err("null ssid_list while num_ssid: %d", num_ssid);
return QDF_STATUS_E_NULL_VALUE;
}
if (num_ssid > max_ssid) {
/* got a big list. alert and continue */
scm_warn("overflow: received %d, max supported : %d",
num_ssid, max_ssid);
return QDF_STATUS_E_E2BIG;
}
if (max_ssid > num_ssid)
max_ssid = num_ssid;
req->scan_req.num_ssids = max_ssid;
qdf_mem_copy(&req->scan_req.ssid[0], ssid_list,
(req->scan_req.num_ssids * sizeof(req->scan_req.ssid[0])));
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
ucfg_scan_init_bssid_params(struct scan_start_request *req,
uint32_t num_bssid, struct qdf_mac_addr *bssid_list)
{
uint32_t max_bssid = sizeof(req->scan_req.bssid_list) /
sizeof(req->scan_req.bssid_list[0]);
if (!req) {
scm_err("null request");
return QDF_STATUS_E_NULL_VALUE;
}
if (!num_bssid) {
/* empty channel list provided */
req->scan_req.num_bssid = 0;
qdf_mem_zero(&req->scan_req.bssid_list[0],
sizeof(req->scan_req.bssid_list));
return QDF_STATUS_SUCCESS;
}
if (!bssid_list) {
scm_err("null bssid_list while num_bssid: %d", num_bssid);
return QDF_STATUS_E_NULL_VALUE;
}
if (num_bssid > max_bssid) {
/* got a big list. alert and continue */
scm_warn("overflow: received %d, max supported : %d",
num_bssid, max_bssid);
return QDF_STATUS_E_E2BIG;
}
if (max_bssid > num_bssid)
max_bssid = num_bssid;
req->scan_req.num_bssid = max_bssid;
qdf_mem_copy(&req->scan_req.bssid_list[0], bssid_list,
req->scan_req.num_bssid * sizeof(req->scan_req.bssid_list[0]));
return QDF_STATUS_SUCCESS;
}
/**
* is_chan_enabled_for_scan() - helper API to check if a frequency
* is allowed to scan.
* @reg_chan: regulatory_channel object
* @low_2g: lower 2.4 GHz frequency thresold
* @high_2g: upper 2.4 GHz frequency thresold
* @low_5g: lower 5 GHz frequency thresold
* @high_5g: upper 5 GHz frequency thresold
*
* Return: true if scan is allowed. false otherwise.
*/
static bool
is_chan_enabled_for_scan(struct regulatory_channel *reg_chan,
qdf_freq_t low_2g, qdf_freq_t high_2g, qdf_freq_t low_5g,
qdf_freq_t high_5g)
{
if (reg_chan->state == CHANNEL_STATE_DISABLE)
return false;
if (reg_chan->nol_chan)
return false;
/* 2 GHz channel */
if ((util_scan_scm_freq_to_band(reg_chan->center_freq) ==
WLAN_BAND_2_4_GHZ) &&
((reg_chan->center_freq < low_2g) ||
(reg_chan->center_freq > high_2g)))
return false;
else if ((util_scan_scm_freq_to_band(reg_chan->center_freq) ==
WLAN_BAND_5_GHZ) &&
((reg_chan->center_freq < low_5g) ||
(reg_chan->center_freq > high_5g)))
return false;
return true;
}
QDF_STATUS
ucfg_scan_init_chanlist_params(struct scan_start_request *req,
uint32_t num_chans, uint32_t *chan_list, uint32_t *phymode)
{
uint32_t idx;
QDF_STATUS status;
struct regulatory_channel *reg_chan_list = NULL;
qdf_freq_t low_2g, high_2g, low_5g, high_5g;
struct wlan_objmgr_pdev *pdev = NULL;
uint32_t *scan_freqs = NULL;
uint32_t max_chans = sizeof(req->scan_req.chan_list.chan) /
sizeof(req->scan_req.chan_list.chan[0]);
if (!req) {
scm_err("null request");
return QDF_STATUS_E_NULL_VALUE;
}
if (req->vdev)
pdev = wlan_vdev_get_pdev(req->vdev);
/*
* If 0 channels are provided for scan and
* wide band scan is enabled, scan all 20 mhz
* available channels. This is required as FW
* scans all channel/phy mode combinations
* provided in scan channel list if 0 chans are
* provided in scan request causing scan to take
* too much time to complete.
*/
if (pdev && !num_chans) {
reg_chan_list = qdf_mem_malloc_atomic(NUM_CHANNELS *
sizeof(struct regulatory_channel));
if (!reg_chan_list) {
status = QDF_STATUS_E_NOMEM;
goto end;
}
scan_freqs =
qdf_mem_malloc_atomic(sizeof(uint32_t) * max_chans);
if (!scan_freqs) {
status = QDF_STATUS_E_NOMEM;
goto end;
}
status = wlan_reg_get_current_chan_list(pdev, reg_chan_list);
if (QDF_IS_STATUS_ERROR(status))
goto end;
status = wlan_reg_get_freq_range(pdev, &low_2g,
&high_2g, &low_5g, &high_5g);
if (QDF_IS_STATUS_ERROR(status))
goto end;
for (idx = 0, num_chans = 0;
(idx < NUM_CHANNELS && num_chans < max_chans); idx++)
if ((is_chan_enabled_for_scan(&reg_chan_list[idx],
low_2g, high_2g,
low_5g, high_5g)) &&
((req->scan_req.scan_f_2ghz &&
WLAN_REG_IS_24GHZ_CH_FREQ(
reg_chan_list[idx].center_freq)) ||
(req->scan_req.scan_f_5ghz &&
(WLAN_REG_IS_5GHZ_CH_FREQ(
reg_chan_list[idx].center_freq) ||
WLAN_REG_IS_49GHZ_FREQ(
reg_chan_list[idx].center_freq) ||
WLAN_REG_IS_6GHZ_CHAN_FREQ(
reg_chan_list[idx].center_freq)))))
scan_freqs[num_chans++] =
reg_chan_list[idx].center_freq;
chan_list = scan_freqs;
}
if (!num_chans) {
/* empty channel list provided */
qdf_mem_zero(&req->scan_req.chan_list,
sizeof(req->scan_req.chan_list));
req->scan_req.chan_list.num_chan = 0;
status = QDF_STATUS_SUCCESS;
goto end;
}
if (!chan_list) {
scm_info("null chan_list while num_chans: %d", num_chans);
status = QDF_STATUS_E_NULL_VALUE;
goto end;
}
if (num_chans > max_chans) {
/* got a big list. alert and fail */
scm_warn("overflow: received %d, max supported : %d",
num_chans, max_chans);
status = QDF_STATUS_E_E2BIG;
goto end;
}
req->scan_req.chan_list.num_chan = num_chans;
for (idx = 0; idx < num_chans; idx++) {
req->scan_req.chan_list.chan[idx].freq =
(chan_list[idx] > WLAN_24_GHZ_BASE_FREQ) ?
chan_list[idx] :
wlan_reg_legacy_chan_to_freq(pdev, chan_list[idx]);
if (phymode)
req->scan_req.chan_list.chan[idx].phymode =
phymode[idx];
else if (req->scan_req.chan_list.chan[idx].freq <=
WLAN_CHAN_15_FREQ)
req->scan_req.chan_list.chan[idx].phymode =
SCAN_PHY_MODE_11G;
else if (req->scan_req.chan_list.chan[idx].freq <=
WLAN_REG_MAX_5GHZ_CHAN_FREQ)
req->scan_req.chan_list.chan[idx].phymode =
SCAN_PHY_MODE_11A;
else
req->scan_req.chan_list.chan[idx].phymode =
SCAN_PHY_MODE_11AX_HE20;
}
end:
if (scan_freqs)
qdf_mem_free(scan_freqs);
if (reg_chan_list)
qdf_mem_free(reg_chan_list);
return QDF_STATUS_SUCCESS;
}
enum scm_scan_status
ucfg_scan_get_vdev_status(struct wlan_objmgr_vdev *vdev)
{
return wlan_get_vdev_status(vdev);
}
enum scm_scan_status
ucfg_scan_get_pdev_status(struct wlan_objmgr_pdev *pdev)
{
return wlan_get_pdev_status(pdev);
}
static void
scan_register_unregister_bcn_cb(struct wlan_objmgr_psoc *psoc,
bool enable)
{
QDF_STATUS status;
struct mgmt_txrx_mgmt_frame_cb_info cb_info[2];
cb_info[0].frm_type = MGMT_PROBE_RESP;
cb_info[0].mgmt_rx_cb = tgt_scan_bcn_probe_rx_callback;
cb_info[1].frm_type = MGMT_BEACON;
cb_info[1].mgmt_rx_cb = tgt_scan_bcn_probe_rx_callback;
if (enable)
status = wlan_mgmt_txrx_register_rx_cb(psoc,
WLAN_UMAC_COMP_SCAN, cb_info, 2);
else
status = wlan_mgmt_txrx_deregister_rx_cb(psoc,
WLAN_UMAC_COMP_SCAN, cb_info, 2);
if (status != QDF_STATUS_SUCCESS)
scm_err("%s the Handle with MGMT TXRX layer has failed",
enable ? "Registering" : "Deregistering");
}
QDF_STATUS ucfg_scan_update_user_config(struct wlan_objmgr_psoc *psoc,
struct scan_user_cfg *scan_cfg)
{
struct wlan_scan_obj *scan_obj;
struct scan_default_params *scan_def;
if (!psoc) {
scm_err("null psoc");
return QDF_STATUS_E_FAILURE;
}
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return QDF_STATUS_E_FAILURE;
}
scan_def = &scan_obj->scan_def;
scan_obj->ie_whitelist = scan_cfg->ie_whitelist;
scan_def->sta_miracast_mcc_rest_time =
scan_cfg->sta_miracast_mcc_rest_time;
return QDF_STATUS_SUCCESS;
}
#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD
static QDF_STATUS
scan_cancel_pdev_scan(struct wlan_objmgr_pdev *pdev)
{
struct scan_cancel_request *req;
QDF_STATUS status;
struct wlan_objmgr_vdev *vdev;
req = qdf_mem_malloc_atomic(sizeof(*req));
if (!req) {
scm_err("Failed to allocate memory");
return QDF_STATUS_E_NOMEM;
}
vdev = wlan_objmgr_pdev_get_first_vdev(pdev, WLAN_SCAN_ID);
if (!vdev) {
scm_err("Failed to get vdev");
qdf_mem_free(req);
return QDF_STATUS_E_INVAL;
}
req->vdev = vdev;
req->cancel_req.scan_id = INVAL_SCAN_ID;
req->cancel_req.pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
req->cancel_req.vdev_id = INVAL_VDEV_ID;
req->cancel_req.req_type = WLAN_SCAN_CANCEL_PDEV_ALL;
status = ucfg_scan_cancel_sync(req);
if (QDF_IS_STATUS_ERROR(status))
scm_err("Cancel scan request failed");
wlan_objmgr_vdev_release_ref(vdev, WLAN_SCAN_ID);
return status;
}
static QDF_STATUS
ucfg_scan_suspend_handler(struct wlan_objmgr_psoc *psoc, void *arg)
{
struct wlan_objmgr_pdev *pdev = NULL;
QDF_STATUS status = QDF_STATUS_SUCCESS;
int i;
wlan_scan_psoc_set_disable(psoc, REASON_SUSPEND);
/* Check all pdev */
for (i = 0; i < WLAN_UMAC_MAX_PDEVS; i++) {
pdev = wlan_objmgr_get_pdev_by_id(psoc, i, WLAN_SCAN_ID);
if (!pdev)
continue;
if (wlan_get_pdev_status(pdev) !=
SCAN_NOT_IN_PROGRESS) {
status = scan_cancel_pdev_scan(pdev);
scm_disable_obss_pdev_scan(psoc, pdev);
}
wlan_objmgr_pdev_release_ref(pdev, WLAN_SCAN_ID);
if (QDF_IS_STATUS_ERROR(status)) {
scm_err("failed to cancel scan for pdev_id %d", i);
return status;
}
}
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS
ucfg_scan_resume_handler(struct wlan_objmgr_psoc *psoc, void *arg)
{
wlan_scan_psoc_set_enable(psoc, REASON_SUSPEND);
return QDF_STATUS_SUCCESS;
}
static inline void
scan_register_pmo_handler(void)
{
pmo_register_suspend_handler(WLAN_UMAC_COMP_SCAN,
ucfg_scan_suspend_handler, NULL);
pmo_register_resume_handler(WLAN_UMAC_COMP_SCAN,
ucfg_scan_resume_handler, NULL);
}
static inline void
scan_unregister_pmo_handler(void)
{
pmo_unregister_suspend_handler(WLAN_UMAC_COMP_SCAN,
ucfg_scan_suspend_handler);
pmo_unregister_resume_handler(WLAN_UMAC_COMP_SCAN,
ucfg_scan_resume_handler);
}
#else
static inline void
scan_register_pmo_handler(void)
{
}
static inline void
scan_unregister_pmo_handler(void)
{
}
#endif
QDF_STATUS
ucfg_scan_psoc_open(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scm_debug("psoc open: 0x%pK", psoc);
if (!psoc) {
scm_err("null psoc");
return QDF_STATUS_E_FAILURE;
}
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return QDF_STATUS_E_FAILURE;
}
/* Initialize the scan Globals */
wlan_scan_global_init(psoc, scan_obj);
qdf_spinlock_create(&scan_obj->lock);
scan_register_pmo_handler();
scm_db_init(psoc);
scm_channel_list_db_init(psoc);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
ucfg_scan_psoc_close(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scm_debug("psoc close: 0x%pK", psoc);
if (!psoc) {
scm_err("null psoc");
return QDF_STATUS_E_FAILURE;
}
scm_db_deinit(psoc);
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return QDF_STATUS_E_FAILURE;
}
scan_unregister_pmo_handler();
qdf_spinlock_destroy(&scan_obj->lock);
wlan_scan_global_deinit(psoc);
scm_channel_list_db_deinit(psoc);
return QDF_STATUS_SUCCESS;
}
static bool scm_serialization_scan_rules_cb(
union wlan_serialization_rules_info *comp_info,
uint8_t comp_id)
{
switch (comp_id) {
case WLAN_UMAC_COMP_TDLS:
if (comp_info->scan_info.is_tdls_in_progress) {
scm_debug("Cancel scan. Tdls in progress");
return false;
}
break;
case WLAN_UMAC_COMP_DFS:
if (comp_info->scan_info.is_cac_in_progress) {
scm_debug("Cancel scan. CAC in progress");
return false;
}
break;
case WLAN_UMAC_COMP_MLME:
if (comp_info->scan_info.is_scan_for_connect) {
scm_debug("Allow scan request from connect");
return true;
}
if (comp_info->scan_info.is_mlme_op_in_progress) {
scm_debug("Cancel scan. MLME operation in progress");
return false;
}
break;
default:
scm_debug("not handled comp_id %d", comp_id);
break;
}
return true;
}
QDF_STATUS
ucfg_scan_psoc_enable(struct wlan_objmgr_psoc *psoc)
{
QDF_STATUS status;
scm_debug("psoc enable: 0x%pK", psoc);
if (!psoc) {
scm_err("null psoc");
return QDF_STATUS_E_FAILURE;
}
/* Subscribe for scan events from lmac layesr */
status = tgt_scan_register_ev_handler(psoc);
QDF_ASSERT(status == QDF_STATUS_SUCCESS);
if (!wlan_reg_is_11d_offloaded(psoc))
scm_11d_cc_db_init(psoc);
scan_register_unregister_bcn_cb(psoc, true);
status = wlan_serialization_register_apply_rules_cb(psoc,
WLAN_SER_CMD_SCAN,
scm_serialization_scan_rules_cb);
QDF_ASSERT(status == QDF_STATUS_SUCCESS);
return status;
}
QDF_STATUS
ucfg_scan_psoc_disable(struct wlan_objmgr_psoc *psoc)
{
QDF_STATUS status;
scm_debug("psoc disable: 0x%pK", psoc);
if (!psoc) {
scm_err("null psoc");
return QDF_STATUS_E_FAILURE;
}
/* Unsubscribe for scan events from lmac layesr */
status = tgt_scan_unregister_ev_handler(psoc);
QDF_ASSERT(status == QDF_STATUS_SUCCESS);
scan_register_unregister_bcn_cb(psoc, false);
if (!wlan_reg_is_11d_offloaded(psoc))
scm_11d_cc_db_deinit(psoc);
return status;
}
uint32_t
ucfg_scan_get_max_active_scans(struct wlan_objmgr_psoc *psoc)
{
struct scan_default_params *scan_params = NULL;
if (!psoc) {
scm_err("null psoc");
return 0;
}
scan_params = wlan_scan_psoc_get_def_params(psoc);
if (!scan_params) {
scm_err("Failed to get scan object");
return 0;
}
return scan_params->max_active_scans_allowed;
}
bool ucfg_copy_ie_whitelist_attrs(struct wlan_objmgr_psoc *psoc,
struct probe_req_whitelist_attr *ie_whitelist)
{
struct wlan_scan_obj *scan_obj = NULL;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj)
return false;
qdf_mem_copy(ie_whitelist, &scan_obj->ie_whitelist,
sizeof(*ie_whitelist));
return true;
}
bool ucfg_ie_whitelist_enabled(struct wlan_objmgr_psoc *psoc,
struct wlan_objmgr_vdev *vdev)
{
struct wlan_scan_obj *scan_obj = NULL;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj)
return false;
if ((wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE) ||
wlan_vdev_is_up(vdev) == QDF_STATUS_SUCCESS)
return false;
if (!scan_obj->ie_whitelist.white_list)
return false;
return true;
}
void ucfg_scan_set_bt_activity(struct wlan_objmgr_psoc *psoc,
bool bt_a2dp_active)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return;
}
scan_obj->bt_a2dp_enabled = bt_a2dp_active;
}
bool ucfg_scan_get_bt_activity(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return false;
}
return scan_obj->bt_a2dp_enabled;
}
bool ucfg_scan_wake_lock_in_user_scan(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj)
return false;
return scan_obj->scan_def.use_wake_lock_in_user_scan;
}
bool ucfg_scan_is_connected_scan_enabled(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return cfg_default(CFG_ENABLE_CONNECTED_SCAN);
}
return scan_obj->scan_def.enable_connected_scan;
}
bool ucfg_scan_is_mac_spoofing_enabled(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return cfg_default(CFG_ENABLE_MAC_ADDR_SPOOFING);
}
return scan_obj->scan_def.enable_mac_spoofing;
}
enum scan_dwelltime_adaptive_mode
ucfg_scan_get_extscan_adaptive_dwell_mode(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return cfg_default(CFG_ADAPTIVE_EXTSCAN_DWELL_MODE);
}
return scan_obj->scan_def.extscan_adaptive_dwell_mode;
}
QDF_STATUS
ucfg_scan_set_global_config(struct wlan_objmgr_psoc *psoc,
enum scan_config config, uint32_t val)
{
struct wlan_scan_obj *scan_obj;
QDF_STATUS status = QDF_STATUS_SUCCESS;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object config:%d, val:%d",
config, val);
return QDF_STATUS_E_INVAL;
}
switch (config) {
case SCAN_CFG_DISABLE_SCAN_COMMAND_TIMEOUT:
scan_obj->disable_timeout = !!val;
break;
case SCAN_CFG_DROP_BCN_ON_CHANNEL_MISMATCH:
scan_obj->drop_bcn_on_chan_mismatch = !!val;
break;
default:
status = QDF_STATUS_E_INVAL;
break;
}
return status;
}
QDF_STATUS ucfg_scan_update_mlme_by_bssinfo(struct wlan_objmgr_pdev *pdev,
struct bss_info *bss_info, struct mlme_info *mlme)
{
QDF_STATUS status;
status = scm_scan_update_mlme_by_bssinfo(pdev, bss_info, mlme);
return status;
}
QDF_STATUS
ucfg_scan_get_global_config(struct wlan_objmgr_psoc *psoc,
enum scan_config config, uint32_t *val)
{
struct wlan_scan_obj *scan_obj;
QDF_STATUS status = QDF_STATUS_SUCCESS;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj || !val) {
scm_err("scan object:%pK config:%d, val:0x%pK",
scan_obj, config, val);
return QDF_STATUS_E_INVAL;
}
switch (config) {
case SCAN_CFG_DISABLE_SCAN_COMMAND_TIMEOUT:
*val = scan_obj->disable_timeout;
break;
case SCAN_CFG_DROP_BCN_ON_CHANNEL_MISMATCH:
*val = scan_obj->drop_bcn_on_chan_mismatch;
break;
default:
status = QDF_STATUS_E_INVAL;
break;
}
return status;
}
void ucfg_scan_set_obss_scan_offload(struct wlan_objmgr_psoc *psoc, bool value)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("NULL scan obj");
return;
}
scan_obj->obss_scan_offload = value;
}
#ifdef FEATURE_WLAN_SCAN_PNO
bool ucfg_scan_is_pno_offload_enabled(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("NULL scan obj");
return false;
}
return scan_obj->pno_cfg.pno_offload_enabled;
}
void ucfg_scan_set_pno_offload(struct wlan_objmgr_psoc *psoc, bool value)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("NULL scan obj");
return;
}
scan_obj->pno_cfg.pno_offload_enabled = value;
}
bool ucfg_scan_get_pno_scan_support(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("NULL scan obj");
return cfg_default(CFG_PNO_SCAN_SUPPORT);
}
return scan_obj->pno_cfg.scan_support_enabled;
}
uint8_t ucfg_get_scan_backoff_multiplier(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("NULL scan obj");
return cfg_default(CFG_SCAN_BACKOFF_MULTIPLIER);
}
return scan_obj->pno_cfg.scan_backoff_multiplier;
}
bool ucfg_scan_is_dfs_chnl_scan_enabled(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("NULL scan obj");
return cfg_default(CFG_ENABLE_DFS_PNO_CHNL_SCAN);
}
return scan_obj->pno_cfg.dfs_chnl_scan_enabled;
}
uint32_t ucfg_scan_get_scan_timer_repeat_value(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("NULL scan obj");
return cfg_default(CFG_PNO_SCAN_TIMER_REPEAT_VALUE);
}
return scan_obj->pno_cfg.scan_timer_repeat_value;
}
uint32_t ucfg_scan_get_slow_scan_multiplier(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("NULL scan obj");
return cfg_default(CFG_PNO_SLOW_SCAN_MULTIPLIER);
}
return scan_obj->pno_cfg.slow_scan_multiplier;
}
uint32_t
ucfg_scan_get_max_sched_scan_plan_interval(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return cfg_default(CFG_MAX_SCHED_SCAN_PLAN_INTERVAL);
}
return scan_obj->pno_cfg.max_sched_scan_plan_interval;
}
uint32_t
ucfg_scan_get_max_sched_scan_plan_iterations(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return cfg_default(CFG_MAX_SCHED_SCAN_PLAN_ITERATIONS);
}
return scan_obj->pno_cfg.max_sched_scan_plan_iterations;
}
bool
ucfg_scan_get_user_config_sched_scan_plan(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("Failed to get scan object");
return cfg_default(CFG_MAX_SCHED_SCAN_PLAN_ITERATIONS);
}
return scan_obj->pno_cfg.user_config_sched_scan_plan;
}
#endif