/* * 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 north bound interface definitions */ #include #include #include #include #include #include #include #include #include #include "wlan_ocb_main.h" /** * wlan_ocb_get_tx_ops() - get target interface tx operations * @pdev: pdev handle * * Return: fp to target interface operations */ static struct wlan_ocb_tx_ops * wlan_ocb_get_tx_ops(struct wlan_objmgr_pdev *pdev) { struct ocb_pdev_obj *ocb_obj; ocb_obj = wlan_get_pdev_ocb_obj(pdev); if (!ocb_obj) { ocb_err("failed to get OCB pdev object"); return NULL; } return &ocb_obj->ocb_txops; } QDF_STATUS ucfg_ocb_init(void) { QDF_STATUS status; ocb_notice("ocb module dispatcher init"); status = wlan_objmgr_register_pdev_create_handler(WLAN_UMAC_COMP_OCB, ocb_pdev_obj_create_notification, NULL); if (QDF_IS_STATUS_ERROR(status)) { ocb_err("Failed to register pdev create handler for ocb"); return status; } status = wlan_objmgr_register_pdev_destroy_handler(WLAN_UMAC_COMP_OCB, ocb_pdev_obj_destroy_notification, NULL); if (QDF_IS_STATUS_ERROR(status)) { ocb_err("Failed to register pdev destroy handler for ocb"); goto fail_delete_pdev; } return status; fail_delete_pdev: wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_OCB, ocb_pdev_obj_create_notification, NULL); return status; } QDF_STATUS ucfg_ocb_deinit(void) { QDF_STATUS status; ocb_notice("ocb module dispatcher deinit"); status = wlan_objmgr_unregister_pdev_destroy_handler(WLAN_UMAC_COMP_OCB, ocb_pdev_obj_destroy_notification, NULL); if (QDF_IS_STATUS_ERROR(status)) ocb_err("Failed to unregister pdev destroy handler"); status = wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_OCB, ocb_pdev_obj_create_notification, NULL); if (QDF_IS_STATUS_ERROR(status)) ocb_err("Failed to unregister pdev create handler"); return status; } QDF_STATUS ocb_psoc_enable(struct wlan_objmgr_psoc *psoc) { struct wlan_objmgr_pdev *pdev; pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_OCB_NB_ID); if (!pdev) { ocb_err("Failed to get pdev handle"); return QDF_STATUS_E_FAILURE; } tgt_ocb_register_ev_handler(pdev); wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_NB_ID); return QDF_STATUS_SUCCESS; } QDF_STATUS ocb_psoc_disable(struct wlan_objmgr_psoc *psoc) { struct wlan_objmgr_pdev *pdev; pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_OCB_NB_ID); if (!pdev) { ocb_err("Failed to get pdev handle"); return QDF_STATUS_E_FAILURE; } tgt_ocb_unregister_ev_handler(pdev); wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_NB_ID); return QDF_STATUS_SUCCESS; } QDF_STATUS ucfg_ocb_set_txrx_handle(struct wlan_objmgr_psoc *psoc, void *txrx_handle) { struct wlan_objmgr_pdev *pdev; struct ocb_pdev_obj *ocb_obj; pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_OCB_NB_ID); if (!pdev) { ocb_err("Failed to get pdev handle"); return QDF_STATUS_E_FAILURE; } ocb_obj = wlan_get_pdev_ocb_obj(pdev); wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_NB_ID); if (!ocb_obj) { ocb_err("OCB object is NULL"); return QDF_STATUS_E_FAILURE; } ocb_obj->dp_pdev = txrx_handle; return QDF_STATUS_SUCCESS; } QDF_STATUS ucfg_ocb_update_dp_handle(struct wlan_objmgr_psoc *psoc, void *dp_soc) { struct wlan_objmgr_pdev *pdev; struct ocb_pdev_obj *ocb_obj; pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_OCB_NB_ID); if (!pdev) { ocb_err("Failed to get pdev handle"); return QDF_STATUS_E_FAILURE; } ocb_obj = wlan_get_pdev_ocb_obj(pdev); wlan_objmgr_pdev_release_ref(pdev, WLAN_OCB_NB_ID); if (!ocb_obj) { ocb_err("OCB object is NULL"); return QDF_STATUS_E_FAILURE; } ocb_obj->dp_soc = dp_soc; return QDF_STATUS_SUCCESS; } QDF_STATUS ucfg_ocb_config_channel(struct wlan_objmgr_pdev *pdev) { QDF_STATUS status; struct ocb_config *config; struct ocb_pdev_obj *ocb_obj; struct wlan_objmgr_psoc *psoc; struct wlan_ocb_tx_ops *tx_ops; ocb_obj = wlan_get_pdev_ocb_obj(pdev); if (!ocb_obj || !ocb_obj->channel_config) { ocb_alert("The request could not be found"); return QDF_STATUS_E_FAILURE; } config = ocb_obj->channel_config; ocb_debug("Set config to vdev%d", config->vdev_id); psoc = wlan_pdev_get_psoc(pdev); if (!psoc) { ocb_err("Null pointer for psoc"); return QDF_STATUS_E_INVAL; } tx_ops = wlan_ocb_get_tx_ops(pdev); if (tx_ops && tx_ops->ocb_set_config) status = tx_ops->ocb_set_config(psoc, ocb_obj->channel_config); else status = QDF_STATUS_E_IO; if (QDF_IS_STATUS_SUCCESS(status)) { ocb_debug("Set channel cmd is sent to southbound"); } else { ocb_err("Failed to set channel config to southbound"); if (ocb_obj->channel_config) { /* * On success case, backup parameters will be released * after channel info is synced to DP */ ocb_info("release the backed config parameters"); qdf_mem_free(ocb_obj->channel_config); ocb_obj->channel_config = NULL; } } return status; } QDF_STATUS ucfg_ocb_set_channel_config(struct wlan_objmgr_vdev *vdev, struct ocb_config *config, ocb_sync_callback set_config_cb, void *arg) { QDF_STATUS status; enum wlan_vdev_state state; uint32_t i; struct wlan_objmgr_pdev *pdev; struct wlan_objmgr_psoc *psoc; struct wlan_ocb_tx_ops *tx_ops; struct ocb_pdev_obj *ocb_obj; struct ocb_callbacks *ocb_cbs; pdev = wlan_vdev_get_pdev(vdev); if (!pdev) { ocb_err("Null pointer for pdev"); return QDF_STATUS_E_INVAL; } ocb_obj = wlan_get_pdev_ocb_obj(pdev); if (!ocb_obj) { ocb_alert("Failed to get OCB vdev object"); return QDF_STATUS_E_IO; } if (!config) { ocb_err("Invalid config input"); return QDF_STATUS_E_FAILURE; } for (i = 0; i < config->channel_count; i++) { if (WLAN_REG_CHAN_TO_BAND(wlan_reg_freq_to_chan(pdev, config->channels[i].chan_freq)) == BAND_2G) config->channels[i].ch_mode = MODE_11G; else config->channels[i].ch_mode = MODE_11A; } /* * backup the new configuration, * it will be released after target's response */ ocb_obj->channel_config = ocb_copy_config(config); if (!ocb_obj->channel_config) { ocb_err("Failed to backup config"); return QDF_STATUS_E_NOMEM; } ocb_cbs = &ocb_obj->ocb_cbs; ocb_cbs->ocb_set_config_callback = set_config_cb; ocb_cbs->ocb_set_config_context = arg; state = wlan_vdev_mlme_get_state(vdev); if (state != WLAN_VDEV_S_RUN) { /* Vdev is not started, start it */ ocb_debug("OCB vdev%d is not up", config->vdev_id); status = ocb_vdev_start(ocb_obj); } else { psoc = wlan_vdev_get_psoc(vdev); tx_ops = wlan_ocb_get_tx_ops(pdev); if (tx_ops && tx_ops->ocb_set_config) status = tx_ops->ocb_set_config(psoc, config); else status = QDF_STATUS_E_IO; ocb_debug("Set config to vdev%d", config->vdev_id); if (QDF_IS_STATUS_SUCCESS(status)) ocb_debug("Set channel cmd is sent to southbound"); else ocb_err("Failed to set channel config to southbound"); } if (QDF_IS_STATUS_ERROR(status) && ocb_obj->channel_config) { /* * On success case, backup parameters will be released * after channel info is synced to DP */ ocb_info("release the backed config parameters"); qdf_mem_free(ocb_obj->channel_config); ocb_obj->channel_config = NULL; } return status; } QDF_STATUS ucfg_ocb_set_utc_time(struct wlan_objmgr_vdev *vdev, struct ocb_utc_param *utc) { QDF_STATUS status; struct wlan_objmgr_psoc *psoc; struct wlan_objmgr_pdev *pdev; struct wlan_ocb_tx_ops *tx_ops; psoc = wlan_vdev_get_psoc(vdev); pdev = wlan_vdev_get_pdev(vdev); if (!psoc || !pdev) { ocb_err("Null pointer for psoc/pdev"); return QDF_STATUS_E_INVAL; } tx_ops = wlan_ocb_get_tx_ops(pdev); if (!tx_ops) { ocb_alert("tx_ops is null"); return QDF_STATUS_E_IO; } if (!tx_ops->ocb_set_utc_time) { ocb_alert("ocb_set_utc_time is null"); return QDF_STATUS_E_IO; } status = tx_ops->ocb_set_utc_time(psoc, utc); if (QDF_IS_STATUS_ERROR(status)) ocb_err("Failed to set UTC time to southbound"); else ocb_debug("UTC time is sent to southbound"); return status; } QDF_STATUS ucfg_ocb_start_timing_advert(struct wlan_objmgr_vdev *vdev, struct ocb_timing_advert_param *ta) { QDF_STATUS status; struct wlan_objmgr_psoc *psoc; struct wlan_objmgr_pdev *pdev; struct wlan_ocb_tx_ops *tx_ops; psoc = wlan_vdev_get_psoc(vdev); pdev = wlan_vdev_get_pdev(vdev); if (!psoc || !pdev) { ocb_err("Null pointer for psoc/pdev"); return QDF_STATUS_E_INVAL; } tx_ops = wlan_ocb_get_tx_ops(pdev); if (!tx_ops) { ocb_alert("tx_ops is null"); return QDF_STATUS_E_IO; } if (!tx_ops->ocb_start_timing_advert) { ocb_alert("ocb_start_timing_advert is null"); return QDF_STATUS_E_IO; } status = tx_ops->ocb_start_timing_advert(psoc, ta); if (QDF_IS_STATUS_ERROR(status)) ocb_err("Failed to sent start timing advert to southbound"); else ocb_debug("Start timing advert is sent to southbound"); return status; } QDF_STATUS ucfg_ocb_stop_timing_advert(struct wlan_objmgr_vdev *vdev, struct ocb_timing_advert_param *ta) { QDF_STATUS status; struct wlan_objmgr_psoc *psoc; struct wlan_objmgr_pdev *pdev; struct wlan_ocb_tx_ops *tx_ops; psoc = wlan_vdev_get_psoc(vdev); pdev = wlan_vdev_get_pdev(vdev); if (!psoc || !pdev) { ocb_err("Null pointer for psoc/pdev"); return QDF_STATUS_E_INVAL; } tx_ops = wlan_ocb_get_tx_ops(pdev); if (!tx_ops) { ocb_alert("tx_ops is null"); return QDF_STATUS_E_IO; } if (!tx_ops->ocb_stop_timing_advert) { ocb_alert("ocb_stop_timing_advert is null"); return QDF_STATUS_E_IO; } status = tx_ops->ocb_stop_timing_advert(psoc, ta); if (QDF_IS_STATUS_ERROR(status)) ocb_err("Failed to sent start timing advert to southbound"); else ocb_debug("Start timing advert is sent to southbound"); return status; } QDF_STATUS ucfg_ocb_get_tsf_timer(struct wlan_objmgr_vdev *vdev, struct ocb_get_tsf_timer_param *req, ocb_sync_callback get_tsf_cb, void *arg) { QDF_STATUS status; struct wlan_objmgr_psoc *psoc; struct ocb_get_tsf_timer_param request; struct wlan_ocb_tx_ops *tx_ops; struct ocb_callbacks *ocb_cbs; struct wlan_objmgr_pdev *pdev; pdev = wlan_vdev_get_pdev(vdev); if (!pdev) { ocb_err("Null pointer for pdev"); return QDF_STATUS_E_INVAL; } tx_ops = wlan_ocb_get_tx_ops(pdev); if (!tx_ops) { ocb_alert("tx_ops is null"); return QDF_STATUS_E_IO; } if (!tx_ops->ocb_get_tsf_timer) { ocb_alert("ocb_get_tsf_timer is null"); return QDF_STATUS_E_IO; } ocb_cbs = wlan_ocb_get_callbacks(pdev); ocb_cbs->ocb_get_tsf_timer_context = arg; ocb_cbs->ocb_get_tsf_timer_callback = get_tsf_cb; request.vdev_id = req->vdev_id; psoc = wlan_vdev_get_psoc(vdev); if (!psoc) { ocb_err("Null pointer for psoc"); return QDF_STATUS_E_INVAL; } status = tx_ops->ocb_get_tsf_timer(psoc, &request); if (QDF_IS_STATUS_ERROR(status)) ocb_err("Failed to sent get tsf timer to southbound"); else ocb_debug("Get tsf timer is sent to southbound"); return status; } QDF_STATUS ucfg_ocb_dcc_get_stats(struct wlan_objmgr_vdev *vdev, struct ocb_dcc_get_stats_param *request, ocb_sync_callback dcc_get_stats_cb, void *arg) { QDF_STATUS status; struct wlan_objmgr_psoc *psoc; struct wlan_objmgr_pdev *pdev; struct ocb_callbacks *ocb_cbs; struct wlan_ocb_tx_ops *tx_ops; pdev = wlan_vdev_get_pdev(vdev); if (!pdev) { ocb_err("Null pointer for pdev"); return QDF_STATUS_E_INVAL; } ocb_cbs = wlan_ocb_get_callbacks(pdev); ocb_cbs->ocb_dcc_get_stats_context = arg; ocb_cbs->ocb_dcc_get_stats_callback = dcc_get_stats_cb; tx_ops = wlan_ocb_get_tx_ops(pdev); if (!tx_ops) { ocb_alert("tx_ops is null"); return QDF_STATUS_E_IO; } if (!tx_ops->ocb_dcc_get_stats) { ocb_alert("ocb_dcc_get_stats is null"); return QDF_STATUS_E_IO; } psoc = wlan_vdev_get_psoc(vdev); if (!psoc) { ocb_err("Null pointer for psoc"); return QDF_STATUS_E_INVAL; } status = tx_ops->ocb_dcc_get_stats(psoc, request); if (QDF_IS_STATUS_ERROR(status)) ocb_err("Failed to sent get dcc stats to southbound"); else ocb_debug("Get dcc stats is sent to southbound"); return status; } QDF_STATUS ucfg_ocb_dcc_clear_stats(struct wlan_objmgr_vdev *vdev, uint16_t vdev_id, uint32_t bitmap) { QDF_STATUS status; struct wlan_objmgr_psoc *psoc; struct wlan_objmgr_pdev *pdev; struct wlan_ocb_tx_ops *tx_ops; struct ocb_dcc_clear_stats_param clear_stats_param; pdev = wlan_vdev_get_pdev(vdev); if (!pdev) { ocb_err("Null pointer for pdev"); return QDF_STATUS_E_INVAL; } tx_ops = wlan_ocb_get_tx_ops(pdev); if (!tx_ops) { ocb_alert("tx_ops is null"); return QDF_STATUS_E_IO; } if (!tx_ops->ocb_dcc_clear_stats) { ocb_alert("ocb_dcc_clear_stats is null"); return QDF_STATUS_E_IO; } clear_stats_param.vdev_id = vdev_id; clear_stats_param.dcc_stats_bitmap = bitmap; psoc = wlan_vdev_get_psoc(vdev); if (!psoc) { ocb_err("Null pointer for psoc"); return QDF_STATUS_E_INVAL; } status = tx_ops->ocb_dcc_clear_stats(psoc, &clear_stats_param); if (QDF_IS_STATUS_ERROR(status)) ocb_err("Failed to sent clear dcc stats to southbound"); else ocb_debug("clear dcc stats is sent to southbound"); return status; } QDF_STATUS ucfg_ocb_dcc_update_ndl(struct wlan_objmgr_vdev *vdev, struct ocb_dcc_update_ndl_param *request, ocb_sync_callback dcc_update_ndl_cb, void *arg) { QDF_STATUS status; struct wlan_objmgr_psoc *psoc; struct ocb_callbacks *ocb_cbs; struct wlan_objmgr_pdev *pdev; struct wlan_ocb_tx_ops *tx_ops; pdev = wlan_vdev_get_pdev(vdev); if (!pdev) { ocb_err("Null pointer for pdev"); return QDF_STATUS_E_INVAL; } ocb_cbs = wlan_ocb_get_callbacks(pdev); ocb_cbs->ocb_dcc_update_ndl_context = arg; ocb_cbs->ocb_dcc_update_ndl_callback = dcc_update_ndl_cb; tx_ops = wlan_ocb_get_tx_ops(pdev); if (!tx_ops) { ocb_alert("tx_ops is null"); return QDF_STATUS_E_IO; } if (!tx_ops->ocb_dcc_update_ndl) { ocb_alert("dcc_update_ndl is null"); return QDF_STATUS_E_IO; } psoc = wlan_vdev_get_psoc(vdev); if (!psoc) { ocb_err("Null pointer for psoc"); return QDF_STATUS_E_INVAL; } status = tx_ops->ocb_dcc_update_ndl(psoc, request); if (QDF_IS_STATUS_ERROR(status)) ocb_err("Failed to sent update ndl to southbound"); else ocb_debug("Update ndl is sent to southbound"); return status; } QDF_STATUS ucfg_ocb_register_for_dcc_stats_event(struct wlan_objmgr_pdev *pdev, void *ctx, ocb_sync_callback dcc_stats_cb) { struct ocb_callbacks *ocb_cbs; if (!pdev) { ocb_err("Null pointer for pdev"); return QDF_STATUS_E_INVAL; } ocb_cbs = wlan_ocb_get_callbacks(pdev); if (!ocb_cbs) { ocb_err("Failed to register dcc stats callback"); return QDF_STATUS_E_FAILURE; } ocb_cbs->ocb_dcc_stats_event_context = ctx; ocb_cbs->ocb_dcc_stats_event_callback = dcc_stats_cb; return QDF_STATUS_SUCCESS; } QDF_STATUS ucfg_ocb_register_vdev_start(struct wlan_objmgr_pdev *pdev, QDF_STATUS (*ocb_start)(struct ocb_config *)) { struct ocb_callbacks *ocb_cbs; if (!pdev) { ocb_err("Null pointer for pdev"); return QDF_STATUS_E_INVAL; } ocb_cbs = wlan_ocb_get_callbacks(pdev); if (!ocb_cbs) { ocb_err("Failed to register dcc stats callback"); return QDF_STATUS_E_FAILURE; } ocb_cbs->start_ocb_vdev = ocb_start; return QDF_STATUS_SUCCESS; }