diff --git a/target_if/direct_buf_rx/inc/target_if_direct_buf_rx_api.h b/target_if/direct_buf_rx/inc/target_if_direct_buf_rx_api.h new file mode 100644 index 000000000000..412a98d6bf18 --- /dev/null +++ b/target_if/direct_buf_rx/inc/target_if_direct_buf_rx_api.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef _TARGET_IF_DIRECT_BUF_RX_API_H_ +#define _TARGET_IF_DIRECT_BUF_RX_API_H_ + +#include "qdf_nbuf.h" +#include "qdf_atomic.h" + +#define direct_buf_rx_log(level, args...) \ + QDF_TRACE(QDF_MODULE_ID_DIRECT_BUF_RX, level, ## args) +#define direct_buf_rx_logfl(level, format, args...) \ + direct_buf_rx_log(level, FL(format), ## args) +#define direct_buf_alert(format, args...) \ + direct_buf_rx_logfl(QDF_TRACE_LEVEL_FATAL, format, ## args) +#define direct_buf_rx_err(format, args...) \ + direct_buf_rx_logfl(QDF_TRACE_LEVEL_ERROR, format, ## args) +#define direct_buf_rx_warn(format, args...) \ + direct_buf_rx_logfl(QDF_TRACE_LEVEL_WARN, format, ## args) +#define direct_buf_rx_notice(format, args...) \ + direct_buf_rx_logfl(QDF_TRACE_LEVEL_INFO, format, ## args) +#define direct_buf_rx_info(format, args...) \ + direct_buf_rx_logfl(QDF_TRACE_LEVEL_INFO_HIGH, format, ## args) +#define direct_buf_rx_debug(format, args...) \ + direct_buf_rx_logfl(QDF_TRACE_LEVEL_DEBUG, format, ## args) +#define direct_buf_rx_enter() \ + direct_buf_rx_logfl(QDF_TRACE_LEVEL_DEBUG, "enter") +#define direct_buf_rx_exit() \ + direct_buf_rx_logfl(QDF_TRACE_LEVEL_DEBUG, "exit") + +struct wlan_objmgr_psoc; +struct wlan_lmac_if_tx_ops; + +/** + * struct direct_buf_rx_data - direct buffer rx data + * @dbr_len: Length of the buffer DMAed + * @vaddr: Virtual address of the buffer that has DMAed data + */ +struct direct_buf_rx_data { + size_t dbr_len; + void *vaddr; +}; + +/** + * direct_buf_rx_attach: Function to initialize direct buf rx module + * @psoc: pointer to psoc object + * + * Return: QDF status of operation + */ +QDF_STATUS direct_buf_rx_attach(struct wlan_objmgr_psoc *psoc); + +/** + * direct_buf_rx_detach: Function to deinitialize direct buf rx module + * @psoc: pointer to psoc object + * + * Return: QDF status of operation + */ +QDF_STATUS direct_buf_rx_detach(struct wlan_objmgr_psoc *psoc); + +/** + * direct_buf_rx_psoc_obj_fill: Fill hal_soc,osdev in direct buf rx psoc obj + * @psoc: pointer to psoc object + * @hal_soc: Opaque HAL SOC handle + * @osdev: QDF os device handle + * + * Return: QDF status of operation + */ +QDF_STATUS direct_buf_rx_psoc_obj_fill(struct wlan_objmgr_psoc *psoc, + void *hal_soc, qdf_device_t osdev); + +/** + * target_if_direct_buf_rx_register_tx_ops: Register tx ops for direct buffer + * rx module + * @tx_ops: pointer to lmac interface tx ops + * + * Return: None + */ +void target_if_direct_buf_rx_register_tx_ops( + struct wlan_lmac_if_tx_ops *tx_ops); + +#endif /* _TARGET_IF_DIRECT_BUF_RX_API_H_ */ diff --git a/target_if/direct_buf_rx/src/target_if_direct_buf_rx_api.c b/target_if/direct_buf_rx/src/target_if_direct_buf_rx_api.c new file mode 100644 index 000000000000..13aee3c2492e --- /dev/null +++ b/target_if/direct_buf_rx/src/target_if_direct_buf_rx_api.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017 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. + */ + +#include +#include +#include +#include +#include +#include +#include "target_if_direct_buf_rx_main.h" + +QDF_STATUS direct_buf_rx_attach(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + + status = target_if_direct_buf_rx_psoc_obj_create(psoc); + + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("Failed to create psoc priv obj"); + return status; + } + + status = wlan_objmgr_register_pdev_create_handler( + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, + target_if_direct_buf_rx_pdev_create_handler, + NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("Failed to register pdev create handler"); + goto dbr_destroy_psoc_handler; + } + + status = wlan_objmgr_register_pdev_destroy_handler( + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, + target_if_direct_buf_rx_pdev_destroy_handler, + NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("Failed to register pdev destroy handler"); + goto dbr_unreg_pdev_create; + } + + status = target_if_direct_buf_rx_register_events(psoc); + + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("Direct Buffer RX register events failed"); + goto dbr_unreg_pdev_destroy; + } + + direct_buf_rx_info("Direct Buffer RX pdev,psoc create and destroy handlers registered"); + + return QDF_STATUS_SUCCESS; + +dbr_unreg_pdev_destroy: + status = wlan_objmgr_unregister_pdev_destroy_handler( + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, + target_if_direct_buf_rx_pdev_destroy_handler, + NULL); + +dbr_unreg_pdev_create: + status = wlan_objmgr_unregister_pdev_create_handler( + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, + target_if_direct_buf_rx_pdev_create_handler, + NULL); + +dbr_destroy_psoc_handler: + status = target_if_direct_buf_rx_psoc_obj_destroy(psoc); + + return QDF_STATUS_E_FAILURE; +} +qdf_export_symbol(direct_buf_rx_attach); + +QDF_STATUS direct_buf_rx_detach(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + + status = target_if_direct_buf_rx_unregister_events(psoc); + + if (QDF_IS_STATUS_ERROR(status)) + direct_buf_rx_err("Direct Buffer RX unregister events failed"); + + status = wlan_objmgr_unregister_pdev_destroy_handler( + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, + target_if_direct_buf_rx_pdev_destroy_handler, + NULL); + + if (QDF_IS_STATUS_ERROR(status)) + direct_buf_rx_err("Failed to unregister pdev destroy handler"); + + status = wlan_objmgr_unregister_pdev_create_handler( + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, + target_if_direct_buf_rx_pdev_create_handler, + NULL); + + if (QDF_IS_STATUS_ERROR(status)) + direct_buf_rx_err("Failed to unregister pdev create handler"); + + status = target_if_direct_buf_rx_psoc_obj_destroy(psoc); + + if (QDF_IS_STATUS_ERROR(status)) + direct_buf_rx_err("Failed to destroy psoc priv obj"); + + direct_buf_rx_info("Direct Buffer RX pdev,psoc create and destroy handlers unregistered"); + + return status; +} +qdf_export_symbol(direct_buf_rx_detach); + +QDF_STATUS direct_buf_rx_psoc_obj_fill(struct wlan_objmgr_psoc *psoc, + void *hal_soc, qdf_device_t osdev) +{ + struct direct_buf_rx_psoc_obj *dbr_psoc_obj; + + if (hal_soc == NULL || osdev == NULL) { + direct_buf_rx_err("hal soc or osdev is null"); + return QDF_STATUS_E_INVAL; + } + + dbr_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); + + direct_buf_rx_info("Dbr psoc obj %pK", dbr_psoc_obj); + + if (dbr_psoc_obj == NULL) { + direct_buf_rx_err("dir buf rx psoc obj is null"); + return QDF_STATUS_E_FAILURE; + } + + dbr_psoc_obj->hal_soc = hal_soc; + dbr_psoc_obj->osdev = osdev; + + return QDF_STATUS_SUCCESS; +} + +void target_if_direct_buf_rx_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops) +{ + tx_ops->dbr_tx_ops.direct_buf_rx_module_register = + target_if_direct_buf_rx_module_register; +} +qdf_export_symbol(target_if_direct_buf_rx_register_tx_ops); diff --git a/target_if/direct_buf_rx/src/target_if_direct_buf_rx_main.c b/target_if/direct_buf_rx/src/target_if_direct_buf_rx_main.c new file mode 100644 index 000000000000..3b1f90f777de --- /dev/null +++ b/target_if/direct_buf_rx/src/target_if_direct_buf_rx_main.c @@ -0,0 +1,972 @@ +/* + * Copyright (c) 2017 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. + */ + +#include "target_if.h" +#include "wmi_unified_api.h" +#include "wlan_lmac_if_def.h" +#include "target_if_direct_buf_rx_main.h" +#include +#include "hal_api.h" +#include + +static uint8_t get_num_dbr_modules_per_pdev(struct wlan_objmgr_pdev *pdev) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_psoc_ext_service_ready_param *ext_svc_param; + struct wlan_psoc_host_dbr_ring_caps *dbr_ring_cap; + uint8_t num_dbr_ring_caps, cap_idx, pdev_id, num_modules; + + psoc = wlan_pdev_get_psoc(pdev); + + if (psoc == NULL) { + direct_buf_rx_err("psoc is null"); + return 0; + } + + ext_svc_param = &psoc->ext_service_param; + num_dbr_ring_caps = ext_svc_param->service_ext_param.num_dbr_ring_caps; + dbr_ring_cap = ext_svc_param->dbr_ring_cap; + pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); + num_modules = 0; + + for (cap_idx = 0; cap_idx < num_dbr_ring_caps; cap_idx++) { + if (dbr_ring_cap[cap_idx].pdev_id == pdev_id) + num_modules++; + } + + return num_modules; +} + +static QDF_STATUS populate_dbr_cap_mod_param(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_module_param *mod_param) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_psoc_ext_service_ready_param *ext_svc_param; + struct wlan_psoc_host_dbr_ring_caps *dbr_ring_cap; + uint8_t cap_idx; + bool cap_found = false; + enum DBR_MODULE mod_id = mod_param->mod_id; + uint32_t num_dbr_ring_caps, pdev_id; + + psoc = wlan_pdev_get_psoc(pdev); + + if (psoc == NULL) { + direct_buf_rx_err("psoc is null"); + return QDF_STATUS_E_INVAL; + } + + ext_svc_param = &psoc->ext_service_param; + num_dbr_ring_caps = ext_svc_param->service_ext_param.num_dbr_ring_caps; + dbr_ring_cap = ext_svc_param->dbr_ring_cap; + pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); + + for (cap_idx = 0; cap_idx < num_dbr_ring_caps; cap_idx++) { + if (dbr_ring_cap[cap_idx].pdev_id == pdev_id) { + if (dbr_ring_cap[cap_idx].mod_id == mod_id) { + mod_param->dbr_ring_cap->ring_elems_min = + dbr_ring_cap[cap_idx].ring_elems_min; + mod_param->dbr_ring_cap->min_buf_size = + dbr_ring_cap[cap_idx].min_buf_size; + mod_param->dbr_ring_cap->min_buf_align = + dbr_ring_cap[cap_idx].min_buf_align; + cap_found = true; + } + } + } + + if (!cap_found) { + direct_buf_rx_err("No cap found for module %d in pdev %d", + mod_id, pdev_id); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS target_if_direct_buf_rx_pdev_create_handler( + struct wlan_objmgr_pdev *pdev, void *data) +{ + struct direct_buf_rx_pdev_obj *dbr_pdev_obj; + struct wlan_objmgr_psoc *psoc; + uint8_t num_modules; + QDF_STATUS status; + + direct_buf_rx_enter(); + + if (pdev == NULL) { + direct_buf_rx_err("pdev context passed is null"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_pdev_get_psoc(pdev); + + if (psoc == NULL) { + direct_buf_rx_err("psoc is null"); + return QDF_STATUS_E_INVAL; + } + + dbr_pdev_obj = qdf_mem_malloc(sizeof(*dbr_pdev_obj)); + + if (dbr_pdev_obj == NULL) { + direct_buf_rx_err("Failed to allocate dir buf rx pdev obj"); + return QDF_STATUS_E_NOMEM; + } + + direct_buf_rx_info("Dbr pdev obj %pK", dbr_pdev_obj); + + num_modules = get_num_dbr_modules_per_pdev(pdev); + direct_buf_rx_info("Number of modules = %d pdev %d", num_modules, + wlan_objmgr_pdev_get_pdev_id(pdev)); + dbr_pdev_obj->num_modules = num_modules; + dbr_pdev_obj->dbr_mod_param = qdf_mem_malloc(num_modules * + sizeof(struct direct_buf_rx_module_param)); + + if (dbr_pdev_obj->dbr_mod_param == NULL) { + direct_buf_rx_err("Failed to allocate dir buf rx mod param"); + qdf_mem_free(dbr_pdev_obj); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_pdev_component_obj_attach(pdev, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, + dbr_pdev_obj, QDF_STATUS_SUCCESS); + + if (status != QDF_STATUS_SUCCESS) { + direct_buf_rx_err("Failed to attach dir buf rx component %d", + status); + goto attach_error; + } + + return QDF_STATUS_SUCCESS; + +attach_error: + qdf_mem_free(dbr_pdev_obj->dbr_mod_param); + qdf_mem_free(dbr_pdev_obj); + + return status; +} + +QDF_STATUS target_if_direct_buf_rx_pdev_destroy_handler( + struct wlan_objmgr_pdev *pdev, void *data) +{ + struct direct_buf_rx_pdev_obj *dbr_pdev_obj; + QDF_STATUS status; + uint8_t num_modules, mod_idx; + + if (pdev == NULL) { + direct_buf_rx_err("pdev context passed is null"); + return QDF_STATUS_E_INVAL; + } + + dbr_pdev_obj = wlan_objmgr_pdev_get_comp_private_obj(pdev, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); + + if (dbr_pdev_obj == NULL) { + direct_buf_rx_err("dir buf rx object is null"); + return QDF_STATUS_E_FAILURE; + } + + num_modules = dbr_pdev_obj->num_modules; + for (mod_idx = 0; mod_idx < num_modules; mod_idx++) + target_if_deinit_dbr_ring(pdev, dbr_pdev_obj, mod_idx); + + status = wlan_objmgr_pdev_component_obj_detach(pdev, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, + dbr_pdev_obj); + + if (status != QDF_STATUS_SUCCESS) { + direct_buf_rx_err("failed to detach dir buf rx component %d", + status); + } + + qdf_mem_free(dbr_pdev_obj); + + return status; +} + +QDF_STATUS target_if_direct_buf_rx_psoc_obj_create( + struct wlan_objmgr_psoc *psoc) +{ + struct direct_buf_rx_psoc_obj *dbr_psoc_obj; + QDF_STATUS status; + + direct_buf_rx_enter(); + + if (psoc == NULL) { + direct_buf_rx_err("psoc context passed is null"); + return QDF_STATUS_E_INVAL; + } + + dbr_psoc_obj = qdf_mem_malloc(sizeof(*dbr_psoc_obj)); + + if (!dbr_psoc_obj) { + direct_buf_rx_err("failed to alloc dir buf rx psoc obj"); + return QDF_STATUS_E_NOMEM; + } + + direct_buf_rx_info("Dbr psoc obj %pK", dbr_psoc_obj); + + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, dbr_psoc_obj, + QDF_STATUS_SUCCESS); + + if (status != QDF_STATUS_SUCCESS) { + direct_buf_rx_err("Failed to attach dir buf rx component %d", + status); + goto attach_error; + } + + return status; + +attach_error: + qdf_mem_free(dbr_psoc_obj); + + return status; +} + +QDF_STATUS target_if_direct_buf_rx_psoc_obj_destroy( + struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct direct_buf_rx_psoc_obj *dbr_psoc_obj; + + direct_buf_rx_enter(); + + dbr_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); + + if (!dbr_psoc_obj) { + direct_buf_rx_err("dir buf rx psoc obj is null"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, + dbr_psoc_obj); + + if (status != QDF_STATUS_SUCCESS) { + direct_buf_rx_err("failed to detach dir buf rx component %d", + status); + } + + qdf_mem_free(dbr_psoc_obj); + + return status; +} + +static QDF_STATUS target_if_dbr_replenish_ring(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_module_param *mod_param, + void *aligned_vaddr, uint8_t cookie) +{ + uint64_t *ring_entry; + uint32_t dw_lo, dw_hi = 0, map_status; + void *hal_soc, *srng; + qdf_dma_addr_t paddr; + struct wlan_objmgr_psoc *psoc; + struct direct_buf_rx_psoc_obj *dbr_psoc_obj; + struct direct_buf_rx_ring_cfg *dbr_ring_cfg; + struct direct_buf_rx_ring_cap *dbr_ring_cap; + struct direct_buf_rx_buf_info *dbr_buf_pool; + QDF_STATUS status; + + direct_buf_rx_enter(); + + dbr_ring_cfg = mod_param->dbr_ring_cfg; + dbr_ring_cap = mod_param->dbr_ring_cap; + dbr_buf_pool = mod_param->dbr_buf_pool; + + psoc = wlan_pdev_get_psoc(pdev); + + if (!psoc) { + direct_buf_rx_err("psoc is null"); + return QDF_STATUS_E_FAILURE; + } + + dbr_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); + + if (dbr_psoc_obj == NULL) { + direct_buf_rx_err("dir buf rx psoc object is null"); + return QDF_STATUS_E_FAILURE; + } + + hal_soc = dbr_psoc_obj->hal_soc; + srng = dbr_ring_cfg->srng; + if (!aligned_vaddr) { + direct_buf_rx_err("aligned vaddr is null"); + return QDF_STATUS_SUCCESS; + } + + map_status = qdf_mem_map_nbytes_single(dbr_psoc_obj->osdev, + aligned_vaddr, + QDF_DMA_FROM_DEVICE, + dbr_ring_cap->min_buf_size, + &paddr); + if (map_status) { + direct_buf_rx_err("mem map failed status = %d", status); + return QDF_STATUS_E_FAILURE; + } + + QDF_ASSERT(!((uint64_t)paddr & dbr_ring_cap->min_buf_align)); + dbr_buf_pool[cookie].paddr = paddr; + + hal_srng_access_start(hal_soc, srng); + ring_entry = hal_srng_src_get_next(hal_soc, srng); + QDF_ASSERT(ring_entry != NULL); + dw_lo = (uint64_t)paddr & 0xFFFFFFFF; + WMI_HOST_DBR_RING_ADDR_HI_SET(dw_hi, (uint64_t)paddr >> 32); + WMI_HOST_DBR_DATA_ADDR_HI_HOST_DATA_SET(dw_hi, cookie); + direct_buf_rx_info("Cookie = %d", cookie); + direct_buf_rx_info("dw_lo = %x dw_hi = %x", dw_lo, dw_hi); + *ring_entry = (uint64_t)dw_hi << 32 | dw_lo; + direct_buf_rx_info("Valid ring entry"); + hal_srng_access_end(hal_soc, srng); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS target_if_dbr_fill_ring(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_module_param *mod_param) +{ + uint8_t idx; + void *buf, *buf_aligned; + struct direct_buf_rx_ring_cfg *dbr_ring_cfg; + struct direct_buf_rx_ring_cap *dbr_ring_cap; + struct direct_buf_rx_buf_info *dbr_buf_pool; + QDF_STATUS status; + + direct_buf_rx_enter(); + + dbr_ring_cfg = mod_param->dbr_ring_cfg; + dbr_ring_cap = mod_param->dbr_ring_cap; + dbr_buf_pool = mod_param->dbr_buf_pool; + + for (idx = 0; idx < dbr_ring_cfg->num_ptr - 1; idx++) { + buf = qdf_mem_malloc(dbr_ring_cap->min_buf_size + + dbr_ring_cap->min_buf_align - 1); + if (!buf) { + direct_buf_rx_err("dir buf rx ring buf alloc failed"); + return QDF_STATUS_E_NOMEM; + } + dbr_buf_pool[idx].vaddr = buf; + buf_aligned = (void *)(uintptr_t)qdf_roundup( + (uint64_t)(uintptr_t)buf, DBR_RING_BASE_ALIGN); + dbr_buf_pool[idx].offset = buf_aligned - buf; + dbr_buf_pool[idx].cookie = idx; + status = target_if_dbr_replenish_ring(pdev, mod_param, + buf_aligned, idx); + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("replenish failed with status : %d", + status); + qdf_mem_free(buf); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS target_if_dbr_init_ring(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_module_param *mod_param) +{ + void *srng; + uint32_t num_entries, ring_alloc_size, max_entries, entry_size; + qdf_dma_addr_t paddr; + struct hal_srng_params ring_params = {0}; + struct wlan_objmgr_psoc *psoc; + struct direct_buf_rx_psoc_obj *dbr_psoc_obj; + struct direct_buf_rx_ring_cap *dbr_ring_cap; + struct direct_buf_rx_ring_cfg *dbr_ring_cfg; + QDF_STATUS status; + + direct_buf_rx_enter(); + + psoc = wlan_pdev_get_psoc(pdev); + + if (!psoc) { + direct_buf_rx_err("psoc is null"); + return QDF_STATUS_E_FAILURE; + } + + dbr_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); + + if (dbr_psoc_obj == NULL) { + direct_buf_rx_err("dir buf rx psoc object is null"); + return QDF_STATUS_E_FAILURE; + } + + max_entries = hal_srng_max_entries(dbr_psoc_obj->hal_soc, + DIR_BUF_RX_DMA_SRC); + entry_size = hal_srng_get_entrysize(dbr_psoc_obj->hal_soc, + DIR_BUF_RX_DMA_SRC); + direct_buf_rx_info("Max Entries = %d", max_entries); + direct_buf_rx_info("Entry Size = %d", entry_size); + + status = populate_dbr_cap_mod_param(pdev, mod_param); + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("Module cap population failed"); + return QDF_STATUS_E_FAILURE; + } + + dbr_ring_cap = mod_param->dbr_ring_cap; + dbr_ring_cfg = mod_param->dbr_ring_cfg; + num_entries = dbr_ring_cap->ring_elems_min > max_entries ? + max_entries : dbr_ring_cap->ring_elems_min; + direct_buf_rx_info("Num entries = %d", num_entries); + dbr_ring_cfg->num_ptr = num_entries; + mod_param->dbr_buf_pool = qdf_mem_malloc(num_entries * sizeof( + struct direct_buf_rx_buf_info)); + if (!mod_param->dbr_buf_pool) { + direct_buf_rx_err("dir buf rx buf pool alloc failed"); + return QDF_STATUS_E_NOMEM; + } + + ring_alloc_size = (num_entries * entry_size) + DBR_RING_BASE_ALIGN - 1; + dbr_ring_cfg->ring_alloc_size = ring_alloc_size; + direct_buf_rx_info("dbr_psoc_obj %pK", dbr_psoc_obj); + dbr_ring_cfg->base_vaddr_unaligned = qdf_mem_alloc_consistent( + dbr_psoc_obj->osdev, dbr_psoc_obj->osdev->dev, ring_alloc_size, + &paddr); + direct_buf_rx_info("vaddr aligned allocated"); + dbr_ring_cfg->base_paddr_unaligned = paddr; + if (!dbr_ring_cfg->base_vaddr_unaligned) { + direct_buf_rx_err("dir buf rx vaddr alloc failed"); + qdf_mem_free(mod_param->dbr_buf_pool); + return QDF_STATUS_E_NOMEM; + } + + /* Alignment is defined to 8 for now. Will be advertised by FW */ + dbr_ring_cfg->base_vaddr_aligned = (void *)(uintptr_t)qdf_roundup( + (uint64_t)(uintptr_t)dbr_ring_cfg->base_vaddr_unaligned, + DBR_RING_BASE_ALIGN); + ring_params.ring_base_vaddr = dbr_ring_cfg->base_vaddr_aligned; + dbr_ring_cfg->base_paddr_aligned = qdf_roundup( + (uint64_t)dbr_ring_cfg->base_paddr_unaligned, + DBR_RING_BASE_ALIGN); + ring_params.ring_base_paddr = + (qdf_dma_addr_t)dbr_ring_cfg->base_paddr_aligned; + ring_params.num_entries = num_entries; + srng = hal_srng_setup(dbr_psoc_obj->hal_soc, DIR_BUF_RX_DMA_SRC, 0, + wlan_objmgr_pdev_get_pdev_id(pdev), &ring_params); + + if (!srng) { + direct_buf_rx_err("srng setup failed"); + qdf_mem_free(mod_param->dbr_buf_pool); + qdf_mem_free_consistent(dbr_psoc_obj->osdev, + dbr_psoc_obj->osdev->dev, + ring_alloc_size, + dbr_ring_cfg->base_vaddr_unaligned, + (qdf_dma_addr_t)dbr_ring_cfg->base_paddr_unaligned, 0); + return QDF_STATUS_E_FAILURE; + } + dbr_ring_cfg->srng = srng; + dbr_ring_cfg->tail_idx_addr = + hal_srng_get_tp_addr(dbr_psoc_obj->hal_soc, srng); + dbr_ring_cfg->head_idx_addr = + hal_srng_get_hp_addr(dbr_psoc_obj->hal_soc, srng); + dbr_ring_cfg->buf_size = dbr_ring_cap->min_buf_size; + + return target_if_dbr_fill_ring(pdev, mod_param); +} + +static QDF_STATUS target_if_dbr_init_srng(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_module_param *mod_param) +{ + QDF_STATUS status; + + direct_buf_rx_info("Init DBR srng"); + + if (!mod_param) { + direct_buf_rx_err("dir buf rx module param is null"); + return QDF_STATUS_E_INVAL; + } + + mod_param->dbr_ring_cap = qdf_mem_malloc(sizeof( + struct direct_buf_rx_ring_cap)); + + if (!mod_param->dbr_ring_cap) { + direct_buf_rx_err("Ring cap alloc failed"); + return QDF_STATUS_E_NOMEM; + } + + /* Allocate memory for DBR Ring Config */ + mod_param->dbr_ring_cfg = qdf_mem_malloc(sizeof( + struct direct_buf_rx_ring_cfg)); + + if (!mod_param->dbr_ring_cfg) { + direct_buf_rx_err("Ring config alloc failed"); + qdf_mem_free(mod_param->dbr_ring_cap); + return QDF_STATUS_E_NOMEM; + } + + status = target_if_dbr_init_ring(pdev, mod_param); + + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("DBR ring init failed"); + qdf_mem_free(mod_param->dbr_ring_cfg); + qdf_mem_free(mod_param->dbr_ring_cap); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS target_if_dbr_cfg_tgt(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_module_param *mod_param) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + void *wmi_hdl; + struct direct_buf_rx_cfg_req dbr_cfg_req = {0}; + struct direct_buf_rx_ring_cfg *dbr_ring_cfg; + struct direct_buf_rx_ring_cap *dbr_ring_cap; + + direct_buf_rx_enter(); + + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) { + direct_buf_rx_err("psoc is null"); + return QDF_STATUS_E_FAILURE; + } + + dbr_ring_cfg = mod_param->dbr_ring_cfg; + dbr_ring_cap = mod_param->dbr_ring_cap; + wmi_hdl = pdev->tgt_if_handle; + if (!wmi_hdl) { + direct_buf_rx_err("WMI handle null. Can't send WMI CMD"); + return QDF_STATUS_E_INVAL; + } + + direct_buf_rx_debug("Sending DBR Ring CFG to target"); + dbr_cfg_req.pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); + /* Module ID numbering starts from 1 in FW. need to fix it */ + dbr_cfg_req.mod_id = mod_param->mod_id; + dbr_cfg_req.base_paddr_lo = (uint64_t)dbr_ring_cfg->base_paddr_aligned + & 0xFFFFFFFF; + dbr_cfg_req.base_paddr_hi = (uint64_t)dbr_ring_cfg->base_paddr_aligned + & 0xFFFFFFFF00000000; + dbr_cfg_req.head_idx_paddr_lo = (uint64_t)dbr_ring_cfg->head_idx_addr + & 0xFFFFFFFF; + dbr_cfg_req.head_idx_paddr_hi = (uint64_t)dbr_ring_cfg->head_idx_addr + & 0xFFFFFFFF00000000; + dbr_cfg_req.tail_idx_paddr_lo = (uint64_t)dbr_ring_cfg->tail_idx_addr + & 0xFFFFFFFF; + dbr_cfg_req.tail_idx_paddr_hi = (uint64_t)dbr_ring_cfg->tail_idx_addr + & 0xFFFFFFFF00000000; + dbr_cfg_req.num_elems = dbr_ring_cap->ring_elems_min; + dbr_cfg_req.buf_size = dbr_ring_cap->min_buf_size; + dbr_cfg_req.num_resp_per_event = DBR_NUM_RESP_PER_EVENT; + dbr_cfg_req.event_timeout_ms = DBR_EVENT_TIMEOUT_IN_MS; + direct_buf_rx_info("pdev id %d mod id %d base addr lo %x\n" + "base addr hi %x head idx addr lo %x\n" + "head idx addr hi %x tail idx addr lo %x\n" + "tail idx addr hi %x num ptr %d\n" + "num resp %d event timeout %d\n", + dbr_cfg_req.pdev_id, dbr_cfg_req.mod_id, + dbr_cfg_req.base_paddr_lo, dbr_cfg_req.base_paddr_hi, + dbr_cfg_req.head_idx_paddr_lo, + dbr_cfg_req.head_idx_paddr_hi, + dbr_cfg_req.tail_idx_paddr_lo, + dbr_cfg_req.tail_idx_paddr_hi, + dbr_cfg_req.num_elems, + dbr_cfg_req.num_resp_per_event, + dbr_cfg_req.event_timeout_ms); + status = wmi_unified_dbr_ring_cfg(wmi_hdl, &dbr_cfg_req); + + return status; +} + +static QDF_STATUS target_if_init_dbr_ring(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_pdev_obj *dbr_pdev_obj, + enum DBR_MODULE mod_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct direct_buf_rx_module_param *mod_param; + + direct_buf_rx_info("Init DBR ring for module %d", mod_id); + + if (!dbr_pdev_obj) { + direct_buf_rx_err("dir buf rx object is null"); + return QDF_STATUS_E_INVAL; + } + + mod_param = &(dbr_pdev_obj->dbr_mod_param[mod_id]); + + if (!mod_param) { + direct_buf_rx_err("dir buf rx module param is null"); + return QDF_STATUS_E_FAILURE; + } + + direct_buf_rx_info("mod_param %pK", mod_param); + + mod_param->mod_id = mod_id+1; + + /* Initialize DMA ring now */ + status = target_if_dbr_init_srng(pdev, mod_param); + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("DBR ring init failed %d", status); + return status; + } + + /* Send CFG request command to firmware */ + status = target_if_dbr_cfg_tgt(pdev, mod_param); + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("DBR config to target failed %d", status); + goto dbr_srng_init_failed; + } + + return QDF_STATUS_SUCCESS; + +dbr_srng_init_failed: + target_if_deinit_dbr_ring(pdev, dbr_pdev_obj, mod_id); + return status; +} + +QDF_STATUS target_if_direct_buf_rx_module_register( + struct wlan_objmgr_pdev *pdev, uint8_t mod_id, + int (*dbr_rsp_handler)(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_data *dbr_data)) +{ + QDF_STATUS status; + struct direct_buf_rx_pdev_obj *dbr_pdev_obj; + + if (pdev == NULL) { + direct_buf_rx_err("pdev context passed is null"); + return QDF_STATUS_E_INVAL; + } + + if (dbr_rsp_handler == NULL) { + direct_buf_rx_err("Response handler is null"); + return QDF_STATUS_E_INVAL; + } + + if (mod_id >= DBR_MODULE_MAX) { + direct_buf_rx_err("Invalid module id"); + return QDF_STATUS_E_INVAL; + } + + dbr_pdev_obj = wlan_objmgr_pdev_get_comp_private_obj(pdev, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); + + if (dbr_pdev_obj == NULL) { + direct_buf_rx_err("dir buf rx object is null"); + return QDF_STATUS_E_FAILURE; + } + direct_buf_rx_info("Dbr pdev obj %pK", dbr_pdev_obj); + + dbr_pdev_obj->dbr_mod_param[mod_id].dbr_rsp_handler = + dbr_rsp_handler; + + status = target_if_init_dbr_ring(pdev, dbr_pdev_obj, + (enum DBR_MODULE)mod_id); + + return status; +} + +static void *target_if_dbr_vaddr_lookup( + struct direct_buf_rx_module_param *mod_param, + qdf_dma_addr_t paddr, uint32_t cookie) +{ + struct direct_buf_rx_buf_info *dbr_buf_pool; + + dbr_buf_pool = mod_param->dbr_buf_pool; + + if (dbr_buf_pool[cookie].paddr == paddr) { + return dbr_buf_pool[cookie].vaddr + + dbr_buf_pool[cookie].offset; + } + + direct_buf_rx_err("Incorrect paddr found on cookie slot"); + return NULL; +} + +static QDF_STATUS target_if_get_dbr_data(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_module_param *mod_param, + struct direct_buf_rx_rsp *dbr_rsp, + struct direct_buf_rx_data *dbr_data, + uint8_t idx, uint32_t *cookie) +{ + qdf_dma_addr_t paddr = 0; + uint32_t addr_hi; + struct direct_buf_rx_psoc_obj *dbr_psoc_obj; + struct direct_buf_rx_ring_cap *dbr_ring_cap; + struct wlan_objmgr_psoc *psoc; + + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) { + direct_buf_rx_err("psoc is null"); + return QDF_STATUS_E_FAILURE; + } + + dbr_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); + + if (dbr_psoc_obj == NULL) { + direct_buf_rx_err("dir buf rx psoc object is null"); + return QDF_STATUS_E_FAILURE; + } + + dbr_ring_cap = mod_param->dbr_ring_cap; + addr_hi = (uint64_t)WMI_HOST_DBR_DATA_ADDR_HI_GET( + dbr_rsp->dbr_entries[idx].paddr_hi); + paddr = (qdf_dma_addr_t)((uint64_t)addr_hi << 32 | + dbr_rsp->dbr_entries[idx].paddr_lo); + *cookie = WMI_HOST_DBR_DATA_ADDR_HI_HOST_DATA_GET( + dbr_rsp->dbr_entries[idx].paddr_hi); + direct_buf_rx_info("Cookie = %d", *cookie); + dbr_data->vaddr = target_if_dbr_vaddr_lookup(mod_param, paddr, *cookie); + direct_buf_rx_info("Vaddr look up = %x", dbr_data->vaddr); + dbr_data->dbr_len = dbr_rsp->dbr_entries[idx].len; + qdf_mem_unmap_nbytes_single(dbr_psoc_obj->osdev, (qdf_dma_addr_t)paddr, + QDF_DMA_FROM_DEVICE, + dbr_ring_cap->min_buf_size); + + return QDF_STATUS_SUCCESS; +} + +static int target_if_direct_buf_rx_rsp_event_handler(ol_scn_t scn, + uint8_t *data_buf, + uint32_t data_len) +{ + int ret; + uint8_t i = 0; + QDF_STATUS status; + uint32_t cookie = 0; + struct direct_buf_rx_rsp dbr_rsp = {0}; + struct direct_buf_rx_data dbr_data = {0}; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + struct direct_buf_rx_buf_info *dbr_buf_pool; + struct direct_buf_rx_pdev_obj *dbr_pdev_obj; + struct direct_buf_rx_module_param *mod_param; + + direct_buf_rx_enter(); + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + direct_buf_rx_err("psoc is null"); + return QDF_STATUS_E_FAILURE; + } + + if (wmi_extract_dbr_buf_release_fixed(GET_WMI_HDL_FROM_PSOC(psoc), + data_buf, &dbr_rsp) != QDF_STATUS_SUCCESS) { + direct_buf_rx_err("unable to extract DBR rsp fixed param"); + return QDF_STATUS_E_FAILURE; + } + + direct_buf_rx_info("Num buf release entry = %d", + dbr_rsp.num_buf_release_entry); + + pdev = wlan_objmgr_get_pdev_by_id(psoc, dbr_rsp.pdev_id, + WLAN_DIRECT_BUF_RX_ID); + if (!pdev) { + direct_buf_rx_err("pdev is null"); + return QDF_STATUS_E_INVAL; + } + + dbr_pdev_obj = wlan_objmgr_pdev_get_comp_private_obj(pdev, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); + + if (dbr_pdev_obj == NULL) { + direct_buf_rx_err("dir buf rx object is null"); + return QDF_STATUS_E_FAILURE; + } + + mod_param = &(dbr_pdev_obj->dbr_mod_param[dbr_rsp.mod_id-1]); + + if (!mod_param) { + direct_buf_rx_err("dir buf rx module param is null"); + return QDF_STATUS_E_FAILURE; + } + + dbr_buf_pool = mod_param->dbr_buf_pool; + dbr_rsp.dbr_entries = qdf_mem_malloc(dbr_rsp.num_buf_release_entry * + sizeof(struct direct_buf_rx_entry)); + + for (i = 0; i < dbr_rsp.num_buf_release_entry; i++) { + if (wmi_extract_dbr_buf_release_entry( + GET_WMI_HDL_FROM_PSOC(psoc), data_buf, i, + &dbr_rsp.dbr_entries[i]) != QDF_STATUS_SUCCESS) { + direct_buf_rx_err("Unable to extract DBR buf entry %d", + i+1); + return QDF_STATUS_E_FAILURE; + } + status = target_if_get_dbr_data(pdev, mod_param, &dbr_rsp, + &dbr_data, i, &cookie); + + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("DBR data get failed"); + return QDF_STATUS_E_FAILURE; + } + ret = mod_param->dbr_rsp_handler(pdev, &dbr_data); + status = target_if_dbr_replenish_ring(pdev, mod_param, + dbr_data.vaddr, cookie); + if (QDF_IS_STATUS_ERROR(status)) { + direct_buf_rx_err("dir buf rx ring replenish failed"); + return QDF_STATUS_E_FAILURE; + } + } + + return ret; +} + +static QDF_STATUS target_if_dbr_empty_ring(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_psoc_obj *dbr_psoc_obj, + struct direct_buf_rx_module_param *mod_param) +{ + uint8_t idx; + struct direct_buf_rx_ring_cfg *dbr_ring_cfg; + struct direct_buf_rx_ring_cap *dbr_ring_cap; + struct direct_buf_rx_buf_info *dbr_buf_pool; + + direct_buf_rx_enter(); + dbr_ring_cfg = mod_param->dbr_ring_cfg; + dbr_ring_cap = mod_param->dbr_ring_cap; + dbr_buf_pool = mod_param->dbr_buf_pool; + + direct_buf_rx_info("dbr_ring_cfg %pK, dbr_ring_cap %pK dbr_buf_pool %pK", + dbr_ring_cfg, dbr_ring_cap, dbr_buf_pool); + + for (idx = 0; idx < dbr_ring_cfg->num_ptr - 1; idx++) { + direct_buf_rx_info("dbr buf pool unmap and free for ptr %d", + idx); + qdf_mem_unmap_nbytes_single(dbr_psoc_obj->osdev, + (qdf_dma_addr_t)dbr_buf_pool[idx].paddr, + QDF_DMA_FROM_DEVICE, + dbr_ring_cap->min_buf_size); + qdf_mem_free(dbr_buf_pool[idx].vaddr); + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS target_if_dbr_deinit_ring(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_module_param *mod_param) +{ + struct wlan_objmgr_psoc *psoc; + struct direct_buf_rx_psoc_obj *dbr_psoc_obj; + struct direct_buf_rx_ring_cfg *dbr_ring_cfg; + + direct_buf_rx_enter(); + psoc = wlan_pdev_get_psoc(pdev); + if (!psoc) { + direct_buf_rx_err("psoc is null"); + return QDF_STATUS_E_FAILURE; + } + + dbr_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); + + if (dbr_psoc_obj == NULL) { + direct_buf_rx_err("dir buf rx psoc object is null"); + return QDF_STATUS_E_FAILURE; + } + direct_buf_rx_info("dbr_psoc_obj %pK", dbr_psoc_obj); + + dbr_ring_cfg = mod_param->dbr_ring_cfg; + target_if_dbr_empty_ring(pdev, dbr_psoc_obj, mod_param); + hal_srng_cleanup(dbr_psoc_obj->hal_soc, dbr_ring_cfg->srng); + qdf_mem_free_consistent(dbr_psoc_obj->osdev, dbr_psoc_obj->osdev->dev, + dbr_ring_cfg->ring_alloc_size, + dbr_ring_cfg->base_vaddr_unaligned, + (qdf_dma_addr_t)dbr_ring_cfg->base_paddr_unaligned, 0); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS target_if_dbr_deinit_srng( + struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_module_param *mod_param) +{ + struct direct_buf_rx_buf_info *dbr_buf_pool; + + direct_buf_rx_enter(); + dbr_buf_pool = mod_param->dbr_buf_pool; + direct_buf_rx_info("dbr buf pool %pK", dbr_buf_pool); + target_if_dbr_deinit_ring(pdev, mod_param); + qdf_mem_free(dbr_buf_pool); + dbr_buf_pool = NULL; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS target_if_deinit_dbr_ring(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_pdev_obj *dbr_pdev_obj, + enum DBR_MODULE mod_id) +{ + struct direct_buf_rx_module_param *mod_param; + struct direct_buf_rx_ring_cap *dbr_ring_cap; + QDF_STATUS status; + + direct_buf_rx_enter(); + mod_param = &(dbr_pdev_obj->dbr_mod_param[mod_id]); + + if (!mod_param) { + direct_buf_rx_err("dir buf rx module param is null"); + return QDF_STATUS_E_FAILURE; + } + direct_buf_rx_info("mod_param %pK", mod_param); + + dbr_ring_cap = mod_param->dbr_ring_cap; + direct_buf_rx_info("dbr_ring_cap %pK", dbr_ring_cap); + target_if_dbr_deinit_srng(pdev, mod_param); + qdf_mem_free(dbr_ring_cap); + dbr_ring_cap = NULL; + qdf_mem_free(mod_param); + mod_param = NULL; + + return status; +} + +QDF_STATUS target_if_direct_buf_rx_register_events( + struct wlan_objmgr_psoc *psoc) +{ + int ret; + + if (!psoc || !GET_WMI_HDL_FROM_PSOC(psoc)) { + direct_buf_rx_err("psoc or psoc->tgt_if_handle is null"); + return QDF_STATUS_E_INVAL; + } + + ret = wmi_unified_register_event_handler(GET_WMI_HDL_FROM_PSOC(psoc), + wmi_dma_buf_release_event_id, + target_if_direct_buf_rx_rsp_event_handler, + WMI_RX_UMAC_CTX); + + if (ret) { + direct_buf_rx_err("register event handler failed: err %d", ret); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS target_if_direct_buf_rx_unregister_events( + struct wlan_objmgr_psoc *psoc) +{ + if (!psoc || !GET_WMI_HDL_FROM_PSOC(psoc)) { + direct_buf_rx_err("psoc or psoc->tgt_if_handle is null"); + return QDF_STATUS_E_INVAL; + } + + wmi_unified_unregister_event_handler(GET_WMI_HDL_FROM_PSOC(psoc), + wmi_dma_buf_release_event_id); + + return QDF_STATUS_SUCCESS; +} diff --git a/target_if/direct_buf_rx/src/target_if_direct_buf_rx_main.h b/target_if/direct_buf_rx/src/target_if_direct_buf_rx_main.h new file mode 100644 index 000000000000..385f2b76070c --- /dev/null +++ b/target_if/direct_buf_rx/src/target_if_direct_buf_rx_main.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef _TARGET_IF_DIRECT_BUF_RX_MAIN_H_ +#define _TARGET_IF_DIRECT_BUF_RX_MAIN_H_ + +#include "qdf_types.h" +#include "qdf_status.h" + +struct wlan_objmgr_psoc; +struct wlan_lmac_if_tx_ops; +struct direct_buf_rx_data; + +#define DBR_RING_BASE_ALIGN 8 +#define DBR_EVENT_TIMEOUT_IN_MS 1 +#define DBR_NUM_RESP_PER_EVENT 1 + +/** + * enum DBR_MODULE - Enum containing the modules supporting direct buf rx + * @DBR_MODULE_SPECTRAL: Module ID for Spectral + * @DBR_MODULE_MAX: Max module ID + */ +enum DBR_MODULE { + DBR_MODULE_SPECTRAL = 1, + DBR_MODULE_MAX, +}; + +/** + * struct direct_buf_rx_info - direct buffer rx operation info struct + * @cookie: SW cookie used to get the virtual address + * @paddr: Physical address pointer for DMA operation + * @vaddr: Virtual address pointer + * @offset: Offset of aligned address from unaligned + */ +struct direct_buf_rx_buf_info { + uint32_t cookie; + qdf_dma_addr_t paddr; + void *vaddr; + uint8_t offset; +}; + +/** + * struct direct_buf_rx_ring_cfg - DMA ring config parameters + * @num_ptr: Depth or the number of physical address pointers in the ring + * @ring_alloc_size: Size of the HAL ring + * @base_paddr_unaligned: base physical addr unaligned + * @base_vaddr_unaligned: base virtual addr unaligned + * @base_paddr_aligned: base physical addr aligned + * @base_vaddr_aligned: base virtual addr unaligned + * @head_idx_addr: head index addr + * @tail_idx_addr: tail index addr + * @srng: HAL srng context + */ +struct direct_buf_rx_ring_cfg { + uint32_t num_ptr; + uint32_t ring_alloc_size; + qdf_dma_addr_t base_paddr_unaligned; + void *base_vaddr_unaligned; + qdf_dma_addr_t base_paddr_aligned; + void *base_vaddr_aligned; + qdf_dma_addr_t head_idx_addr; + qdf_dma_addr_t tail_idx_addr; + void *srng; + uint32_t buf_size; +}; + +/** + * struct direct_buf_rx_ring_cap - DMA ring capabilities + * @ring_elems_min: Minimum number of pointers in the ring + * @min_buf_size: Minimum size of each buffer entry in the ring + * @min_buf_align: Minimum alignment of the addresses in the ring + */ +struct direct_buf_rx_ring_cap { + uint32_t ring_elems_min; + uint32_t min_buf_size; + uint32_t min_buf_align; +}; + +/** + * struct direct_buf_rx_module_param - DMA module param + * @mod_id: Module ID + * @dbr_ring_cap: Pointer to direct buf rx ring capabilities struct + * @dbr_ring_cfg: Pointer to direct buf rx ring config struct + * @dbr_buf_pool: Pointer to direct buf rx buffer pool struct + * @dbr_rsp_handler: Pointer to direct buf rx response handler for the module + */ +struct direct_buf_rx_module_param { + enum DBR_MODULE mod_id; + struct direct_buf_rx_ring_cap *dbr_ring_cap; + struct direct_buf_rx_ring_cfg *dbr_ring_cfg; + struct direct_buf_rx_buf_info *dbr_buf_pool; + int (*dbr_rsp_handler)(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_data *dbr_data); +}; + +/** + * struct direct_buf_rx_pdev_obj - Direct Buf RX pdev object struct + * @num_modules: Number of modules registered to DBR for the pdev + * @dbr_mod_param: Pointer to direct buf rx module param struct + */ +struct direct_buf_rx_pdev_obj { + uint32_t num_modules; + struct direct_buf_rx_module_param *dbr_mod_param; +}; + +/** + * struct direct_buf_rx_psoc_obj - Direct Buf RX psoc object struct + * @hal_soc: Opaque HAL SOC handle + * @osdev: QDF os device handle + */ +struct direct_buf_rx_psoc_obj { + void *hal_soc; + qdf_device_t osdev; +}; + +/** + * target_if_direct_buf_rx_register_events: Register WMI events to direct + * buffer rx module + * @psoc: pointer to psoc object + * + * Return : QDF status of operation + */ +QDF_STATUS target_if_direct_buf_rx_register_events( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_direct_buf_rx_unregister_events: Unregister WMI events to direct + * buffer rx module + * @psoc: pointer to psoc object + * + * Return : QDF status of operation + */ +QDF_STATUS target_if_direct_buf_rx_unregister_events( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_direct_buf_rx_pdev_create_handler: Handler to be invoked for + * direct buffer rx module during + * pdev object create + * @pdev: pointer to pdev object + * @data: pointer to data + * + * Return : QDF status of operation + */ +QDF_STATUS target_if_direct_buf_rx_pdev_create_handler( + struct wlan_objmgr_pdev *pdev, void *data); + +/** + * target_if_direct_buf_rx_pdev_destroy_handler: Handler to be invoked for + * direct buffer rx module during + * pdev object destroy + * @pdev: pointer to pdev object + * @data: pointer to data + * + * Return : QDF status of operation + */ +QDF_STATUS target_if_direct_buf_rx_pdev_destroy_handler( + struct wlan_objmgr_pdev *pdev, void *data); + +/** + * target_if_direct_buf_rx_psoc_obj_create: Handler invoked for + * direct buffer rx module during + * attach + * @pdev: pointer to psoc object + * + * Return : QDF status of operation + */ +QDF_STATUS target_if_direct_buf_rx_psoc_obj_create( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_direct_buf_rx_psoc_obj_destroy: Handler invoked for + * direct buffer rx module during + * detach + * @pdev: pointer to psoc object + * + * Return : QDF status of operation + */ +QDF_STATUS target_if_direct_buf_rx_psoc_obj_destroy( + struct wlan_objmgr_psoc *psoc); + +/** + * target_if_deinit_dbr_ring: Function to deinitialize buffers and ring + * allocated for direct buffer rx module + * @pdev: pointer to pdev object + * @dbr_pdev_obj: pointer to direct buffer rx module pdev obj + * @mod_id: module id indicating the module using direct buffer rx framework + * + * Return : QDF status of operation + */ +QDF_STATUS target_if_deinit_dbr_ring(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_pdev_obj *dbr_pdev_obj, + enum DBR_MODULE mod_id); +/** + * target_if_direct_buf_rx_module_register: Function to register to direct + * buffer rx module + * @pdev: pointer to pdev object + * @mod_id: module id indicating the module using direct buffer rx framework + * @dbr_rsp_handler: function pointer pointing to the response handler to be + * invoked for the module registering to direct buffer rx + * module + * + * Return: QDF status of operation + */ +QDF_STATUS target_if_direct_buf_rx_module_register( + struct wlan_objmgr_pdev *pdev, uint8_t mod_id, + int (*dbr_rsp_handler)(struct wlan_objmgr_pdev *pdev, + struct direct_buf_rx_data *dbr_data)); + +#endif /* _TARGET_IF_DIRECT_BUF_RX_MAIN_H_ */