qcacld-3.0: Add target interfaces for OCB component

Add target interfaces and core APIs for OCB component.

CRs-Fixed: 2177578
Change-Id: I76a1afa4d6f3876b0b5df89ffe4080af77cd13c4
This commit is contained in:
Zhang Qian
2018-01-04 20:34:08 +08:00
committed by snandini
parent 15f4d581d1
commit b94edcee44
5 changed files with 1711 additions and 0 deletions

View File

@@ -0,0 +1,572 @@
/*
* Copyright (c) 2018 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 core ocb function definitions
*/
#include <qdf_status.h>
#include "scheduler_api.h"
#include "wlan_objmgr_cmn.h"
#include "wlan_objmgr_global_obj.h"
#include "wlan_objmgr_psoc_obj.h"
#include "wlan_objmgr_pdev_obj.h"
#include "wlan_objmgr_vdev_obj.h"
#include <cdp_txrx_handle.h>
#include <cdp_txrx_cmn.h>
#include <cdp_txrx_ocb.h>
#include "wlan_ocb_main.h"
#include "wlan_ocb_tgt_api.h"
#include "target_if_ocb.h"
/**
* ocb_get_cmd_type_str() - parse cmd to string
* @cmd_type: ocb cmd type
*
* This function parse ocb cmd to string.
*
* Return: command string
*/
static const char *ocb_get_evt_type_str(enum ocb_southbound_event evt_type)
{
switch (evt_type) {
case OCB_CHANNEL_CONFIG_STATUS:
return "OCB channel config status";
case OCB_TSF_TIMER:
return "OCB TSF timer";
case OCB_DCC_STATS_RESPONSE:
return "OCB DCC get stats response";
case OCB_NDL_RESPONSE:
return "OCB NDL indication";
case OCB_DCC_INDICATION:
return "OCB DCC stats indication";
default:
return "Invalid OCB command";
}
}
/**
* ocb_set_chan_info() - Set channel info to dp
* @dp_soc: data path soc handle
* @dp_pdev: data path pdev handle
* @vdev_id: OCB vdev_id
* @config: channel config parameters
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS ocb_set_chan_info(void *dp_soc,
void *dp_pdev,
uint32_t vdev_id,
struct ocb_config *config)
{
struct cdp_vdev *dp_vdev;
struct ol_txrx_ocb_set_chan ocb_set_chan;
struct ol_txrx_ocb_chan_info *ocb_channel_info;
if (!dp_soc || !dp_pdev) {
ocb_err("DP global handle is null");
return QDF_STATUS_E_INVAL;
}
dp_vdev = cdp_get_vdev_from_vdev_id(dp_soc, dp_pdev, vdev_id);
if (!dp_vdev) {
ocb_err("DP vdev handle is NULL");
return QDF_STATUS_E_FAILURE;
}
ocb_set_chan.ocb_channel_count = config->channel_count;
/* release old settings */
ocb_channel_info = cdp_get_ocb_chan_info(dp_soc, dp_vdev);
if (ocb_channel_info)
qdf_mem_free(ocb_channel_info);
if (config->channel_count) {
int i, buf_size;
buf_size = sizeof(*ocb_channel_info) * config->channel_count;
ocb_set_chan.ocb_channel_info = qdf_mem_malloc(buf_size);
if (!ocb_set_chan.ocb_channel_info) {
ocb_err("Failed to allocate buffer for chan info");
return QDF_STATUS_E_NOMEM;
}
ocb_channel_info = ocb_set_chan.ocb_channel_info;
for (i = 0; i < config->channel_count; i++) {
ocb_channel_info[i].chan_freq =
config->channels[i].chan_freq;
if (config->channels[i].flags &
OCB_CHANNEL_FLAG_DISABLE_RX_STATS_HDR)
ocb_channel_info[i].disable_rx_stats_hdr = 1;
}
} else {
ocb_debug("No channel config to dp");
ocb_set_chan.ocb_channel_info = NULL;
}
ocb_debug("Sync channel config to dp");
cdp_set_ocb_chan_info(dp_soc, dp_vdev, ocb_set_chan);
return QDF_STATUS_SUCCESS;
}
/**
* ocb_channel_config_status() - Process set channel config response
* @evt: response event
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS ocb_channel_config_status(struct ocb_rx_event *evt)
{
QDF_STATUS status;
uint32_t vdev_id;
struct ocb_rx_event *event;
struct wlan_objmgr_psoc *psoc;
struct wlan_objmgr_pdev *pdev;
struct ocb_pdev_obj *ocb_obj;
struct ocb_callbacks *cbs;
struct ocb_set_config_response config_rsp;
if (!evt) {
ocb_err("Event buffer is NULL");
return QDF_STATUS_E_FAILURE;
}
event = evt;
psoc = event->psoc;
pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_OCB_SB_ID);
if (!pdev) {
ocb_alert("Pdev is NULL");
return QDF_STATUS_E_FAILURE;
}
ocb_obj = wlan_get_pdev_ocb_obj(pdev);
if (!ocb_obj) {
ocb_err("Pdev object is NULL");
status = QDF_STATUS_E_INVAL;
goto exit;
}
cbs = &ocb_obj->ocb_cbs;
if (ocb_obj->channel_config) {
vdev_id = ocb_obj->channel_config->vdev_id;
config_rsp = event->rsp.channel_cfg_rsp;
/* Sync channel status to data path */
if (config_rsp.status == OCB_CHANNEL_CONFIG_SUCCESS)
ocb_set_chan_info(ocb_obj->dp_soc,
ocb_obj->dp_pdev,
vdev_id,
ocb_obj->channel_config);
qdf_mem_free(ocb_obj->channel_config);
ocb_obj->channel_config = NULL;
} else {
ocb_err("Failed to sync channel info to DP");
config_rsp.status = OCB_CHANNEL_CONFIG_FAIL;
}
if (cbs->ocb_set_config_callback) {
cbs->ocb_set_config_callback(cbs->ocb_set_config_context,
&config_rsp);
status = QDF_STATUS_SUCCESS;
} else {
ocb_err("ocb_set_config_resp_cb is NULL");
status = QDF_STATUS_E_INVAL;
}
exit:
wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID);
return status;
}
/**
* ocb_tsf_timer() - Process get TSF timer response
* @evt: repsonse event
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS ocb_tsf_timer(struct ocb_rx_event *evt)
{
QDF_STATUS status;
struct ocb_rx_event *event;
struct wlan_objmgr_vdev *vdev;
struct wlan_objmgr_pdev *pdev;
struct ocb_callbacks *cbs;
struct ocb_get_tsf_timer_response *tsf_timer;
if (!evt) {
ocb_err("Event buffer is NULL");
return QDF_STATUS_E_FAILURE;
}
event = evt;
vdev = event->vdev;
pdev = wlan_vdev_get_pdev(vdev);
cbs = wlan_ocb_get_callbacks(pdev);
tsf_timer = &event->rsp.tsf_timer;
ocb_debug("TSF timer low=%d, high=%d",
tsf_timer->timer_low, tsf_timer->timer_high);
if (cbs && cbs->ocb_get_tsf_timer_callback) {
ocb_debug("%s: send TSF timer.", __func__);
cbs->ocb_get_tsf_timer_callback(cbs->ocb_get_tsf_timer_context,
tsf_timer);
status = QDF_STATUS_SUCCESS;
} else {
ocb_err("ocb_get_tsf_timer_cb is NULL");
status = QDF_STATUS_E_FAILURE;
}
return status;
}
/**
* ocb_dcc_stats_response() - Process get DCC stats response
* @evt: repsonse event
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS ocb_dcc_stats_response(struct ocb_rx_event *evt)
{
QDF_STATUS status;
struct ocb_rx_event *event;
struct wlan_objmgr_vdev *vdev;
struct wlan_objmgr_pdev *pdev;
struct ocb_callbacks *cbs;
struct ocb_dcc_get_stats_response *dcc_stats;
if (!evt) {
ocb_err("Event buffer is NULL");
return QDF_STATUS_E_FAILURE;
}
event = evt;
vdev = event->vdev;
pdev = wlan_vdev_get_pdev(vdev);
cbs = wlan_ocb_get_callbacks(pdev);
dcc_stats = &event->rsp.dcc_stats;
if (cbs && cbs->ocb_dcc_get_stats_callback) {
ocb_debug("%s: send DCC stats", __func__);
cbs->ocb_dcc_get_stats_callback(cbs->ocb_dcc_get_stats_context,
dcc_stats);
status = QDF_STATUS_SUCCESS;
} else {
ocb_err("dcc_get_stats_cb is NULL");
status = QDF_STATUS_E_FAILURE;
}
return status;
}
/**
* ocb_ndl_response() - Process NDL update response
* @evt: repsonse event
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS ocb_ndl_response(struct ocb_rx_event *evt)
{
QDF_STATUS status;
struct ocb_rx_event *event;
struct wlan_objmgr_vdev *vdev;
struct wlan_objmgr_pdev *pdev;
struct ocb_callbacks *cbs;
struct ocb_dcc_update_ndl_response *ndl;
if (!evt) {
ocb_err("Event buffer is NULL");
return QDF_STATUS_E_FAILURE;
}
event = evt;
vdev = event->vdev;
pdev = wlan_vdev_get_pdev(vdev);
cbs = wlan_ocb_get_callbacks(pdev);
ndl = &event->rsp.ndl;
if (cbs && cbs->ocb_dcc_update_ndl_callback) {
ocb_debug("%s: NDL update response", __func__);
cbs->ocb_dcc_update_ndl_callback(
cbs->ocb_dcc_update_ndl_context, ndl);
status = QDF_STATUS_SUCCESS;
} else {
ocb_err("dcc_update_ndl is NULL");
status = QDF_STATUS_E_FAILURE;
}
return status;
}
/**
* ocb_dcc_indication() - Process DCC stats indication
* @evt: repsonse event
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS ocb_dcc_indication(struct ocb_rx_event *evt)
{
QDF_STATUS status;
struct ocb_rx_event *event;
struct wlan_objmgr_vdev *vdev;
struct ocb_callbacks *cbs;
struct wlan_objmgr_pdev *pdev;
struct ocb_dcc_get_stats_response *dcc_stats;
if (!evt) {
ocb_err("Event buffer is NULL");
return QDF_STATUS_E_FAILURE;
}
event = evt;
vdev = event->vdev;
pdev = wlan_vdev_get_pdev(vdev);
cbs = wlan_ocb_get_callbacks(pdev);
dcc_stats = &event->rsp.dcc_stats;
if (cbs && cbs->ocb_dcc_stats_event_callback) {
ocb_debug("%s: DCC stats indication", __func__);
cbs->ocb_dcc_stats_event_callback(
cbs->ocb_dcc_stats_event_context, dcc_stats);
status = QDF_STATUS_SUCCESS;
} else {
ocb_err("dcc_get_stats_cb is NULL");
status = QDF_STATUS_E_FAILURE;
}
return status;
}
/**
* ocb_flush_start_msg() - Flush ocb start message
* @msg: OCB start vdev message
*
* Return: QDF_STATUS_SUCCESS on success.
*/
static QDF_STATUS ocb_flush_start_msg(struct scheduler_msg *msg)
{
struct ocb_pdev_obj *ocb_obj;
if (!msg) {
ocb_err("Null point for OCB message");
return QDF_STATUS_E_INVAL;
}
ocb_obj = msg->bodyptr;
if (ocb_obj && ocb_obj->channel_config) {
ocb_info("release the backed config parameters");
qdf_mem_free(ocb_obj->channel_config);
ocb_obj->channel_config = NULL;
}
return QDF_STATUS_SUCCESS;
}
/**
* ocb_process_start_vdev_msg() - Handler for OCB vdev start message
* @msg: OCB start vdev message
*
* Return: QDF_STATUS_SUCCESS on success.
*/
static QDF_STATUS ocb_process_start_vdev_msg(struct scheduler_msg *msg)
{
QDF_STATUS status;
struct ocb_config *config;
struct ocb_pdev_obj *ocb_obj;
struct ocb_callbacks *ocb_cbs;
struct wlan_objmgr_vdev *vdev;
if (!msg || !msg->bodyptr) {
ocb_err("invalid message");
return QDF_STATUS_E_INVAL;
}
ocb_obj = msg->bodyptr;
ocb_cbs = &ocb_obj->ocb_cbs;
if (!ocb_cbs->start_ocb_vdev) {
ocb_err("No callback to start ocb vdev");
return QDF_STATUS_E_FAILURE;
}
config = ocb_obj->channel_config;
if (!config) {
ocb_err("NULL config parameters");
return QDF_STATUS_E_INVAL;
}
vdev = wlan_objmgr_get_vdev_by_id_from_pdev(ocb_obj->pdev,
config->vdev_id,
WLAN_OCB_SB_ID);
if (!vdev) {
ocb_err("Cannot get vdev");
return QDF_STATUS_E_FAILURE;
}
ocb_debug("Start to send OCB vdev start cmd");
status = ocb_cbs->start_ocb_vdev(config);
wlan_objmgr_vdev_release_ref(vdev, WLAN_OCB_SB_ID);
if (QDF_IS_STATUS_ERROR(status)) {
ocb_err("Failed to start OCB vdev");
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS ocb_vdev_start(struct ocb_pdev_obj *ocb_obj)
{
QDF_STATUS status;
struct scheduler_msg msg = {0};
msg.bodyptr = ocb_obj;
msg.callback = ocb_process_start_vdev_msg;
msg.flush_callback = ocb_flush_start_msg;
status = scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg);
if (QDF_IS_STATUS_ERROR(status))
ocb_err("Failed to post vdev start message");
return status;
}
QDF_STATUS ocb_process_evt(struct scheduler_msg *msg)
{
QDF_STATUS status;
struct ocb_rx_event *event;
ocb_debug("msg type %d, %s", msg->type,
ocb_get_evt_type_str(msg->type));
if (!(msg->bodyptr)) {
ocb_err("Invalid message body");
return QDF_STATUS_E_INVAL;
}
event = msg->bodyptr;
switch (msg->type) {
case OCB_CHANNEL_CONFIG_STATUS:
status = ocb_channel_config_status(event);
break;
case OCB_TSF_TIMER:
status = ocb_tsf_timer(event);
break;
case OCB_DCC_STATS_RESPONSE:
status = ocb_dcc_stats_response(event);
break;
case OCB_NDL_RESPONSE:
status = ocb_ndl_response(event);
break;
case OCB_DCC_INDICATION:
status = ocb_dcc_indication(event);
break;
default:
status = QDF_STATUS_E_INVAL;
break;
}
wlan_ocb_release_rx_event(event);
msg->bodyptr = NULL;
return status;
}
struct ocb_config *ocb_copy_config(struct ocb_config *src)
{
struct ocb_config *dst;
uint32_t length;
uint8_t *cursor;
length = sizeof(*src) +
src->channel_count * sizeof(*src->channels) +
src->schedule_size * sizeof(*src->schedule) +
src->dcc_ndl_chan_list_len +
src->dcc_ndl_active_state_list_len;
dst = qdf_mem_malloc(length);
if (!dst)
return NULL;
*dst = *src;
cursor = (uint8_t *)dst;
cursor += sizeof(*dst);
dst->channels = (struct ocb_config_chan *)cursor;
cursor += src->channel_count * sizeof(*dst->channels);
qdf_mem_copy(dst->channels, src->channels,
src->channel_count * sizeof(*dst->channels));
dst->schedule = (struct ocb_config_schdl *)cursor;
cursor += src->schedule_size * sizeof(*dst->schedule);
qdf_mem_copy(dst->schedule, src->schedule,
src->schedule_size * sizeof(*dst->schedule));
dst->dcc_ndl_chan_list = cursor;
cursor += src->dcc_ndl_chan_list_len;
qdf_mem_copy(dst->dcc_ndl_chan_list, src->dcc_ndl_chan_list,
src->dcc_ndl_chan_list_len);
dst->dcc_ndl_active_state_list = cursor;
cursor += src->dcc_ndl_active_state_list_len;
qdf_mem_copy(dst->dcc_ndl_active_state_list,
src->dcc_ndl_active_state_list,
src->dcc_ndl_active_state_list_len);
return dst;
}
QDF_STATUS ocb_pdev_obj_create_notification(struct wlan_objmgr_pdev *pdev,
void *arg_list)
{
QDF_STATUS status;
struct ocb_pdev_obj *ocb_obj;
ocb_notice("ocb pdev created");
ocb_obj = qdf_mem_malloc(sizeof(*ocb_obj));
if (!ocb_obj) {
ocb_err("Failed to allocate memory for ocb pdev object");
return QDF_STATUS_E_FAILURE;
}
status = wlan_objmgr_pdev_component_obj_attach(pdev,
WLAN_UMAC_COMP_OCB,
(void *)ocb_obj,
QDF_STATUS_SUCCESS);
if (QDF_IS_STATUS_ERROR(status)) {
ocb_err("Failed to attach pdev ocb component");
qdf_mem_free(ocb_obj);
return status;
}
ocb_obj->pdev = pdev;
/* register OCB tx/rx ops */
tgt_ocb_register_rx_ops(&ocb_obj->ocb_rxops);
target_if_ocb_register_tx_ops(&ocb_obj->ocb_txops);
ocb_notice("ocb pdev attached");
return status;
}
QDF_STATUS ocb_pdev_obj_destroy_notification(struct wlan_objmgr_pdev *pdev,
void *arg_list)
{
QDF_STATUS status;
struct ocb_pdev_obj *ocb_obj;
ocb_obj = wlan_objmgr_pdev_get_comp_private_obj(pdev,
WLAN_UMAC_COMP_OCB);
if (!ocb_obj) {
ocb_err("Failed to get ocb pdev object");
return QDF_STATUS_E_FAILURE;
}
status = wlan_objmgr_pdev_component_obj_detach(pdev,
WLAN_UMAC_COMP_OCB,
ocb_obj);
if (QDF_IS_STATUS_ERROR(status))
ocb_err("Failed to detatch ocb pdev object");
qdf_mem_free(ocb_obj);
return QDF_STATUS_SUCCESS;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018 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: OCB south bound interface declaration
*/
#ifndef _WLAN_OCB_TGT_API_H
#define _WLAN_OCB_TGT_API_H
#include <wlan_ocb_public_structs.h>
/**
* tgt_ocb_register_ev_handler() - register south bound event handler
* @pdev: pdev handle
*
* Return: QDF_STATUS_SUCCESS on success
*/
QDF_STATUS tgt_ocb_register_ev_handler(struct wlan_objmgr_pdev *pdev);
/**
* tgt_ocb_unregister_ev_handler() - unregister south bound event handler
* @pdev: pdev handle
*
* Return: QDF_STATUS_SUCCESS on success
*/
QDF_STATUS tgt_ocb_unregister_ev_handler(struct wlan_objmgr_pdev *pdev);
/**
* tgt_ocb_register_rx_ops() - register OCB rx operations
* @ocb_rxops: fps to rx operations
*
* Return: QDF_STATUS_SUCCESS on success
*/
QDF_STATUS tgt_ocb_register_rx_ops(struct wlan_ocb_rx_ops *ocb_rxops);
#endif

View File

@@ -0,0 +1,336 @@
/*
* Copyright (c) 2018 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: This file contains ocb south bound interface definitions
*/
#include <scheduler_api.h>
#include <wlan_objmgr_psoc_obj.h>
#include <wlan_objmgr_global_obj.h>
#include <wlan_objmgr_pdev_obj.h>
#include <wlan_objmgr_vdev_obj.h>
#include "wlan_ocb_public_structs.h"
#include "wlan_ocb_ucfg_api.h"
#include "wlan_ocb_tgt_api.h"
#include "wlan_ocb_main.h"
/**
* wlan_ocb_flush_callback() - OCB message flash callback
* @msg: OCB message
*
* Return: QDF_STATUS_SUCCESS on success.
*/
static QDF_STATUS wlan_ocb_flush_callback(struct scheduler_msg *msg)
{
struct ocb_rx_event *event;
if (!msg) {
ocb_err("Null point for OCB message");
return QDF_STATUS_E_INVAL;
}
event = msg->bodyptr;
wlan_ocb_release_rx_event(event);
return QDF_STATUS_SUCCESS;
}
/**
* tgt_ocb_channel_config_status() - handler for channel config response
* @psoc: psoc handle
* @status: status for last channel config
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS
tgt_ocb_channel_config_status(struct wlan_objmgr_psoc *psoc,
uint32_t status)
{
QDF_STATUS qdf_status;
struct scheduler_msg msg = {0};
struct ocb_rx_event *event;
event = qdf_mem_malloc(sizeof(*event));
if (!event) {
ocb_err("Memory malloc failed for channel config status");
return QDF_STATUS_E_NOMEM;
}
qdf_status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_OCB_SB_ID);
if (QDF_IS_STATUS_ERROR(qdf_status)) {
ocb_err("Failed to get psoc ref");
wlan_ocb_release_rx_event(event);
return qdf_status;
}
event->psoc = psoc;
event->rsp.channel_cfg_rsp.status = status;
event->evt_id = OCB_CHANNEL_CONFIG_STATUS;
msg.type = OCB_CHANNEL_CONFIG_STATUS;
msg.bodyptr = event;
msg.callback = ocb_process_evt;
msg.flush_callback = wlan_ocb_flush_callback;
qdf_status = scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg);
if (QDF_IS_STATUS_SUCCESS(qdf_status))
return QDF_STATUS_SUCCESS;
ocb_err("failed to send OCB_CHANNEL_CONFIG_STATUS msg");
wlan_ocb_release_rx_event(event);
return qdf_status;
}
/**
* tgt_ocb_get_tsf_timer() - handle for TSF timer response
* @psoc: psoc handle
* @response: TSF timer response
*
* Return: QDF_STATUS_SUCCESS on succcess
*/
static QDF_STATUS
tgt_ocb_get_tsf_timer(struct wlan_objmgr_psoc *psoc,
struct ocb_get_tsf_timer_response *response)
{
QDF_STATUS status;
struct scheduler_msg msg = {0};
struct ocb_rx_event *event;
event = qdf_mem_malloc(sizeof(*event));
if (!event) {
ocb_err("Memory malloc failed for tsf timer");
return QDF_STATUS_E_NOMEM;
}
status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_OCB_SB_ID);
if (QDF_IS_STATUS_ERROR(status)) {
ocb_err("Failed to get psoc ref");
goto flush_ref;
}
event->psoc = psoc;
event->vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
response->vdev_id,
WLAN_OCB_SB_ID);
if (!event->vdev) {
ocb_err("Cannot get vdev handle");
status = QDF_STATUS_E_FAILURE;
goto flush_ref;
}
event->evt_id = OCB_TSF_TIMER;
event->rsp.tsf_timer.vdev_id = response->vdev_id;
event->rsp.tsf_timer.timer_high = response->timer_high;
event->rsp.tsf_timer.timer_low = response->timer_low;
msg.type = OCB_TSF_TIMER;
msg.bodyptr = event;
msg.callback = ocb_process_evt;
msg.flush_callback = wlan_ocb_flush_callback;
status = scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg);
if (QDF_IS_STATUS_SUCCESS(status))
return QDF_STATUS_SUCCESS;
ocb_err("failed to send OCB_TSF_TIMER msg");
flush_ref:
wlan_ocb_release_rx_event(event);
return status;
}
/**
* tgt_ocb_dcc_ndl_update() - handler for NDL update response
* @psoc: psoc handle
* @resp: NDL update response
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS
tgt_ocb_dcc_ndl_update(struct wlan_objmgr_psoc *psoc,
struct ocb_dcc_update_ndl_response *resp)
{
QDF_STATUS status;
struct scheduler_msg msg = {0};
struct ocb_rx_event *event;
event = qdf_mem_malloc(sizeof(*event));
if (!event) {
ocb_err("Memory malloc failed for ndl update response");
return QDF_STATUS_E_NOMEM;
}
status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_OCB_SB_ID);
if (QDF_IS_STATUS_ERROR(status)) {
ocb_err("Failed to get psoc ref");
goto flush_ref;
}
event->psoc = psoc;
event->vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
resp->vdev_id,
WLAN_OCB_SB_ID);
if (!event->vdev) {
ocb_err("Cannot get vdev handle");
status = QDF_STATUS_E_FAILURE;
goto flush_ref;
}
event->evt_id = OCB_NDL_RESPONSE;
qdf_mem_copy(&event->rsp.ndl, resp, sizeof(*resp));
msg.type = OCB_NDL_RESPONSE;
msg.bodyptr = event;
msg.callback = ocb_process_evt;
msg.flush_callback = wlan_ocb_flush_callback;
status = scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg);
if (QDF_IS_STATUS_SUCCESS(status))
return QDF_STATUS_SUCCESS;
ocb_err("failed to send OCB_NDL_RESPONSE msg");
flush_ref:
wlan_ocb_release_rx_event(event);
return status;
}
/**
* tgt_ocb_dcc_stats_indicate() - handler for DCC stats indication
* @psoc: psoc handle
* @response: DCC stats
* @bool: true for active query, false for passive indicate
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS
tgt_ocb_dcc_stats_indicate(struct wlan_objmgr_psoc *psoc,
struct ocb_dcc_get_stats_response *response,
bool active)
{
QDF_STATUS status;
uint8_t *buf;
uint32_t size;
struct scheduler_msg msg = {0};
struct ocb_rx_event *event;
size = sizeof(*event) +
response->channel_stats_array_len;
buf = qdf_mem_malloc(size);
if (!buf) {
ocb_err("Memory malloc failed for dcc indication");
return QDF_STATUS_E_NOMEM;
}
event = (struct ocb_rx_event *)buf;
status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_OCB_SB_ID);
if (QDF_IS_STATUS_ERROR(status)) {
ocb_err("Failed to get psoc ref");
goto flush_ref;
}
event->psoc = psoc;
event->vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
response->vdev_id,
WLAN_OCB_SB_ID);
if (!event->vdev) {
ocb_err("Cannot get vdev handle");
status = QDF_STATUS_E_FAILURE;
goto flush_ref;
}
event->rsp.dcc_stats.channel_stats_array =
(uint8_t *)&event->rsp.dcc_stats +
sizeof(struct ocb_dcc_get_stats_response);
event->rsp.dcc_stats.vdev_id = response->vdev_id;
event->rsp.dcc_stats.num_channels = response->num_channels;
event->rsp.dcc_stats.channel_stats_array_len =
response->channel_stats_array_len;
qdf_mem_copy(event->rsp.dcc_stats.channel_stats_array,
response->channel_stats_array,
response->channel_stats_array_len);
ocb_debug("Message type is %s",
active ? "Get stats response" : "DCC stats indication");
if (active)
msg.type = OCB_DCC_STATS_RESPONSE;
else
msg.type = OCB_DCC_INDICATION;
event->evt_id = msg.type;
msg.bodyptr = event;
msg.callback = ocb_process_evt;
msg.flush_callback = wlan_ocb_flush_callback;
status = scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg);
if (QDF_IS_STATUS_SUCCESS(status))
return QDF_STATUS_SUCCESS;
ocb_err("failed to send DCC stats msg(%d)", msg.type);
flush_ref:
wlan_ocb_release_rx_event(event);
return status;
}
QDF_STATUS tgt_ocb_register_ev_handler(struct wlan_objmgr_pdev *pdev)
{
struct wlan_objmgr_psoc *psoc;
struct wlan_ocb_tx_ops *ocb_ops;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
ocb_err("Null soc handle");
return QDF_STATUS_E_INVAL;
}
ocb_ops = wlan_pdev_get_ocb_tx_ops(pdev);
if (ocb_ops && ocb_ops->ocb_reg_ev_handler) {
status = ocb_ops->ocb_reg_ev_handler(psoc, NULL);
ocb_debug("register ocb event, status:%d", status);
} else {
ocb_alert("No ocb objects or ocb_reg_ev_handler");
}
return status;
}
QDF_STATUS tgt_ocb_unregister_ev_handler(struct wlan_objmgr_pdev *pdev)
{
struct wlan_objmgr_psoc *psoc;
struct wlan_ocb_tx_ops *ocb_ops;
QDF_STATUS status;
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
ocb_err("Null soc handle");
return QDF_STATUS_E_INVAL;
}
ocb_ops = wlan_pdev_get_ocb_tx_ops(pdev);
if (ocb_ops && ocb_ops->ocb_unreg_ev_handler) {
status = ocb_ops->ocb_unreg_ev_handler(psoc, NULL);
ocb_debug("unregister ocb event, status:%d", status);
} else {
ocb_alert("No ocb objects or ocb_unreg_ev_handler");
status = QDF_STATUS_E_FAILURE;
}
return status;
}
QDF_STATUS tgt_ocb_register_rx_ops(struct wlan_ocb_rx_ops *ocb_rxops)
{
ocb_rxops->ocb_set_config_status = tgt_ocb_channel_config_status;
ocb_rxops->ocb_tsf_timer = tgt_ocb_get_tsf_timer;
ocb_rxops->ocb_dcc_ndl_update = tgt_ocb_dcc_ndl_update;
ocb_rxops->ocb_dcc_stats_indicate = tgt_ocb_dcc_stats_indicate;
return QDF_STATUS_SUCCESS;
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2018 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: offload lmac interface APIs for ocb
*
*/
#ifndef __TARGET_IF_OCB_H__
#define __TARGET_IF_OCB_H__
/**
* target_if_ocb_register_event_handler() - lmac handler to register ocb event
* handler
* @psoc : psoc object
* @arg: argument passed to lmac
*
* Return: QDF_STATUS
*/
QDF_STATUS target_if_ocb_register_event_handler(struct wlan_objmgr_psoc *psoc,
void *arg);
/**
* target_if_ocb_unregister_event_handler() - lmac handler to unregister ocb
* event handler
* @psoc : psoc object
* @arg: argument passed to lmac
*
* Return: QDF_STATUS
*/
QDF_STATUS target_if_ocb_unregister_event_handler(struct wlan_objmgr_psoc *psoc,
void *arg);
/**
* target_if_ocb_register_tx_ops() - lmac handler to register ocb tx ops
* callback functions
* @tx_ops: ocb tx operations
*
* Return: QDF_STATUS
*/
QDF_STATUS target_if_ocb_register_tx_ops(
struct wlan_ocb_tx_ops *tx_ops);
#endif

View File

@@ -0,0 +1,696 @@
/*
* Copyright (c) 2018 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: offload lmac interface APIs for ocb
*
*/
#include <qdf_mem.h>
#include <target_if.h>
#include <qdf_status.h>
#include <wmi_unified_api.h>
#include <wmi_unified_priv.h>
#include <wmi_unified_param.h>
#include <wlan_objmgr_psoc_obj.h>
#include <wlan_utility.h>
#include <wlan_defs.h>
#include <wlan_ocb_public_structs.h>
#include <wlan_ocb_main.h>
#include <target_if_ocb.h>
/**
* target_if_ocb_get_rx_ops() - get target interface RX operations
* @pdev: pdev handle
*
* Return: fp to target interface RX operations
*/
static inline struct wlan_ocb_rx_ops *
target_if_ocb_get_rx_ops(struct wlan_objmgr_pdev *pdev)
{
struct ocb_pdev_obj *ocb_obj;
ocb_obj = wlan_get_pdev_ocb_obj(pdev);
return &ocb_obj->ocb_rxops;
}
/**
* target_if_ocb_set_config() - send the OCB config to the FW
* @psoc: pointer to PSOC object
* @config: OCB channel configuration
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS target_if_ocb_set_config(struct wlan_objmgr_psoc *psoc,
struct ocb_config *config)
{
QDF_STATUS status;
status = wmi_unified_ocb_set_config(GET_WMI_HDL_FROM_PSOC(psoc),
config);
if (status)
target_if_err("Failed to set OCB config %d", status);
return status;
}
/**
* target_if_ocb_set_utc_time() - send the UTC time to the firmware
* @psoc: pointer to PSOC object
* @utc: pointer to the UTC time structure
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS target_if_ocb_set_utc_time(struct wlan_objmgr_psoc *psoc,
struct ocb_utc_param *utc)
{
QDF_STATUS status;
status = wmi_unified_ocb_set_utc_time_cmd(GET_WMI_HDL_FROM_PSOC(psoc),
utc);
if (status)
target_if_err("Failed to set OCB UTC time %d", status);
return status;
}
/**
* target_if_ocb_start_timing_advert() - start sending the timing
* advertisement frame on a channel
* @psoc: pointer to PSOC object
* @ta: pointer to the timing advertisement
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS
target_if_ocb_start_timing_advert(struct wlan_objmgr_psoc *psoc,
struct ocb_timing_advert_param *ta)
{
QDF_STATUS status;
status = wmi_unified_ocb_start_timing_advert(
GET_WMI_HDL_FROM_PSOC(psoc), ta);
if (status)
target_if_err("Failed to start OCB timing advert %d", status);
return status;
}
/**
* target_if_ocb_stop_timing_advert() - stop sending the timing
* advertisement frame on a channel
* @psoc: pointer to PSOC object
* @ta: pointer to the timing advertisement
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS
target_if_ocb_stop_timing_advert(struct wlan_objmgr_psoc *psoc,
struct ocb_timing_advert_param *ta)
{
QDF_STATUS status;
status =
wmi_unified_ocb_stop_timing_advert(GET_WMI_HDL_FROM_PSOC(psoc),
ta);
if (status)
target_if_err("Failed to stop OCB timing advert %d", status);
return status;
}
/**
* target_if_ocb_get_tsf_timer() - get tsf timer
* @psoc: pointer to PSOC object
* @request: pointer to the request
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS
target_if_ocb_get_tsf_timer(struct wlan_objmgr_psoc *psoc,
struct ocb_get_tsf_timer_param *request)
{
QDF_STATUS status;
status = wmi_unified_ocb_get_tsf_timer(GET_WMI_HDL_FROM_PSOC(psoc),
request);
if (status)
target_if_err("Failed to send get tsf timer cmd: %d", status);
return status;
}
/**
* target_if_dcc_get_stats() - get the DCC channel stats
* @psoc: pointer to PSOC object
* @get_stats_param: pointer to the dcc stats request
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS
target_if_dcc_get_stats(struct wlan_objmgr_psoc *psoc,
struct ocb_dcc_get_stats_param *get_stats_param)
{
QDF_STATUS status;
status = wmi_unified_dcc_get_stats_cmd(GET_WMI_HDL_FROM_PSOC(psoc),
get_stats_param);
if (status)
target_if_err("Failed to send get DCC stats cmd: %d", status);
return status;
}
/**
* target_if_dcc_clear_stats() - send command to clear the DCC stats
* @psoc: pointer to PSOC object
* @clear_stats_param: parameters to the command
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS
target_if_dcc_clear_stats(struct wlan_objmgr_psoc *psoc,
struct ocb_dcc_clear_stats_param *clear_stats_param)
{
QDF_STATUS status;
status = wmi_unified_dcc_clear_stats(GET_WMI_HDL_FROM_PSOC(psoc),
clear_stats_param);
if (status)
target_if_err("Failed to send clear DCC stats cmd: %d", status);
return status;
}
/**
* target_if_dcc_update_ndl() - command to update the NDL data
* @psoc: pointer to PSOC object
* @update_ndl_param: pointer to the request parameters
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS
target_if_dcc_update_ndl(struct wlan_objmgr_psoc *psoc,
struct ocb_dcc_update_ndl_param *update_ndl_param)
{
QDF_STATUS status;
/* Send the WMI command */
status = wmi_unified_dcc_update_ndl(GET_WMI_HDL_FROM_PSOC(psoc),
update_ndl_param);
if (status)
target_if_err("Failed to send NDL update cmd: %d", status);
return status;
}
/**
* target_if_ocb_set_config_resp() - handler for channel config response
* @scn: scn handle
* @event_buf: pointer to the event buffer
* @len: length of the buffer
*
* Return: 0 on success
*/
static int
target_if_ocb_set_config_resp(ol_scn_t scn, uint8_t *event_buf,
uint32_t len)
{
int rc;
QDF_STATUS status;
uint32_t resp;
struct wlan_objmgr_pdev *pdev;
struct wlan_objmgr_psoc *psoc;
struct wlan_ocb_rx_ops *ocb_rx_ops;
target_if_debug("scn:%pK, data:%pK, datalen:%d",
scn, event_buf, len);
if (!scn || !event_buf) {
target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf);
return -EINVAL;
}
psoc = target_if_get_psoc_from_scn_hdl(scn);
if (!psoc) {
target_if_err("null psoc");
return -EINVAL;
}
pdev = wlan_objmgr_get_pdev_by_id(psoc, 0,
WLAN_OCB_SB_ID);
if (!pdev) {
target_if_err("pdev is NULL");
return -EINVAL;
}
ocb_rx_ops = target_if_ocb_get_rx_ops(pdev);
if (ocb_rx_ops->ocb_set_config_status) {
status = wmi_extract_ocb_set_channel_config_resp(
GET_WMI_HDL_FROM_PSOC(psoc),
event_buf, &resp);
if (QDF_IS_STATUS_ERROR(status)) {
target_if_err("Failed to extract config status");
rc = -EINVAL;
goto exit;
}
status = ocb_rx_ops->ocb_set_config_status(psoc, resp);
if (status != QDF_STATUS_SUCCESS) {
target_if_err("ocb_set_config_status failed.");
rc = -EINVAL;
goto exit;
}
rc = 0;
} else {
target_if_fatal("No ocb_set_config_status callback");
rc = -EINVAL;
}
exit:
wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID);
return rc;
};
/**
* target_if_ocb_get_tsf_timer_resp() - handler for TSF timer response
* @scn: scn handle
* @event_buf: pointer to the event buffer
* @len: length of the buffer
*
* Return: 0 on success
*/
static int target_if_ocb_get_tsf_timer_resp(ol_scn_t scn,
uint8_t *event_buf,
uint32_t len)
{
int rc;
QDF_STATUS status;
struct wlan_objmgr_pdev *pdev;
struct wlan_objmgr_psoc *psoc;
struct ocb_get_tsf_timer_response response;
struct wlan_ocb_rx_ops *ocb_rx_ops;
target_if_debug("scn:%pK, data:%pK, datalen:%d",
scn, event_buf, len);
if (!scn || !event_buf) {
target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf);
return -EINVAL;
}
psoc = target_if_get_psoc_from_scn_hdl(scn);
if (!psoc) {
target_if_err("null psoc");
return -EINVAL;
}
pdev = wlan_objmgr_get_pdev_by_id(psoc, 0,
WLAN_OCB_SB_ID);
if (!pdev) {
target_if_err("pdev is NULL");
return -EINVAL;
}
ocb_rx_ops = target_if_ocb_get_rx_ops(pdev);
if (ocb_rx_ops->ocb_tsf_timer) {
status = wmi_extract_ocb_tsf_timer(GET_WMI_HDL_FROM_PSOC(psoc),
event_buf, &response);
if (QDF_IS_STATUS_ERROR(status)) {
target_if_err("Failed to extract tsf timer");
rc = -EINVAL;
goto exit;
}
status = ocb_rx_ops->ocb_tsf_timer(psoc, &response);
if (status != QDF_STATUS_SUCCESS) {
target_if_err("ocb_tsf_timer failed.");
rc = -EINVAL;
goto exit;
}
rc = 0;
} else {
target_if_fatal("No ocb_tsf_timer callback");
rc = -EINVAL;
}
exit:
wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID);
return rc;
}
/**
* target_if_dcc_update_ndl_resp() - handler for update NDL response
* @scn: scn handle
* @event_buf: pointer to the event buffer
* @len: length of the buffer
*
* Return: 0 on success
*/
static int target_if_dcc_update_ndl_resp(ol_scn_t scn,
uint8_t *event_buf,
uint32_t len)
{
int rc;
QDF_STATUS status;
struct wlan_objmgr_pdev *pdev;
struct wlan_objmgr_psoc *psoc;
struct ocb_dcc_update_ndl_response *resp;
struct wlan_ocb_rx_ops *ocb_rx_ops;
target_if_debug("scn:%pK, data:%pK, datalen:%d",
scn, event_buf, len);
if (!scn || !event_buf) {
target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf);
return -EINVAL;
}
psoc = target_if_get_psoc_from_scn_hdl(scn);
if (!psoc) {
target_if_err("null psoc");
return -EINVAL;
}
pdev = wlan_objmgr_get_pdev_by_id(psoc, 0,
WLAN_OCB_SB_ID);
if (!pdev) {
target_if_err("pdev is NULL");
return -EINVAL;
}
/* Allocate and populate the response */
resp = qdf_mem_malloc(sizeof(*resp));
if (!resp) {
target_if_err("Error allocating memory for the response.");
rc = -ENOMEM;
goto exit;
}
ocb_rx_ops = target_if_ocb_get_rx_ops(pdev);
if (ocb_rx_ops->ocb_dcc_ndl_update) {
status = wmi_extract_dcc_update_ndl_resp(
GET_WMI_HDL_FROM_PSOC(psoc),
event_buf, resp);
if (QDF_IS_STATUS_ERROR(status)) {
target_if_err("Failed to extract ndl status");
rc = -EINVAL;
goto exit;
}
status = ocb_rx_ops->ocb_dcc_ndl_update(psoc, resp);
if (status != QDF_STATUS_SUCCESS) {
target_if_err("dcc_ndl_update failed.");
rc = -EINVAL;
goto exit;
}
rc = 0;
} else {
target_if_fatal("No dcc_ndl_update callback");
rc = -EINVAL;
}
exit:
wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID);
if (resp)
qdf_mem_free(resp);
return rc;
}
/**
* target_if_dcc_get_stats_resp() - handler for get stats response
* @scn: scn handle
* @event_buf: pointer to the event buffer
* @len: length of the buffer
*
* Return: 0 on success
*/
static int target_if_dcc_get_stats_resp(ol_scn_t scn,
uint8_t *event_buf,
uint32_t len)
{
int rc;
QDF_STATUS status;
struct wlan_objmgr_pdev *pdev;
struct wlan_objmgr_psoc *psoc;
struct ocb_dcc_get_stats_response *response;
struct wlan_ocb_rx_ops *ocb_rx_ops;
target_if_debug("scn:%pK, data:%pK, datalen:%d",
scn, event_buf, len);
if (!scn || !event_buf) {
target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf);
return -EINVAL;
}
psoc = target_if_get_psoc_from_scn_hdl(scn);
if (!psoc) {
target_if_err("null psoc");
return -EINVAL;
}
pdev = wlan_objmgr_get_pdev_by_id(psoc, 0,
WLAN_OCB_SB_ID);
if (!pdev) {
target_if_err("pdev is NULL");
return -EINVAL;
}
ocb_rx_ops = target_if_ocb_get_rx_ops(pdev);
if (ocb_rx_ops->ocb_dcc_stats_indicate) {
status = wmi_extract_dcc_stats(GET_WMI_HDL_FROM_PSOC(psoc),
event_buf, &response);
if (!response || QDF_IS_STATUS_ERROR(status)) {
target_if_err("Cannot get DCC stats");
rc = -ENOMEM;
goto exit;
}
status = ocb_rx_ops->ocb_dcc_stats_indicate(psoc,
response,
true);
if (QDF_IS_STATUS_ERROR(status)) {
target_if_err("dcc_stats_indicate failed.");
rc = -EINVAL;
goto exit;
}
rc = 0;
} else {
target_if_fatal("No dcc_stats_indicate callback");
rc = -EINVAL;
}
exit:
wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID);
if (response)
qdf_mem_free(response);
return rc;
}
/**
* target_if_dcc_stats_resp() - handler for DCC stats indication event
* @scn: scn handle
* @event_buf: pointer to the event buffer
* @len: length of the buffer
*
* Return: 0 on success
*/
static int target_if_dcc_stats_resp(ol_scn_t scn, uint8_t *event_buf,
uint32_t len)
{
int rc;
QDF_STATUS status;
struct wlan_objmgr_pdev *pdev;
struct wlan_objmgr_psoc *psoc;
struct ocb_dcc_get_stats_response *response;
struct wlan_ocb_rx_ops *ocb_rx_ops;
target_if_debug("scn:%pK, data:%pK, datalen:%d",
scn, event_buf, len);
if (!scn || !event_buf) {
target_if_err("scn: 0x%pK, data: 0x%pK", scn, event_buf);
return -EINVAL;
}
psoc = target_if_get_psoc_from_scn_hdl(scn);
if (!psoc) {
target_if_err("null psoc");
return -EINVAL;
}
pdev = wlan_objmgr_get_pdev_by_id(psoc, 0,
WLAN_OCB_SB_ID);
if (!pdev) {
target_if_err("pdev is NULL");
return -EINVAL;
}
ocb_rx_ops = target_if_ocb_get_rx_ops(pdev);
if (ocb_rx_ops->ocb_dcc_stats_indicate) {
status = wmi_extract_dcc_stats(GET_WMI_HDL_FROM_PSOC(psoc),
event_buf, &response);
if (!response || QDF_IS_STATUS_ERROR(status)) {
target_if_err("Cannot get DCC stats");
rc = -ENOMEM;
goto exit;
}
status = ocb_rx_ops->ocb_dcc_stats_indicate(psoc,
response,
false);
if (QDF_IS_STATUS_ERROR(status)) {
target_if_err("dcc_stats_indicate failed.");
rc = -EINVAL;
goto exit;
}
rc = 0;
} else {
target_if_fatal("dcc_stats_indicate failed.");
response = NULL;
rc = -EINVAL;
}
exit:
wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_SB_ID);
if (response)
qdf_mem_free(response);
return rc;
}
QDF_STATUS target_if_ocb_register_event_handler(struct wlan_objmgr_psoc *psoc,
void *arg)
{
int rc;
/* Initialize the members in WMA used by wma_ocb */
rc = wmi_unified_register_event(GET_WMI_HDL_FROM_PSOC(psoc),
wmi_ocb_set_config_resp_event_id,
target_if_ocb_set_config_resp);
if (rc) {
target_if_err("Failed to register OCB config resp event cb");
return QDF_STATUS_E_FAILURE;
}
rc = wmi_unified_register_event(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_ocb_get_tsf_timer_resp_event_id,
target_if_ocb_get_tsf_timer_resp);
if (rc) {
target_if_err("Failed to register OCB TSF resp event cb");
goto unreg_set_config;
}
rc = wmi_unified_register_event(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_dcc_get_stats_resp_event_id,
target_if_dcc_get_stats_resp);
if (rc) {
target_if_err("Failed to register DCC get stats resp event cb");
goto unreg_tsf_timer;
}
rc = wmi_unified_register_event(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_dcc_update_ndl_resp_event_id,
target_if_dcc_update_ndl_resp);
if (rc) {
target_if_err("Failed to register NDL update event cb");
goto unreg_get_stats;
}
rc = wmi_unified_register_event(GET_WMI_HDL_FROM_PSOC(psoc),
wmi_dcc_stats_event_id,
target_if_dcc_stats_resp);
if (rc) {
target_if_err("Failed to register DCC stats event cb");
goto unreg_ndl;
}
return QDF_STATUS_SUCCESS;
unreg_ndl:
wmi_unified_unregister_event_handler(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_dcc_update_ndl_resp_event_id);
unreg_get_stats:
wmi_unified_unregister_event_handler(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_dcc_get_stats_resp_event_id);
unreg_tsf_timer:
wmi_unified_unregister_event_handler(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_ocb_get_tsf_timer_resp_event_id);
unreg_set_config:
wmi_unified_unregister_event(GET_WMI_HDL_FROM_PSOC(psoc),
wmi_ocb_set_config_resp_event_id);
return QDF_STATUS_E_FAILURE;
}
QDF_STATUS
target_if_ocb_unregister_event_handler(struct wlan_objmgr_psoc *psoc,
void *arg)
{
int rc;
rc = wmi_unified_unregister_event_handler(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_dcc_stats_event_id);
if (rc)
target_if_err("Failed to unregister DCC stats event cb");
rc = wmi_unified_unregister_event_handler(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_dcc_update_ndl_resp_event_id);
if (rc)
target_if_err("Failed to unregister NDL update event cb");
rc = wmi_unified_unregister_event_handler(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_dcc_get_stats_resp_event_id);
if (rc)
target_if_err("Failed to unregister DCC get stats resp cb");
rc = wmi_unified_unregister_event_handler(
GET_WMI_HDL_FROM_PSOC(psoc),
wmi_ocb_get_tsf_timer_resp_event_id);
if (rc)
target_if_err("Failed to unregister OCB TSF resp event cb");
rc = wmi_unified_unregister_event(GET_WMI_HDL_FROM_PSOC(psoc),
wmi_ocb_set_config_resp_event_id);
if (rc)
target_if_err("Failed to unregister OCB config resp event cb");
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
target_if_ocb_register_tx_ops(struct wlan_ocb_tx_ops *ocb_txops)
{
ocb_txops->ocb_set_config = target_if_ocb_set_config;
ocb_txops->ocb_set_utc_time = target_if_ocb_set_utc_time;
ocb_txops->ocb_start_timing_advert = target_if_ocb_start_timing_advert;
ocb_txops->ocb_stop_timing_advert = target_if_ocb_stop_timing_advert;
ocb_txops->ocb_get_tsf_timer = target_if_ocb_get_tsf_timer;
ocb_txops->ocb_dcc_get_stats = target_if_dcc_get_stats;
ocb_txops->ocb_dcc_clear_stats = target_if_dcc_clear_stats;
ocb_txops->ocb_dcc_update_ndl = target_if_dcc_update_ndl;
ocb_txops->ocb_reg_ev_handler = target_if_ocb_register_event_handler;
ocb_txops->ocb_unreg_ev_handler =
target_if_ocb_unregister_event_handler;
return QDF_STATUS_SUCCESS;
}