blob: a4cc8b3a40616408ab826384a6767eef325baa05 [file] [log] [blame]
/*
* Copyright (c) 2017-2021 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 south bound interface definitions
*/
#include <wlan_cmn.h>
#include <qdf_list.h>
#include "../../core/src/wlan_scan_main.h"
#include <wlan_scan_utils_api.h>
#include <wlan_scan_ucfg_api.h>
#include <wlan_scan_tgt_api.h>
#include <wlan_objmgr_cmn.h>
#include <wlan_lmac_if_def.h>
#include <wlan_objmgr_psoc_obj.h>
#include <wlan_objmgr_pdev_obj.h>
#include <wlan_objmgr_vdev_obj.h>
#include <../../core/src/wlan_scan_manager.h>
static inline struct wlan_lmac_if_scan_tx_ops *
wlan_psoc_get_scan_txops(struct wlan_objmgr_psoc *psoc)
{
struct wlan_lmac_if_tx_ops *tx_ops;
tx_ops = wlan_psoc_get_lmac_if_txops(psoc);
if (!tx_ops) {
scm_err("tx_ops is NULL");
return NULL;
}
return &tx_ops->scan;
}
static inline struct wlan_lmac_if_scan_tx_ops *
wlan_vdev_get_scan_txops(struct wlan_objmgr_vdev *vdev)
{
struct wlan_objmgr_psoc *psoc = NULL;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
scm_err("NULL psoc");
return NULL;
}
return wlan_psoc_get_scan_txops(psoc);
}
static inline struct wlan_lmac_if_scan_rx_ops *
wlan_vdev_get_scan_rxops(struct wlan_objmgr_vdev *vdev)
{
struct wlan_objmgr_psoc *psoc = NULL;
struct wlan_lmac_if_rx_ops *rx_ops;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
scm_err("NULL psoc");
return NULL;
}
rx_ops = wlan_psoc_get_lmac_if_rxops(psoc);
if (!rx_ops) {
scm_err("rx_ops is NULL");
return NULL;
}
return &rx_ops->scan;
}
#ifdef FEATURE_WLAN_SCAN_PNO
QDF_STATUS tgt_scan_pno_start(struct wlan_objmgr_vdev *vdev,
struct pno_scan_req_params *req)
{
struct wlan_lmac_if_scan_tx_ops *scan_ops;
struct wlan_objmgr_psoc *psoc;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
scm_err("NULL PSOC");
return QDF_STATUS_E_FAILURE;
}
scan_ops = wlan_psoc_get_scan_txops(psoc);
if (!scan_ops) {
scm_err("NULL scan_ops");
return QDF_STATUS_E_FAILURE;
}
/* invoke wmi_unified_pno_start_cmd() */
QDF_ASSERT(scan_ops->pno_start);
if (scan_ops->pno_start)
return scan_ops->pno_start(psoc, req);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS tgt_scan_pno_stop(struct wlan_objmgr_vdev *vdev,
uint8_t vdev_id)
{
struct wlan_lmac_if_scan_tx_ops *scan_ops;
struct wlan_objmgr_psoc *psoc;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
scm_err("NULL PSOC");
return QDF_STATUS_E_FAILURE;
}
scan_ops = wlan_psoc_get_scan_txops(psoc);
if (!scan_ops) {
scm_err("NULL scan_ops");
return QDF_STATUS_E_FAILURE;
}
/* invoke wmi_unified_pno_stop_cmd() */
QDF_ASSERT(scan_ops->pno_stop);
if (scan_ops->pno_stop)
return scan_ops->pno_stop(psoc, vdev_id);
return QDF_STATUS_SUCCESS;
}
#endif
QDF_STATUS tgt_scan_obss_disable(struct wlan_objmgr_vdev *vdev)
{
struct wlan_lmac_if_scan_tx_ops *scan_ops;
struct wlan_objmgr_psoc *psoc;
uint8_t vdev_id;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
scm_err("NULL PSOC");
return QDF_STATUS_E_FAILURE;
}
scan_ops = wlan_psoc_get_scan_txops(psoc);
if (!scan_ops) {
scm_err("NULL scan_ops");
return QDF_STATUS_E_FAILURE;
}
vdev_id = wlan_vdev_get_id(vdev);
/* invoke wmi_unified_obss_disable_cmd() */
QDF_ASSERT(scan_ops->obss_disable);
if (scan_ops->obss_disable)
return scan_ops->obss_disable(psoc, vdev_id);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
tgt_scan_start(struct scan_start_request *req)
{
struct wlan_lmac_if_scan_tx_ops *scan_ops;
struct wlan_objmgr_psoc *psoc;
struct wlan_objmgr_pdev *pdev;
struct wlan_objmgr_vdev *vdev = req->vdev;
if (!vdev) {
scm_err("vdev is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
psoc = wlan_vdev_get_psoc(vdev);
pdev = wlan_vdev_get_pdev(vdev);
if (!psoc || !pdev) {
scm_err("psoc: 0x%pK or pdev: 0x%pK is NULL", psoc, pdev);
return QDF_STATUS_E_NULL_VALUE;
}
scan_ops = wlan_psoc_get_scan_txops(psoc);
if (!scan_ops) {
scm_err("NULL scan_ops");
return QDF_STATUS_E_NULL_VALUE;
}
/* invoke wmi_unified_scan_start_cmd_send() */
QDF_ASSERT(scan_ops->scan_start);
if (scan_ops->scan_start)
return scan_ops->scan_start(pdev, req);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
tgt_scan_cancel(struct scan_cancel_request *req)
{
struct wlan_lmac_if_scan_tx_ops *scan_ops;
struct wlan_objmgr_psoc *psoc;
struct wlan_objmgr_pdev *pdev;
struct wlan_objmgr_vdev *vdev = req->vdev;
if (!vdev) {
scm_err("vdev is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
psoc = wlan_vdev_get_psoc(vdev);
pdev = wlan_vdev_get_pdev(vdev);
if (!psoc || !pdev) {
scm_err("psoc: 0x%pK or pdev: 0x%pK is NULL", psoc, pdev);
return QDF_STATUS_E_NULL_VALUE;
}
scan_ops = wlan_psoc_get_scan_txops(psoc);
if (!scan_ops) {
scm_err("NULL scan_ops");
return QDF_STATUS_E_NULL_VALUE;
}
/* invoke wmi_unified_scan_stop_cmd_send() */
QDF_ASSERT(scan_ops->scan_cancel);
if (scan_ops->scan_cancel)
return scan_ops->scan_cancel(pdev, &req->cancel_req);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
tgt_scan_register_ev_handler(struct wlan_objmgr_psoc *psoc)
{
struct wlan_lmac_if_scan_tx_ops *scan_ops = NULL;
scan_ops = wlan_psoc_get_scan_txops(psoc);
if (!scan_ops) {
scm_err("NULL scan_ops");
return QDF_STATUS_E_FAILURE;
}
/* invoke wmi_unified_register_event_handler()
* since event id, handler function and context is
* already known to offload lmac, passing NULL as argument.
* DA can pass necessary arguments by clubing then into
* some structure.
*/
QDF_ASSERT(scan_ops->scan_reg_ev_handler);
if (scan_ops->scan_reg_ev_handler)
return scan_ops->scan_reg_ev_handler(psoc, NULL);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
tgt_scan_unregister_ev_handler(struct wlan_objmgr_psoc *psoc)
{
struct wlan_lmac_if_scan_tx_ops *scan_ops = NULL;
scan_ops = wlan_psoc_get_scan_txops(psoc);
if (!scan_ops) {
scm_err("NULL scan_ops");
return QDF_STATUS_E_FAILURE;
}
/* invoke wmi_unified_register_event_handler()
* since event id, handler function and context is
* already known to offload lmac, passing NULL as argument.
* DA can pass necessary arguments by clubing then into
* some structure.
*/
QDF_ASSERT(scan_ops->scan_unreg_ev_handler);
if (scan_ops->scan_unreg_ev_handler)
return scan_ops->scan_unreg_ev_handler(psoc, NULL);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
tgt_scan_event_handler(struct wlan_objmgr_psoc *psoc,
struct scan_event_info *event_info)
{
struct scheduler_msg msg = {0};
struct scan_event *event = &event_info->event;
uint8_t vdev_id = event->vdev_id;
QDF_STATUS status;
if (!psoc || !event_info) {
scm_err("psoc: 0x%pK, event_info: 0x%pK", psoc, event_info);
return QDF_STATUS_E_NULL_VALUE;
}
event_info->vdev =
wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
vdev_id, WLAN_SCAN_ID);
if (!event_info->vdev) {
scm_err("null vdev, vdev_id: %d, psoc: 0x%pK", vdev_id, psoc);
return QDF_STATUS_E_INVAL;
}
msg.bodyptr = event_info;
msg.callback = scm_scan_event_handler;
msg.flush_callback = scm_scan_event_flush_callback;
status = scheduler_post_message(QDF_MODULE_ID_SCAN,
QDF_MODULE_ID_SCAN,
QDF_MODULE_ID_SCAN, &msg);
if (QDF_IS_STATUS_ERROR(status)) {
wlan_objmgr_vdev_release_ref(event_info->vdev, WLAN_SCAN_ID);
}
return status;
}
QDF_STATUS tgt_scan_bcn_probe_rx_callback(struct wlan_objmgr_psoc *psoc,
struct wlan_objmgr_peer *peer, qdf_nbuf_t buf,
struct mgmt_rx_event_params *rx_param,
enum mgmt_frame_type frm_type)
{
struct scheduler_msg msg = {0};
struct scan_bcn_probe_event *bcn = NULL;
QDF_STATUS status;
uint32_t scan_queue_size = 0;
if ((frm_type != MGMT_PROBE_RESP) &&
(frm_type != MGMT_BEACON)) {
scm_err("frame is not beacon or probe resp");
status = QDF_STATUS_E_INVAL;
goto free;
}
bcn = qdf_mem_malloc_atomic(sizeof(*bcn));
if (!bcn) {
status = QDF_STATUS_E_NOMEM;
goto free;
}
bcn->rx_data =
qdf_mem_malloc_atomic(sizeof(*rx_param));
if (!bcn->rx_data) {
status = QDF_STATUS_E_NOMEM;
goto free;
}
if (frm_type == MGMT_PROBE_RESP)
bcn->frm_type = MGMT_SUBTYPE_PROBE_RESP;
else
bcn->frm_type = MGMT_SUBTYPE_BEACON;
/* Check if the beacon/probe frame can be posted in the scan queue */
status = scheduler_get_queue_size(QDF_MODULE_ID_SCAN, &scan_queue_size);
if (!QDF_IS_STATUS_SUCCESS(status) ||
scan_queue_size > MAX_BCN_PROBE_IN_SCAN_QUEUE) {
scm_debug_rl("Dropping beacon/probe frame, queue size %d",
scan_queue_size);
status = QDF_STATUS_E_FAILURE;
goto free;
}
status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_SCAN_ID);
if (QDF_IS_STATUS_ERROR(status)) {
scm_info("unable to get reference");
goto free;
}
bcn->psoc = psoc;
bcn->buf = buf;
qdf_mem_copy(bcn->rx_data, rx_param, sizeof(*rx_param));
msg.bodyptr = bcn;
msg.callback = scm_handle_bcn_probe;
msg.flush_callback = scm_bcn_probe_flush_callback;
status = scheduler_post_message(QDF_MODULE_ID_SCAN,
QDF_MODULE_ID_SCAN,
QDF_MODULE_ID_SCAN, &msg);
if (QDF_IS_STATUS_SUCCESS(status))
return status;
wlan_objmgr_psoc_release_ref(psoc, WLAN_SCAN_ID);
free:
if (bcn && bcn->rx_data)
qdf_mem_free(bcn->rx_data);
if (bcn)
qdf_mem_free(bcn);
if (buf)
qdf_nbuf_free(buf);
return status;
}
QDF_STATUS
tgt_scan_set_max_active_scans(struct wlan_objmgr_psoc *psoc,
uint32_t max_active_scans)
{
struct scan_default_params *scan_params = NULL;
if (!psoc) {
scm_err("null psoc");
return QDF_STATUS_E_NULL_VALUE;
}
scan_params = wlan_scan_psoc_get_def_params(psoc);
if (!scan_params) {
scm_err("wlan_scan_psoc_get_def_params returned NULL");
return QDF_STATUS_E_NULL_VALUE;
}
scan_params->max_active_scans_allowed = max_active_scans;
return QDF_STATUS_SUCCESS;
}