blob: 697efffce0a7d024caa0f16583bebcb120d2315e [file] [log] [blame]
/*
* Copyright (c) 2017-2019 The Linux Foundation. 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 manager functionality
*/
#include <wlan_serialization_api.h>
#include <wlan_scan_ucfg_api.h>
#include <wlan_scan_tgt_api.h>
#include "wlan_scan_main.h"
#include "wlan_scan_manager.h"
#include "wlan_utility.h"
#include <wlan_reg_services_api.h>
#ifdef FEATURE_WLAN_SCAN_PNO
#include <host_diag_core_event.h>
#endif
#ifdef WLAN_POLICY_MGR_ENABLE
#include <wlan_policy_mgr_api.h>
#endif
#include <wlan_dfs_utils_api.h>
#include <wlan_scan_cfg.h>
/* Beacon/probe weightage multiplier */
#define BCN_PROBE_WEIGHTAGE 5
/* Saved profile weightage multiplier */
#define SAVED_PROFILE_WEIGHTAGE 10
/* maximum number of 6ghz hints can be sent per scan request */
#define MAX_HINTS_PER_SCAN_REQ 15
/* maximum number of hints can be sent per 6ghz channel */
#define MAX_HINTS_PER_CHANNEL 4
QDF_STATUS
scm_scan_free_scan_request_mem(struct scan_start_request *req)
{
void *ie;
if (!req) {
scm_err("null request");
QDF_ASSERT(0);
return QDF_STATUS_E_FAILURE;
}
scm_debug("freed scan request: 0x%pK, scan_id: %d, requester: %d",
req, req->scan_req.scan_id, req->scan_req.scan_req_id);
/* Free vendor(extra) ie */
ie = req->scan_req.extraie.ptr;
if (ie) {
req->scan_req.extraie.ptr = NULL;
req->scan_req.extraie.len = 0;
qdf_mem_free(ie);
}
/* Free htcap ie */
ie = req->scan_req.htcap.ptr;
if (ie) {
req->scan_req.htcap.len = 0;
req->scan_req.htcap.ptr = NULL;
qdf_mem_free(ie);
}
/* Free vhtcap ie */
ie = req->scan_req.vhtcap.ptr;
if (ie) {
req->scan_req.vhtcap.len = 0;
req->scan_req.vhtcap.ptr = NULL;
qdf_mem_free(ie);
}
/* free scan_start_request memory */
qdf_mem_free(req);
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS
scm_scan_get_pdev_global_event_handlers(struct scan_event_listeners *listeners,
struct pdev_scan_ev_handler *pdev_ev_handler)
{
uint32_t i;
struct cb_handler *cb_handlers = &(pdev_ev_handler->cb_handlers[0]);
for (i = 0; i < MAX_SCAN_EVENT_HANDLERS_PER_PDEV; i++, cb_handlers++) {
if ((cb_handlers->func) &&
(listeners->count < MAX_SCAN_EVENT_LISTENERS)) {
listeners->cb[listeners->count].func =
cb_handlers->func;
listeners->cb[listeners->count].arg =
cb_handlers->arg;
listeners->count++;
}
}
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS
scm_scan_get_requester_event_handler(struct scan_event_listeners *listeners,
struct scan_requester_info *requesters,
wlan_scan_requester requester_id)
{
uint32_t idx;
struct cb_handler *ev_handler;
idx = requester_id & WLAN_SCAN_REQUESTER_ID_PREFIX;
if (idx != WLAN_SCAN_REQUESTER_ID_PREFIX)
return QDF_STATUS_SUCCESS;
idx = requester_id & WLAN_SCAN_REQUESTER_ID_MASK;
if (idx < WLAN_MAX_REQUESTORS) {
ev_handler = &(requesters[idx].ev_handler);
if (ev_handler->func) {
if (listeners->count < MAX_SCAN_EVENT_LISTENERS) {
listeners->cb[listeners->count].func =
ev_handler->func;
listeners->cb[listeners->count].arg =
ev_handler->arg;
listeners->count++;
}
}
return QDF_STATUS_SUCCESS;
} else {
scm_err("invalid requester id");
return QDF_STATUS_E_INVAL;
}
}
static void scm_scan_post_event(struct wlan_objmgr_vdev *vdev,
struct scan_event *event)
{
uint32_t i = 0;
struct wlan_scan_obj *scan;
struct pdev_scan_ev_handler *pdev_ev_handler;
struct cb_handler *cb_handlers;
struct scan_requester_info *requesters;
struct scan_event_listeners *listeners;
if (!vdev || !event) {
scm_err("vdev: 0x%pK, event: 0x%pK", vdev, event);
return;
}
if (!event->requester) {
scm_err("invalid requester id");
QDF_ASSERT(0);
}
scan = wlan_vdev_get_scan_obj(vdev);
pdev_ev_handler = wlan_vdev_get_pdev_scan_ev_handlers(vdev);
if (!pdev_ev_handler)
return;
cb_handlers = &(pdev_ev_handler->cb_handlers[0]);
requesters = scan->requesters;
scm_debug("vdev: %d, type: %d, reason: %d, freq: %d, req: %d, scanid: %d",
event->vdev_id, event->type, event->reason, event->chan_freq,
event->requester, event->scan_id);
listeners = qdf_mem_malloc_atomic(sizeof(*listeners));
if (!listeners) {
scm_warn("couldn't allocate listeners list");
return;
}
/* initialize number of listeners */
listeners->count = 0;
/*
* Initiator of scan request decides which all scan events
* he is interested in and FW will send only those scan events
* to host driver.
* All the events received by scan module will be notified
* to all registered handlers.
*/
qdf_spin_lock_bh(&scan->lock);
/* find all global scan event handlers on this pdev */
scm_scan_get_pdev_global_event_handlers(listeners, pdev_ev_handler);
/* find owner who triggered this scan request */
scm_scan_get_requester_event_handler(listeners, requesters,
event->requester);
qdf_spin_unlock_bh(&scan->lock);
/* notify all interested handlers */
for (i = 0; i < listeners->count; i++) {
scm_debug("func: 0x%pK, arg: 0x%pK",
listeners->cb[i].func, listeners->cb[i].arg);
listeners->cb[i].func(vdev, event, listeners->cb[i].arg);
}
qdf_mem_free(listeners);
}
static QDF_STATUS
scm_release_serialization_command(struct wlan_objmgr_vdev *vdev,
uint32_t scan_id)
{
struct wlan_serialization_queued_cmd_info cmd = {0};
cmd.requestor = WLAN_UMAC_COMP_SCAN;
cmd.cmd_type = WLAN_SER_CMD_SCAN;
cmd.cmd_id = scan_id;
cmd.req_type = WLAN_SER_CANCEL_SINGLE_SCAN;
cmd.vdev = vdev;
cmd.queue_type = WLAN_SERIALIZATION_ACTIVE_QUEUE;
/* Inform serialization for command completion */
wlan_serialization_remove_cmd(&cmd);
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS
scm_post_internal_scan_complete_event(struct scan_start_request *req,
enum scan_completion_reason reason)
{
struct scan_event event = {0, };
/* prepare internal scan complete event */
event.type = SCAN_EVENT_TYPE_COMPLETED;
event.reason = reason;
event.chan_freq = 0; /* Invalid frequency */
event.vdev_id = req->scan_req.vdev_id;
event.requester = req->scan_req.scan_req_id;
event.scan_id = req->scan_req.scan_id;
/* Fill scan_start_request used to trigger this scan */
event.scan_start_req = req;
/* post scan event to registered handlers */
scm_scan_post_event(req->vdev, &event);
return QDF_STATUS_SUCCESS;
}
static inline struct pdev_scan_info *
scm_scan_get_pdev_priv_info(uint8_t pdev_id, struct wlan_scan_obj *scan_obj)
{
return &scan_obj->pdev_info[pdev_id];
}
static QDF_STATUS
scm_update_last_scan_time(struct scan_start_request *req)
{
uint8_t pdev_id;
struct wlan_scan_obj *scan_obj;
struct pdev_scan_info *pdev_scan_info;
scan_obj = wlan_vdev_get_scan_obj(req->vdev);
pdev_id = wlan_scan_vdev_get_pdev_id(req->vdev);
pdev_scan_info = scm_scan_get_pdev_priv_info(pdev_id, scan_obj);
/* update last scan start time */
pdev_scan_info->last_scan_time = qdf_system_ticks();
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS
scm_activate_scan_request(struct scan_start_request *req)
{
QDF_STATUS status;
status = tgt_scan_start(req);
if (status != QDF_STATUS_SUCCESS) {
scm_err("tgt_scan_start failed, status: %d", status);
/* scan could not be started and hence
* we will not receive any completions.
* post scan cancelled
*/
scm_post_internal_scan_complete_event(req,
SCAN_REASON_CANCELLED);
return status;
}
/* save last scan start time */
status = scm_update_last_scan_time(req);
return status;
}
static QDF_STATUS
scm_cancel_scan_request(struct scan_start_request *req)
{
struct scan_cancel_request cancel_req = {0, };
QDF_STATUS status;
cancel_req.vdev = req->vdev;
cancel_req.cancel_req.scan_id = req->scan_req.scan_id;
cancel_req.cancel_req.requester = req->scan_req.scan_req_id;
cancel_req.cancel_req.req_type = WLAN_SCAN_CANCEL_SINGLE;
cancel_req.cancel_req.vdev_id = req->scan_req.vdev_id;
/* send scan cancel to fw */
status = tgt_scan_cancel(&cancel_req);
if (status != QDF_STATUS_SUCCESS)
scm_err("tgt_scan_cancel failed: status: %d, scanid: %d",
status, req->scan_req.scan_id);
/* notify event handler about scan cancellation */
scm_post_internal_scan_complete_event(req, SCAN_REASON_CANCELLED);
return status;
}
static QDF_STATUS
scm_scan_serialize_callback(struct wlan_serialization_command *cmd,
enum wlan_serialization_cb_reason reason)
{
struct scan_start_request *req;
QDF_STATUS status;
if (!cmd) {
scm_err("cmd is NULL, reason: %d", reason);
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
if (!cmd->umac_cmd) {
scm_err("cmd->umac_cmd is NULL , reason: %d", reason);
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
req = cmd->umac_cmd;
scm_debug("reason:%d, reqid:%d, scanid:%d, vdevid:%d, vdev:0x%pK",
reason, req->scan_req.scan_req_id, req->scan_req.scan_id,
req->scan_req.vdev_id, req->vdev);
if (!req->vdev) {
scm_err("NULL vdev. req:0x%pK, reason:%d\n", req, reason);
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
qdf_mtrace(QDF_MODULE_ID_SERIALIZATION, QDF_MODULE_ID_SCAN, reason,
req->scan_req.vdev_id, req->scan_req.scan_id);
switch (reason) {
case WLAN_SER_CB_ACTIVATE_CMD:
/* command moved to active list
* modify the params if required for concurency case.
*/
status = scm_activate_scan_request(req);
break;
case WLAN_SER_CB_CANCEL_CMD:
/* command removed from pending list.
* notify registered scan event handlers with
* status completed and reason cancelled.
*/
status = scm_post_internal_scan_complete_event(req,
SCAN_REASON_CANCELLED);
break;
case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
/* active command timed out.
* prepare internal scan cancel request
*/
status = scm_cancel_scan_request(req);
break;
case WLAN_SER_CB_RELEASE_MEM_CMD:
/* command successfully completed.
* Release vdev reference and free scan_start_request memory
*/
cmd->umac_cmd = NULL;
wlan_objmgr_vdev_release_ref(req->vdev, WLAN_SCAN_ID);
status = scm_scan_free_scan_request_mem(req);
break;
default:
/* Do nothing but logging */
QDF_ASSERT(0);
status = QDF_STATUS_E_INVAL;
break;
}
return status;
}
bool scm_is_scan_allowed(struct wlan_objmgr_vdev *vdev)
{
struct wlan_scan_obj *scan_psoc_obj;
struct scan_vdev_obj *scan_vdev_obj;
if (!vdev) {
scm_err("vdev is NULL");
return false;
}
scan_psoc_obj = wlan_vdev_get_scan_obj(vdev);
if (!scan_psoc_obj) {
scm_err("Couldn't find scan psoc object");
return false;
}
if (scan_psoc_obj->scan_disabled) {
scm_err_rl("scan disabled %x, for psoc",
scan_psoc_obj->scan_disabled);
return false;
}
scan_vdev_obj = wlan_get_vdev_scan_obj(vdev);
if (!scan_vdev_obj) {
scm_err("Couldn't find scan vdev object");
return false;
}
if (scan_vdev_obj->scan_disabled) {
scm_err_rl("scan disabled %x on vdev_id:%d",
scan_vdev_obj->scan_disabled,
wlan_vdev_get_id(vdev));
return false;
}
return true;
}
#ifdef WLAN_POLICY_MGR_ENABLE
/**
* scm_update_dbs_scan_ctrl_ext_flag() - update dbs scan ctrl flags
* @req: pointer to scan request
*
* This function sets scan_ctrl_flags_ext value depending on the type of
* scan and the channel lists.
*
* Non-DBS scan is requested if any of the below case is met:
* 1. HW is DBS incapable
* 2. A high accuracy scan request is sent by kernel.
*
* DBS scan is enabled for these conditions:
* 1. A low power or low span scan request is sent by kernel.
* For remaining cases DBS is enabled by default.
* Return: void
*/
static void
scm_update_dbs_scan_ctrl_ext_flag(struct scan_start_request *req)
{
struct wlan_objmgr_psoc *psoc;
uint32_t scan_dbs_policy = SCAN_DBS_POLICY_DEFAULT;
psoc = wlan_vdev_get_psoc(req->vdev);
if (!policy_mgr_is_dbs_scan_allowed(psoc)) {
scm_debug("dbs disabled, going for non-dbs scan");
scan_dbs_policy = SCAN_DBS_POLICY_FORCE_NONDBS;
goto end;
}
if (!wlan_scan_cfg_honour_nl_scan_policy_flags(psoc)) {
scm_debug("nl scan policy flags not honoured, goto end");
goto end;
}
if (req->scan_req.scan_policy_high_accuracy) {
scm_debug("high accuracy scan received, going for non-dbs scan");
scan_dbs_policy = SCAN_DBS_POLICY_FORCE_NONDBS;
goto end;
}
if ((req->scan_req.scan_policy_low_power) ||
(req->scan_req.scan_policy_low_span)) {
scm_debug("low power/span scan received, going for dbs scan");
scan_dbs_policy = SCAN_DBS_POLICY_IGNORE_DUTY;
goto end;
}
end:
req->scan_req.scan_ctrl_flags_ext |=
((scan_dbs_policy << SCAN_FLAG_EXT_DBS_SCAN_POLICY_BIT)
& SCAN_FLAG_EXT_DBS_SCAN_POLICY_MASK);
scm_debug("scan_ctrl_flags_ext: 0x%x",
req->scan_req.scan_ctrl_flags_ext);
}
/**
* scm_update_passive_dwell_time() - update dwell passive time
* @vdev: vdev object
* @req: scan request
*
* Return: None
*/
static void
scm_update_passive_dwell_time(struct wlan_objmgr_vdev *vdev,
struct scan_start_request *req)
{
struct wlan_objmgr_psoc *psoc;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc)
return;
if (policy_mgr_is_sta_connected_2g(psoc) &&
!policy_mgr_is_hw_dbs_capable(psoc) &&
ucfg_scan_get_bt_activity(psoc))
req->scan_req.dwell_time_passive =
PASSIVE_DWELL_TIME_BT_A2DP_ENABLED;
}
static const struct probe_time_dwell_time
scan_probe_time_dwell_time_map[SCAN_DWELL_TIME_PROBE_TIME_MAP_SIZE] = {
{28, 11}, /* 0 SSID */
{28, 20}, /* 1 SSID */
{28, 20}, /* 2 SSID */
{28, 20}, /* 3 SSID */
{28, 20}, /* 4 SSID */
{28, 20}, /* 5 SSID */
{28, 20}, /* 6 SSID */
{28, 11}, /* 7 SSID */
{28, 11}, /* 8 SSID */
{28, 11}, /* 9 SSID */
{28, 8} /* 10 SSID */
};
/**
* scm_scan_get_burst_duration() - get burst duration depending on max chan
* and miracast.
* @max_ch_time: max channel time
* @miracast_enabled: if miracast is enabled
*
* Return: burst_duration
*/
static inline
int scm_scan_get_burst_duration(int max_ch_time, bool miracast_enabled)
{
int burst_duration = 0;
if (miracast_enabled) {
/*
* When miracast is running, burst
* duration needs to be minimum to avoid
* any stutter or glitch in miracast
* during station scan
*/
if (max_ch_time <= SCAN_GO_MIN_ACTIVE_SCAN_BURST_DURATION)
burst_duration = max_ch_time;
else
burst_duration = SCAN_GO_MIN_ACTIVE_SCAN_BURST_DURATION;
} else {
/*
* If miracast is not running, accommodate max
* stations to make the scans faster
*/
burst_duration = SCAN_GO_BURST_SCAN_MAX_NUM_OFFCHANNELS *
max_ch_time;
if (burst_duration > SCAN_GO_MAX_ACTIVE_SCAN_BURST_DURATION) {
uint8_t channels = SCAN_P2P_SCAN_MAX_BURST_DURATION /
max_ch_time;
if (channels)
burst_duration = channels * max_ch_time;
else
burst_duration =
SCAN_GO_MAX_ACTIVE_SCAN_BURST_DURATION;
}
}
return burst_duration;
}
/**
* scm_req_update_concurrency_params() - update scan req params depending on
* concurrent mode present.
* @vdev: vdev object pointer
* @req: scan request
* @scan_obj: scan object
*
* Return: void
*/
static void scm_req_update_concurrency_params(struct wlan_objmgr_vdev *vdev,
struct scan_start_request *req,
struct wlan_scan_obj *scan_obj)
{
bool ap_present, go_present, sta_active, p2p_cli_present, ndi_present;
struct wlan_objmgr_psoc *psoc;
uint16_t sap_peer_count = 0;
uint16_t go_peer_count = 0;
struct wlan_objmgr_pdev *pdev;
psoc = wlan_vdev_get_psoc(vdev);
pdev = wlan_vdev_get_pdev(vdev);
if (!psoc || !pdev)
return;
ap_present = policy_mgr_mode_specific_connection_count(
psoc, PM_SAP_MODE, NULL);
go_present = policy_mgr_mode_specific_connection_count(
psoc, PM_P2P_GO_MODE, NULL);
p2p_cli_present = policy_mgr_mode_specific_connection_count(
psoc, PM_P2P_CLIENT_MODE, NULL);
sta_active = policy_mgr_mode_specific_connection_count(
psoc, PM_STA_MODE, NULL);
ndi_present = policy_mgr_mode_specific_connection_count(
psoc, PM_NDI_MODE, NULL);
if (ap_present)
sap_peer_count =
wlan_util_get_peer_count_for_mode(pdev, QDF_SAP_MODE);
if (go_present)
go_peer_count =
wlan_util_get_peer_count_for_mode(pdev, QDF_P2P_GO_MODE);
if (!req->scan_req.scan_f_passive)
scm_update_passive_dwell_time(vdev, req);
if (policy_mgr_get_connection_count(psoc)) {
if (req->scan_req.scan_f_passive)
req->scan_req.dwell_time_passive =
scan_obj->scan_def.conc_passive_dwell;
else
req->scan_req.dwell_time_active =
scan_obj->scan_def.conc_active_dwell;
req->scan_req.max_rest_time =
scan_obj->scan_def.conc_max_rest_time;
req->scan_req.min_rest_time =
scan_obj->scan_def.conc_min_rest_time;
req->scan_req.idle_time = scan_obj->scan_def.conc_idle_time;
}
if (wlan_vdev_is_up(req->vdev) != QDF_STATUS_SUCCESS)
req->scan_req.adaptive_dwell_time_mode =
scan_obj->scan_def.adaptive_dwell_time_mode_nc;
/*
* If AP/GO is active and has connected clients :
* 1.set min rest time same as max rest time, so that
* firmware spends more time on home channel which will
* increase the probability of sending beacon at TBTT
* 2.if DBS is supported and SAP is not on 2g,
* do not reset active dwell time for 2g.
*/
if ((ap_present && sap_peer_count) ||
(go_present && go_peer_count)) {
if (policy_mgr_is_hw_dbs_capable(psoc) &&
policy_mgr_is_sap_go_on_2g(psoc)) {
req->scan_req.dwell_time_active_2g =
QDF_MIN(req->scan_req.dwell_time_active,
(SCAN_CTS_DURATION_MS_MAX -
SCAN_ROAM_SCAN_CHANNEL_SWITCH_TIME));
}
req->scan_req.min_rest_time = req->scan_req.max_rest_time;
}
/*
* If scan req for SAP (ACS Sacn) use dwell_time_active_def as dwell
* time for 2g channels instead of dwell_time_active_2g
*/
if (vdev->vdev_mlme.vdev_opmode == QDF_SAP_MODE)
req->scan_req.dwell_time_active_2g = 0;
if (req->scan_req.scan_type == SCAN_TYPE_DEFAULT) {
/*
* Decide burst_duration and dwell_time_active based on
* what type of devices are active.
*/
do {
if (ap_present && go_present && sta_active) {
if (req->scan_req.dwell_time_active <=
SCAN_3PORT_CONC_SCAN_MAX_BURST_DURATION)
req->scan_req.burst_duration =
req->scan_req.dwell_time_active;
else
req->scan_req.burst_duration =
SCAN_3PORT_CONC_SCAN_MAX_BURST_DURATION;
break;
}
if (scan_obj->miracast_enabled &&
policy_mgr_is_mcc_in_24G(psoc))
req->scan_req.max_rest_time =
scan_obj->scan_def.sta_miracast_mcc_rest_time;
if (go_present) {
/*
* Background scan while GO is sending beacons.
* Every off-channel transition has overhead of
* 2 beacon intervals for NOA. Maximize number
* of channels in every transition by using
* burst scan.
*/
if (scan_obj->scan_def.go_scan_burst_duration)
req->scan_req.burst_duration =
scan_obj->
scan_def.go_scan_burst_duration;
else
req->scan_req.burst_duration =
scm_scan_get_burst_duration(
req->scan_req.
dwell_time_active,
scan_obj->
miracast_enabled);
break;
}
if ((sta_active || p2p_cli_present)) {
if (scan_obj->scan_def.sta_scan_burst_duration)
req->scan_req.burst_duration =
scan_obj->scan_def.
sta_scan_burst_duration;
break;
}
if (ndi_present) {
req->scan_req.burst_duration =
scm_scan_get_burst_duration(
req->scan_req.dwell_time_active,
scan_obj->miracast_enabled);
break;
}
} while (0);
if (ap_present) {
uint8_t ssid_num;
ssid_num = req->scan_req.num_ssids *
req->scan_req.num_bssid;
req->scan_req.repeat_probe_time =
scan_probe_time_dwell_time_map[
QDF_MIN(ssid_num,
SCAN_DWELL_TIME_PROBE_TIME_MAP_SIZE
- 1)].probe_time;
req->scan_req.n_probes =
(req->scan_req.repeat_probe_time > 0) ?
req->scan_req.dwell_time_active /
req->scan_req.repeat_probe_time : 0;
}
}
if (ap_present) {
uint16_t ap_chan_freq;
struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev);
ap_chan_freq = policy_mgr_get_channel(psoc, PM_SAP_MODE, NULL);
/*
* P2P/STA scan while SoftAP is sending beacons.
* Max duration of CTS2self is 32 ms, which limits the
* dwell time.
* If DBS is supported and:
* 1.if SAP is on 2G channel then keep passive
* dwell time default.
* 2.if SAP is on 5G/6G channel then update dwell time active.
*/
if (sap_peer_count) {
if (policy_mgr_is_hw_dbs_capable(psoc) &&
(WLAN_REG_IS_5GHZ_CH_FREQ(ap_chan_freq) ||
WLAN_REG_IS_6GHZ_CHAN_FREQ(ap_chan_freq))) {
req->scan_req.dwell_time_active =
QDF_MIN(req->scan_req.dwell_time_active,
(SCAN_CTS_DURATION_MS_MAX -
SCAN_ROAM_SCAN_CHANNEL_SWITCH_TIME));
}
if (!policy_mgr_is_hw_dbs_capable(psoc) ||
(policy_mgr_is_hw_dbs_capable(psoc) &&
WLAN_REG_IS_5GHZ_CH_FREQ(ap_chan_freq))) {
req->scan_req.dwell_time_passive =
req->scan_req.dwell_time_active;
}
}
if (scan_obj->scan_def.ap_scan_burst_duration) {
req->scan_req.burst_duration =
scan_obj->scan_def.ap_scan_burst_duration;
} else {
req->scan_req.burst_duration = 0;
if (wlan_reg_is_dfs_for_freq(pdev, ap_chan_freq))
req->scan_req.burst_duration =
SCAN_BURST_SCAN_MAX_NUM_OFFCHANNELS *
req->scan_req.dwell_time_active;
}
}
}
/**
* scm_scan_chlist_concurrency_modify() - modify chan list to skip 5G if
* required
* @vdev: vdev object
* @req: scan request
*
* Check and skip 5G chan list based on DFS AP present and current hw mode.
*
* Return: void
*/
static inline void scm_scan_chlist_concurrency_modify(
struct wlan_objmgr_vdev *vdev, struct scan_start_request *req)
{
struct wlan_objmgr_psoc *psoc;
uint32_t i;
uint32_t num_scan_channels;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc)
return;
/* do this only for STA and P2P-CLI mode */
if (!(wlan_vdev_mlme_get_opmode(req->vdev) == QDF_STA_MODE) &&
!(wlan_vdev_mlme_get_opmode(req->vdev) == QDF_P2P_CLIENT_MODE))
return;
if (!policy_mgr_scan_trim_5g_chnls_for_dfs_ap(psoc))
return;
num_scan_channels = 0;
for (i = 0; i < req->scan_req.chan_list.num_chan; i++) {
if (WLAN_REG_IS_5GHZ_CH_FREQ(
req->scan_req.chan_list.chan[i].freq)) {
continue;
}
req->scan_req.chan_list.chan[num_scan_channels++] =
req->scan_req.chan_list.chan[i];
}
if (num_scan_channels < req->scan_req.chan_list.num_chan)
scm_debug("5g chan skipped (%d, %d)",
req->scan_req.chan_list.num_chan, num_scan_channels);
req->scan_req.chan_list.num_chan = num_scan_channels;
}
#else
static inline
void scm_req_update_concurrency_params(struct wlan_objmgr_vdev *vdev,
struct scan_start_request *req,
struct wlan_scan_obj *scan_obj)
{
}
static inline void
scm_update_dbs_scan_ctrl_ext_flag(struct scan_start_request *req)
{
}
static inline void scm_scan_chlist_concurrency_modify(
struct wlan_objmgr_vdev *vdev, struct scan_start_request *req)
{
}
#endif
#ifdef CONFIG_BAND_6GHZ
static void
scm_update_6ghz_channel_list(struct wlan_objmgr_vdev *vdev,
struct chan_list *chan_list,
struct wlan_scan_obj *scan_obj)
{
uint8_t i;
struct regulatory_channel *chan_list_6g;
bool psc_channel_found = false;
bool channel_6g_found = false;
uint8_t num_scan_channels = 0, channel_count;
struct wlan_objmgr_pdev *pdev;
uint32_t freq;
pdev = wlan_vdev_get_pdev(vdev);
if (!pdev)
return;
scm_debug("6g scan mode %d", scan_obj->scan_def.scan_mode_6g);
for (i = 0; i < chan_list->num_chan; i++) {
freq = chan_list->chan[i].freq;
if ((scan_obj->scan_def.scan_mode_6g ==
SCAN_MODE_6G_NO_CHANNEL) &&
(wlan_reg_is_6ghz_chan_freq(freq))) {
/* Drop the 6Ghz channels */
continue;
} else if ((scan_obj->scan_def.scan_mode_6g ==
SCAN_MODE_6G_PSC_CHANNEL) &&
(wlan_reg_is_6ghz_chan_freq(freq))) {
/* Allow only PSC channels */
if (wlan_reg_is_6ghz_psc_chan_freq(freq))
psc_channel_found = true;
else
continue;
} else if ((scan_obj->scan_def.scan_mode_6g ==
SCAN_MODE_6G_ALL_CHANNEL) &&
(wlan_reg_is_6ghz_chan_freq(freq))) {
/* Allow any 6ghz channel */
channel_6g_found = true;
}
chan_list->chan[num_scan_channels++] =
chan_list->chan[i];
}
scm_debug("psc_channel_found %d channel_6g_found%d",
psc_channel_found, channel_6g_found);
if ((scan_obj->scan_def.scan_mode_6g == SCAN_MODE_6G_PSC_CHANNEL &&
!psc_channel_found) ||
(scan_obj->scan_def.scan_mode_6g == SCAN_MODE_6G_ALL_CHANNEL &&
!channel_6g_found)) {
chan_list_6g = qdf_mem_malloc(NUM_6GHZ_CHANNELS *
sizeof(struct regulatory_channel));
if (!chan_list_6g)
goto end;
/* Add the 6Ghz channels based on config*/
channel_count = wlan_reg_get_band_channel_list(pdev,
BIT(REG_BAND_6G),
chan_list_6g);
scm_debug("Number of 6G channels %d", channel_count);
for (i = 0; i < channel_count; i++) {
if ((scan_obj->scan_def.scan_mode_6g ==
SCAN_MODE_6G_PSC_CHANNEL) &&
(!psc_channel_found) &&
wlan_reg_is_6ghz_psc_chan_freq(chan_list_6g[i].
center_freq)) {
chan_list->chan[num_scan_channels++].freq =
chan_list_6g[i].center_freq;
} else if ((scan_obj->scan_def.scan_mode_6g ==
SCAN_MODE_6G_ALL_CHANNEL) &&
(!channel_6g_found)) {
chan_list->chan[num_scan_channels++].freq =
chan_list_6g[i].center_freq;
}
}
qdf_mem_free(chan_list_6g);
}
scm_debug("Number of channels to scan %d", num_scan_channels);
for (i = 0; i < num_scan_channels; i++)
scm_debug("channels to scan %d", chan_list->chan[i].freq);
end:
chan_list->num_chan = num_scan_channels;
}
#else
static void
scm_update_6ghz_channel_list(struct wlan_objmgr_vdev *vdev,
struct chan_list *chan_list,
struct wlan_scan_obj *scan_obj)
{
}
#endif
#ifdef FEATURE_6G_SCAN_CHAN_SORT_ALGO
static void scm_sort_6ghz_channel_list(struct wlan_objmgr_vdev *vdev,
struct chan_list *chan_list)
{
uint8_t i, j = 0, min, tmp_list_count;
struct meta_rnr_channel *channel;
struct chan_info temp_list[MAX_6GHZ_CHANNEL];
struct rnr_chan_weight *rnr_chan_info, *temp;
uint32_t weight;
rnr_chan_info = qdf_mem_malloc(sizeof(rnr_chan_info) * MAX_6GHZ_CHANNEL);
if (!rnr_chan_info)
return;
for (i = 0; i < chan_list->num_chan; i++) {
if (WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_list->chan[i].freq))
temp_list[j++].freq = chan_list->chan[i].freq;
}
tmp_list_count = j;
scm_debug("Total 6ghz channels %d", tmp_list_count);
/* No Need to sort if the 6ghz channels are less than one */
if (tmp_list_count < 1) {
qdf_mem_free(rnr_chan_info);
return;
}
/* compute the weightage */
for (i = 0; i < tmp_list_count; i++) {
channel = scm_get_chan_meta(temp_list[i].freq);
weight = channel->bss_beacon_probe_count * BCN_PROBE_WEIGHTAGE +
channel->saved_profile_count * SAVED_PROFILE_WEIGHTAGE;
rnr_chan_info[i].weight = weight;
rnr_chan_info[i].chan_freq = temp_list[i].freq;
}
/* Sort the channel using selection sort */
for (i = 0; i < tmp_list_count - 1; i++) {
min = i;
for (j = i + 1; j < tmp_list_count; j++) {
if (rnr_chan_info[j].weight <
rnr_chan_info[min].weight) {
min = j;
}
}
if (min != i) {
qdf_mem_copy(&temp, &rnr_chan_info[min],
sizeof(*rnr_chan_info));
qdf_mem_copy(&rnr_chan_info[min], &rnr_chan_info[i],
sizeof(*rnr_chan_info));
qdf_mem_copy(&rnr_chan_info[i], &temp,
sizeof(*rnr_chan_info));
}
}
/* update the 6g list based on the weightage */
for (i = 0, j = 0;
(i < NUM_CHANNELS && j < NUM_6GHZ_CHANNELS); i++) {
if (wlan_reg_is_6ghz_chan_freq(chan_list->chan[i].freq))
chan_list->chan[i].freq = rnr_chan_info[j++].chan_freq;
}
qdf_mem_free(rnr_chan_info);
}
static void scm_update_rnr_info(struct scan_start_request *req)
{
uint8_t i, num_bssid = 0, num_ssid = 0;
uint8_t total_count = MAX_HINTS_PER_SCAN_REQ;
uint32_t freq;
struct meta_rnr_channel *chan;
qdf_list_node_t *cur_node, *next_node;
struct scan_rnr_node *rnr_node;
struct chan_list *chan_list;
if (!req)
return;
chan_list = &req->scan_req.chan_list;
for (i = 0; i < chan_list->num_chan; i++) {
freq = chan_list->chan[i].freq;
if (!wlan_reg_is_6ghz_chan_freq(freq))
continue;
chan = scm_get_chan_meta(freq);
if (qdf_list_empty(&chan->rnr_list))
continue;
qdf_list_peek_front(&chan->rnr_list, &cur_node);
while (cur_node && total_count) {
qdf_list_peek_next(&chan->rnr_list, cur_node,
&next_node);
rnr_node = qdf_container_of(cur_node,
struct scan_rnr_node,
node);
if (!qdf_is_macaddr_zero(&rnr_node->entry.bssid)) {
qdf_mem_copy(&req->scan_req.hint_bssid[num_bssid++].bssid,
&rnr_node->entry.bssid,
QDF_MAC_ADDR_SIZE);
req->scan_req.num_hint_bssid++;
total_count--;
} else if (rnr_node->entry.short_ssid) {
req->scan_req.hint_s_ssid[num_ssid++].short_ssid =
rnr_node->entry.short_ssid;
req->scan_req.num_hint_s_ssid++;
total_count--;
}
cur_node = next_node;
next_node = NULL;
}
}
}
#else
static void scm_sort_6ghz_channel_list(struct wlan_objmgr_vdev *vdev,
struct chan_list *chan_list)
{
}
static void scm_update_rnr_info(struct scan_start_request *req)
{
}
#endif
/**
* scm_update_channel_list() - update scan req params depending on dfs inis
* and initial scan request.
* @req: scan request
* @scan_obj: scan object
*
* Return: void
*/
static void
scm_update_channel_list(struct scan_start_request *req,
struct wlan_scan_obj *scan_obj)
{
uint8_t i;
uint8_t num_scan_channels = 0;
struct scan_vdev_obj *scan_vdev_obj;
struct wlan_objmgr_pdev *pdev;
bool first_scan_done = true;
bool p2p_search = false;
bool skip_dfs_ch = true;
uint32_t first_freq;
pdev = wlan_vdev_get_pdev(req->vdev);
scan_vdev_obj = wlan_get_vdev_scan_obj(req->vdev);
if (!scan_vdev_obj) {
scm_err("null scan_vdev_obj");
return;
}
if (!scan_vdev_obj->first_scan_done) {
first_scan_done = false;
scan_vdev_obj->first_scan_done = true;
}
if (req->scan_req.scan_type == SCAN_TYPE_P2P_SEARCH)
p2p_search = true;
/*
* No need to update channels if req is single channel* ie ROC,
* Preauth or a single channel scan etc.
* If the single chan in the scan channel list is an NOL channel,it is
* removed and it would reduce the number of scan channels to 0.
*/
first_freq = req->scan_req.chan_list.chan[0].freq;
if ((req->scan_req.chan_list.num_chan == 1) &&
(!utils_dfs_is_freq_in_nol(pdev, first_freq)))
return;
/* do this only for STA and P2P-CLI mode */
if ((!(wlan_vdev_mlme_get_opmode(req->vdev) == QDF_STA_MODE) &&
!(wlan_vdev_mlme_get_opmode(req->vdev) == QDF_P2P_CLIENT_MODE)) &&
!p2p_search)
skip_dfs_ch = false;
if ((scan_obj->scan_def.allow_dfs_chan_in_scan &&
(scan_obj->scan_def.allow_dfs_chan_in_first_scan ||
first_scan_done)) &&
!(scan_obj->scan_def.skip_dfs_chan_in_p2p_search && p2p_search))
skip_dfs_ch = false;
for (i = 0; i < req->scan_req.chan_list.num_chan; i++) {
uint32_t freq;
freq = req->scan_req.chan_list.chan[i].freq;
if (skip_dfs_ch &&
wlan_reg_chan_has_dfs_attribute_for_freq(pdev, freq))
continue;
if (utils_dfs_is_freq_in_nol(pdev, freq))
continue;
req->scan_req.chan_list.chan[num_scan_channels++] =
req->scan_req.chan_list.chan[i];
}
req->scan_req.chan_list.num_chan = num_scan_channels;
/* Dont upadte the channel list for SAP mode */
if (wlan_vdev_mlme_get_opmode(req->vdev) != QDF_SAP_MODE) {
scm_update_6ghz_channel_list(req->vdev,
&req->scan_req.chan_list,
scan_obj);
scm_sort_6ghz_channel_list(req->vdev, &req->scan_req.chan_list);
}
scm_scan_chlist_concurrency_modify(req->vdev, req);
}
/**
* scm_scan_req_update_params() - update scan req params depending on modes
* and scan type.
* @vdev: vdev object pointer
* @req: scan request
* @scan_obj: scan object
*
* Return: void
*/
static void
scm_scan_req_update_params(struct wlan_objmgr_vdev *vdev,
struct scan_start_request *req,
struct wlan_scan_obj *scan_obj)
{
struct chan_list *custom_chan_list;
struct wlan_objmgr_pdev *pdev;
uint8_t pdev_id;
/* Ensure correct number of probes are sent on active channel */
if (!req->scan_req.repeat_probe_time)
req->scan_req.repeat_probe_time =
req->scan_req.dwell_time_active / SCAN_NPROBES_DEFAULT;
if (req->scan_req.scan_f_passive)
req->scan_req.scan_ctrl_flags_ext |=
SCAN_FLAG_EXT_FILTER_PUBLIC_ACTION_FRAME;
if (!req->scan_req.n_probes)
req->scan_req.n_probes = (req->scan_req.repeat_probe_time > 0) ?
req->scan_req.dwell_time_active /
req->scan_req.repeat_probe_time : 0;
if (req->scan_req.scan_type == SCAN_TYPE_P2P_SEARCH ||
req->scan_req.scan_type == SCAN_TYPE_P2P_LISTEN) {
req->scan_req.adaptive_dwell_time_mode = SCAN_DWELL_MODE_STATIC;
req->scan_req.dwell_time_active_2g = 0;
if (req->scan_req.scan_type == SCAN_TYPE_P2P_LISTEN) {
req->scan_req.repeat_probe_time = 0;
} else {
req->scan_req.scan_f_filter_prb_req = true;
if (!req->scan_req.num_ssids)
req->scan_req.scan_f_bcast_probe = true;
req->scan_req.dwell_time_active +=
P2P_SEARCH_DWELL_TIME_INC;
/*
* 3 channels with default max dwell time 40 ms.
* Cap limit will be set by
* P2P_SCAN_MAX_BURST_DURATION. Burst duration
* should be such that no channel is scanned less
* than the dwell time in normal scenarios.
*/
if (req->scan_req.chan_list.num_chan ==
WLAN_P2P_SOCIAL_CHANNELS &&
!scan_obj->miracast_enabled)
req->scan_req.repeat_probe_time =
req->scan_req.dwell_time_active / 5;
else
req->scan_req.repeat_probe_time =
req->scan_req.dwell_time_active / 3;
if (scan_obj->scan_def.p2p_scan_burst_duration) {
req->scan_req.burst_duration =
scan_obj->scan_def.
p2p_scan_burst_duration;
} else {
req->scan_req.burst_duration =
BURST_SCAN_MAX_NUM_OFFCHANNELS *
req->scan_req.dwell_time_active;
if (req->scan_req.burst_duration >
P2P_SCAN_MAX_BURST_DURATION) {
uint8_t channels =
P2P_SCAN_MAX_BURST_DURATION /
req->scan_req.dwell_time_active;
if (channels)
req->scan_req.burst_duration =
channels *
req->scan_req.dwell_time_active;
else
req->scan_req.burst_duration =
P2P_SCAN_MAX_BURST_DURATION;
}
}
req->scan_req.scan_ev_bss_chan = false;
}
} else {
req->scan_req.scan_f_cck_rates = true;
if (!req->scan_req.num_ssids)
req->scan_req.scan_f_bcast_probe = true;
req->scan_req.scan_f_add_ds_ie_in_probe = true;
req->scan_req.scan_f_filter_prb_req = true;
req->scan_req.scan_f_add_tpc_ie_in_probe = true;
}
scm_update_dbs_scan_ctrl_ext_flag(req);
/*
* No need to update conncurrency parmas if req is passive scan on
* single channel ie ROC, Preauth etc
*/
if (!(req->scan_req.scan_f_passive &&
req->scan_req.chan_list.num_chan == 1) &&
req->scan_req.scan_type != SCAN_TYPE_RRM)
scm_req_update_concurrency_params(vdev, req, scan_obj);
/*
* Set wide band flag if enabled. This will cause
* phymode TLV being sent to FW.
*/
pdev = wlan_vdev_get_pdev(vdev);
pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
if (ucfg_scan_get_wide_band_scan(pdev))
req->scan_req.scan_f_wide_band = true;
else
req->scan_req.scan_f_wide_band = false;
/*
* Overwrite scan channles with custom scan channel
* list if configured.
*/
custom_chan_list = &scan_obj->pdev_info[pdev_id].custom_chan_list;
if (custom_chan_list->num_chan)
qdf_mem_copy(&req->scan_req.chan_list, custom_chan_list,
sizeof(struct chan_list));
else if (!req->scan_req.chan_list.num_chan)
ucfg_scan_init_chanlist_params(req, 0, NULL, NULL);
scm_update_channel_list(req, scan_obj);
scm_update_rnr_info(req);
scm_debug("dwell time: active %d, passive %d, repeat_probe_time %d n_probes %d flags_ext %x, wide_bw_scan: %d priority: %d",
req->scan_req.dwell_time_active,
req->scan_req.dwell_time_passive,
req->scan_req.repeat_probe_time, req->scan_req.n_probes,
req->scan_req.scan_ctrl_flags_ext,
req->scan_req.scan_f_wide_band,
req->scan_req.scan_priority);
}
QDF_STATUS
scm_scan_start_req(struct scheduler_msg *msg)
{
struct wlan_serialization_command cmd = {0, };
enum wlan_serialization_status ser_cmd_status;
struct scan_start_request *req = NULL;
struct wlan_scan_obj *scan_obj;
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint8_t idx;
if (!msg) {
scm_err("msg received is NULL");
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
if (!msg->bodyptr) {
scm_err("bodyptr is NULL");
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
req = msg->bodyptr;
if (!scm_is_scan_allowed(req->vdev)) {
scm_err("scan disabled, rejecting the scan req");
status = QDF_STATUS_E_NULL_VALUE;
goto err;
}
scan_obj = wlan_vdev_get_scan_obj(req->vdev);
if (!scan_obj) {
scm_debug("Couldn't find scan object");
status = QDF_STATUS_E_NULL_VALUE;
goto err;
}
scm_scan_req_update_params(req->vdev, req, scan_obj);
if (!req->scan_req.chan_list.num_chan) {
scm_err("Scan Aborted, 0 channel to scan");
status = QDF_STATUS_E_NULL_VALUE;
goto err;
}
scm_info("request to scan %d channels",
req->scan_req.chan_list.num_chan);
for (idx = 0; idx < req->scan_req.chan_list.num_chan; idx++)
scm_debug("chan[%d]: freq:%d, phymode:%d", idx,
req->scan_req.chan_list.chan[idx].freq,
req->scan_req.chan_list.chan[idx].phymode);
cmd.cmd_type = WLAN_SER_CMD_SCAN;
cmd.cmd_id = req->scan_req.scan_id;
cmd.cmd_cb = scm_scan_serialize_callback;
cmd.umac_cmd = req;
cmd.source = WLAN_UMAC_COMP_SCAN;
cmd.is_high_priority = false;
cmd.cmd_timeout_duration = req->scan_req.max_scan_time +
SCAN_TIMEOUT_GRACE_PERIOD;
cmd.vdev = req->vdev;
if (scan_obj->disable_timeout)
cmd.cmd_timeout_duration = 0;
scm_debug("req: 0x%pK, reqid: %d, scanid: %d, vdevid: %d",
req, req->scan_req.scan_req_id, req->scan_req.scan_id,
req->scan_req.vdev_id);
qdf_mtrace(QDF_MODULE_ID_SCAN, QDF_MODULE_ID_SERIALIZATION,
WLAN_SER_CMD_SCAN, req->vdev->vdev_objmgr.vdev_id,
req->scan_req.scan_id);
ser_cmd_status = wlan_serialization_request(&cmd);
scm_debug("wlan_serialization_request status:%d", ser_cmd_status);
switch (ser_cmd_status) {
case WLAN_SER_CMD_PENDING:
/* command moved to pending list.Do nothing */
break;
case WLAN_SER_CMD_ACTIVE:
/* command moved to active list. Do nothing */
break;
case WLAN_SER_CMD_DENIED_LIST_FULL:
case WLAN_SER_CMD_DENIED_RULES_FAILED:
case WLAN_SER_CMD_DENIED_UNSPECIFIED:
goto err;
default:
QDF_ASSERT(0);
status = QDF_STATUS_E_INVAL;
goto err;
}
return status;
err:
/*
* notify registered scan event handlers
* about internal error
*/
scm_post_internal_scan_complete_event(req,
SCAN_REASON_INTERNAL_FAILURE);
/*
* cmd can't be serviced.
* release vdev reference and free scan_start_request memory
*/
if (req) {
wlan_objmgr_vdev_release_ref(req->vdev, WLAN_SCAN_ID);
scm_scan_free_scan_request_mem(req);
}
return status;
}
static inline enum wlan_serialization_cancel_type
get_serialization_cancel_type(enum scan_cancel_req_type type)
{
enum wlan_serialization_cancel_type serialization_type;
switch (type) {
case WLAN_SCAN_CANCEL_SINGLE:
serialization_type = WLAN_SER_CANCEL_SINGLE_SCAN;
break;
case WLAN_SCAN_CANCEL_VDEV_ALL:
serialization_type = WLAN_SER_CANCEL_VDEV_SCANS;
break;
case WLAN_SCAN_CANCEL_PDEV_ALL:
serialization_type = WLAN_SER_CANCEL_PDEV_SCANS;
break;
default:
QDF_ASSERT(0);
scm_warn("invalid scan_cancel_req_type: %d", type);
serialization_type = WLAN_SER_CANCEL_PDEV_SCANS;
break;
}
return serialization_type;
}
QDF_STATUS
scm_scan_cancel_req(struct scheduler_msg *msg)
{
struct wlan_serialization_queued_cmd_info cmd = {0,};
struct wlan_serialization_command ser_cmd = {0,};
enum wlan_serialization_cmd_status ser_cmd_status;
struct scan_cancel_request *req;
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!msg) {
scm_err("msg received is NULL");
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
if (!msg->bodyptr) {
scm_err("Bodyptr is NULL");
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
req = msg->bodyptr;
/*
* If requester wants to wait for target scan cancel event
* instead of internally generated cancel event, just check
* which queue this scan request belongs to and send scan
* cancel request to FW accordingly.
* Else generate internal scan cancel event and notify
* handlers and free scan request resources.
*/
if (req->wait_tgt_cancel &&
(req->cancel_req.req_type == WLAN_SCAN_CANCEL_SINGLE)) {
ser_cmd.cmd_type = WLAN_SER_CMD_SCAN;
ser_cmd.cmd_id = req->cancel_req.scan_id;
ser_cmd.cmd_cb = NULL;
ser_cmd.umac_cmd = NULL;
ser_cmd.source = WLAN_UMAC_COMP_SCAN;
ser_cmd.is_high_priority = false;
ser_cmd.vdev = req->vdev;
if (wlan_serialization_is_cmd_present_in_active_queue(NULL, &ser_cmd))
ser_cmd_status = WLAN_SER_CMD_IN_ACTIVE_LIST;
else if (wlan_serialization_is_cmd_present_in_pending_queue(NULL, &ser_cmd))
ser_cmd_status = WLAN_SER_CMD_IN_PENDING_LIST;
else
ser_cmd_status = WLAN_SER_CMD_NOT_FOUND;
} else {
cmd.requestor = 0;
cmd.cmd_type = WLAN_SER_CMD_SCAN;
cmd.cmd_id = req->cancel_req.scan_id;
cmd.vdev = req->vdev;
cmd.queue_type = WLAN_SERIALIZATION_ACTIVE_QUEUE |
WLAN_SERIALIZATION_PENDING_QUEUE;
cmd.req_type = get_serialization_cancel_type(req->cancel_req.req_type);
ser_cmd_status = wlan_serialization_cancel_request(&cmd);
}
scm_debug("status: %d, reqid: %d, scanid: %d, vdevid: %d, type: %d",
ser_cmd_status, req->cancel_req.requester,
req->cancel_req.scan_id, req->cancel_req.vdev_id,
req->cancel_req.req_type);
switch (ser_cmd_status) {
case WLAN_SER_CMD_IN_PENDING_LIST:
/* do nothing */
break;
case WLAN_SER_CMD_IN_ACTIVE_LIST:
case WLAN_SER_CMDS_IN_ALL_LISTS:
/* send wmi scan cancel to fw */
status = tgt_scan_cancel(req);
break;
case WLAN_SER_CMD_NOT_FOUND:
/* do nothing */
break;
default:
QDF_ASSERT(0);
status = QDF_STATUS_E_INVAL;
break;
}
/* Release vdev reference and scan cancel request
* processing is complete
*/
wlan_objmgr_vdev_release_ref(req->vdev, WLAN_SCAN_ID);
/* Free cancel request memory */
qdf_mem_free(req);
return status;
}
#ifdef FEATURE_WLAN_SCAN_PNO
static QDF_STATUS
scm_pno_event_handler(struct wlan_objmgr_vdev *vdev,
struct scan_event *event)
{
struct scan_vdev_obj *scan_vdev_obj;
struct wlan_scan_obj *scan_psoc_obj;
scan_event_handler pno_cb;
void *cb_arg;
scan_vdev_obj = wlan_get_vdev_scan_obj(vdev);
scan_psoc_obj = wlan_vdev_get_scan_obj(vdev);
if (!scan_vdev_obj || !scan_psoc_obj) {
scm_err("null scan_vdev_obj %pK scan_obj %pK",
scan_vdev_obj, scan_psoc_obj);
return QDF_STATUS_E_INVAL;
}
switch (event->type) {
case SCAN_EVENT_TYPE_NLO_COMPLETE:
if (!scan_vdev_obj->pno_match_evt_received)
return QDF_STATUS_SUCCESS;
qdf_wake_lock_release(&scan_psoc_obj->pno_cfg.pno_wake_lock,
WIFI_POWER_EVENT_WAKELOCK_PNO);
qdf_wake_lock_timeout_acquire(
&scan_psoc_obj->pno_cfg.pno_wake_lock,
SCAN_PNO_SCAN_COMPLETE_WAKE_LOCK_TIMEOUT);
scan_vdev_obj->pno_match_evt_received = false;
break;
case SCAN_EVENT_TYPE_NLO_MATCH:
scan_vdev_obj->pno_match_evt_received = true;
qdf_wake_lock_timeout_acquire(
&scan_psoc_obj->pno_cfg.pno_wake_lock,
SCAN_PNO_MATCH_WAKE_LOCK_TIMEOUT);
return QDF_STATUS_SUCCESS;
default:
return QDF_STATUS_E_INVAL;
}
qdf_spin_lock_bh(&scan_psoc_obj->lock);
pno_cb = scan_psoc_obj->pno_cfg.pno_cb.func;
cb_arg = scan_psoc_obj->pno_cfg.pno_cb.arg;
qdf_spin_unlock_bh(&scan_psoc_obj->lock);
if (pno_cb)
pno_cb(vdev, event, cb_arg);
return QDF_STATUS_SUCCESS;
}
#else
static QDF_STATUS
scm_pno_event_handler(struct wlan_objmgr_vdev *vdev,
struct scan_event *event)
{
return QDF_STATUS_SUCCESS;
}
#endif
/**
* scm_scan_update_scan_event() - update scan event
* @scan: scan object
* @event: scan event
* @scan_start_req: scan_start_req used for triggering scan
*
* update scan params in scan event
*
* Return: QDF_STATUS
*/
static QDF_STATUS
scm_scan_update_scan_event(struct wlan_scan_obj *scan,
struct scan_event *event,
struct scan_start_request *scan_start_req)
{
if (!event)
return QDF_STATUS_E_NULL_VALUE;
if (!scan || !scan_start_req) {
event->scan_start_req = NULL;
return QDF_STATUS_E_NULL_VALUE;
}
/* copy scan start request to pass back buffer */
qdf_mem_copy(&scan->scan_start_request_buff, scan_start_req,
sizeof(struct scan_start_request));
/* reset all pointers */
scan->scan_start_request_buff.scan_req.extraie.ptr = NULL;
scan->scan_start_request_buff.scan_req.extraie.len = 0;
scan->scan_start_request_buff.scan_req.htcap.ptr = NULL;
scan->scan_start_request_buff.scan_req.htcap.len = 0;
scan->scan_start_request_buff.scan_req.vhtcap.ptr = NULL;
scan->scan_start_request_buff.scan_req.vhtcap.len = 0;
event->scan_start_req = &scan->scan_start_request_buff;
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
scm_scan_event_handler(struct scheduler_msg *msg)
{
struct wlan_objmgr_vdev *vdev;
struct scan_event *event;
struct scan_event_info *event_info;
struct wlan_serialization_command cmd = {0,};
struct wlan_serialization_command *queued_cmd;
struct scan_start_request *scan_start_req;
struct wlan_scan_obj *scan;
if (!msg) {
scm_err("NULL msg received ");
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
if (!msg->bodyptr) {
scm_err("NULL scan event received");
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
event_info = msg->bodyptr;
vdev = event_info->vdev;
event = &(event_info->event);
scm_debug("vdevid:%d, type:%d, reason:%d, freq:%d, reqstr:%d, scanid:%d",
event->vdev_id, event->type, event->reason, event->chan_freq,
event->requester, event->scan_id);
/*
* NLO requests are never queued, so post NLO events
* without checking for their presence in active queue.
*/
switch (event->type) {
case SCAN_EVENT_TYPE_NLO_COMPLETE:
case SCAN_EVENT_TYPE_NLO_MATCH:
scm_pno_event_handler(vdev, event);
goto exit;
default:
break;
}
cmd.cmd_type = WLAN_SER_CMD_SCAN;
cmd.cmd_id = event->scan_id;
cmd.cmd_cb = NULL;
cmd.umac_cmd = NULL;
cmd.source = WLAN_UMAC_COMP_SCAN;
cmd.is_high_priority = false;
cmd.vdev = vdev;
if (!wlan_serialization_is_cmd_present_in_active_queue(NULL, &cmd)) {
/*
* We received scan event for an already completed/cancelled
* scan request. Drop this event.
*/
scm_debug("Received scan event while request not in active queue");
goto exit;
}
/* Fill scan_start_request used to trigger this scan */
queued_cmd = wlan_serialization_get_scan_cmd_using_scan_id(
wlan_vdev_get_psoc(vdev), wlan_vdev_get_id(vdev),
event->scan_id, true);
if (!queued_cmd) {
scm_err("NULL queued_cmd");
goto exit;
}
if (!queued_cmd->umac_cmd) {
scm_err("NULL umac_cmd");
goto exit;
}
scan_start_req = queued_cmd->umac_cmd;
if (scan_start_req->scan_req.scan_req_id != event->requester) {
scm_err("req ID mismatch, scan_req_id:%d, event_req_id:%d",
scan_start_req->scan_req.scan_req_id,
event->requester);
goto exit;
}
scan = wlan_vdev_get_scan_obj(vdev);
if (scan)
scm_scan_update_scan_event(scan, event, scan_start_req);
switch (event->type) {
case SCAN_EVENT_TYPE_COMPLETED:
if (event->reason == SCAN_REASON_COMPLETED)
scm_11d_decide_country_code(vdev);
/* fall through to release the command */
case SCAN_EVENT_TYPE_START_FAILED:
case SCAN_EVENT_TYPE_DEQUEUED:
scm_release_serialization_command(vdev, event->scan_id);
break;
default:
break;
}
/* Notify all interested parties */
scm_scan_post_event(vdev, event);
exit:
/* free event info memory */
qdf_mem_free(event_info);
wlan_objmgr_vdev_release_ref(vdev, WLAN_SCAN_ID);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS scm_scan_event_flush_callback(struct scheduler_msg *msg)
{
struct wlan_objmgr_vdev *vdev;
struct scan_event_info *event_info;
if (!msg || !msg->bodyptr) {
scm_err("msg or msg->bodyptr is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
event_info = msg->bodyptr;
vdev = event_info->vdev;
/* free event info memory */
qdf_mem_free(event_info);
wlan_objmgr_vdev_release_ref(vdev, WLAN_SCAN_ID);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS scm_bcn_probe_flush_callback(struct scheduler_msg *msg)
{
struct scan_bcn_probe_event *bcn;
bcn = msg->bodyptr;
if (!bcn) {
scm_err("bcn is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
if (bcn->psoc)
wlan_objmgr_psoc_release_ref(bcn->psoc, WLAN_SCAN_ID);
if (bcn->rx_data)
qdf_mem_free(bcn->rx_data);
if (bcn->buf)
qdf_nbuf_free(bcn->buf);
qdf_mem_free(bcn);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS scm_scan_start_flush_callback(struct scheduler_msg *msg)
{
struct scan_start_request *req;
if (!msg || !msg->bodyptr) {
scm_err("msg or msg->bodyptr is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
req = msg->bodyptr;
scm_post_internal_scan_complete_event(req, SCAN_REASON_CANCELLED);
wlan_objmgr_vdev_release_ref(req->vdev, WLAN_SCAN_ID);
scm_scan_free_scan_request_mem(req);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS scm_scan_cancel_flush_callback(struct scheduler_msg *msg)
{
struct scan_cancel_request *req;
if (!msg || !msg->bodyptr) {
scm_err("msg or msg->bodyptr is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
req = msg->bodyptr;
wlan_objmgr_vdev_release_ref(req->vdev, WLAN_SCAN_ID);
/* Free cancel request memory */
qdf_mem_free(req);
return QDF_STATUS_SUCCESS;
}