diff --git a/components/ocb/core/src/wlan_ocb_main.c b/components/ocb/core/src/wlan_ocb_main.c new file mode 100644 index 0000000000..f5c653ab83 --- /dev/null +++ b/components/ocb/core/src/wlan_ocb_main.c @@ -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 +#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 +#include +#include +#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; +} diff --git a/components/ocb/dispatcher/inc/wlan_ocb_tgt_api.h b/components/ocb/dispatcher/inc/wlan_ocb_tgt_api.h new file mode 100644 index 0000000000..4456f603b9 --- /dev/null +++ b/components/ocb/dispatcher/inc/wlan_ocb_tgt_api.h @@ -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 + +/** + * 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 diff --git a/components/ocb/dispatcher/src/wlan_ocb_tgt_api.c b/components/ocb/dispatcher/src/wlan_ocb_tgt_api.c new file mode 100644 index 0000000000..a56329c5c7 --- /dev/null +++ b/components/ocb/dispatcher/src/wlan_ocb_tgt_api.c @@ -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 +#include +#include +#include +#include +#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; +} diff --git a/components/target_if/ocb/inc/target_if_ocb.h b/components/target_if/ocb/inc/target_if_ocb.h new file mode 100644 index 0000000000..13f9afb9c8 --- /dev/null +++ b/components/target_if/ocb/inc/target_if_ocb.h @@ -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 diff --git a/components/target_if/ocb/src/target_if_ocb.c b/components/target_if/ocb/src/target_if_ocb.c new file mode 100644 index 0000000000..6d66294b84 --- /dev/null +++ b/components/target_if/ocb/src/target_if_ocb.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * 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; +}