/* * Copyright (c) 2017-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. */ #include "target_if.h" #include "wlan_lmac_if_def.h" #include "target_if_direct_buf_rx_main.h" #include #include "hal_api.h" #include #include /** * struct module_name : Module name information structure * @module_name_str : Module name subscribing to DBR */ struct module_name { unsigned char module_name_str[QDF_MAX_NAME_SIZE]; }; static const struct module_name g_dbr_module_name[DBR_MODULE_MAX] = { [DBR_MODULE_SPECTRAL] = {"SPECTRAL"}, }; static uint8_t get_num_dbr_modules_per_pdev(struct wlan_objmgr_pdev *pdev) { struct wlan_objmgr_psoc *psoc; struct wlan_psoc_host_dbr_ring_caps *dbr_ring_cap; uint8_t num_dbr_ring_caps, cap_idx, pdev_id, num_modules; struct target_psoc_info *tgt_psoc_info; struct wlan_psoc_host_service_ext_param *ext_svc_param; psoc = wlan_pdev_get_psoc(pdev); if (psoc == NULL) { direct_buf_rx_err("psoc is null"); return 0; } tgt_psoc_info = wlan_psoc_get_tgt_if_handle(psoc); if (tgt_psoc_info == NULL) { direct_buf_rx_err("target_psoc_info is null"); return 0; } ext_svc_param = target_psoc_get_service_ext_param(tgt_psoc_info); num_dbr_ring_caps = ext_svc_param->num_dbr_ring_caps; dbr_ring_cap = target_psoc_get_dbr_ring_caps(tgt_psoc_info); 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_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; struct target_psoc_info *tgt_psoc_info; struct wlan_psoc_host_service_ext_param *ext_svc_param; psoc = wlan_pdev_get_psoc(pdev); if (psoc == NULL) { direct_buf_rx_err("psoc is null"); return QDF_STATUS_E_INVAL; } tgt_psoc_info = wlan_psoc_get_tgt_if_handle(psoc); if (tgt_psoc_info == NULL) { direct_buf_rx_err("target_psoc_info is null"); return QDF_STATUS_E_INVAL; } ext_svc_param = target_psoc_get_service_ext_param(tgt_psoc_info); num_dbr_ring_caps = ext_svc_param->num_dbr_ring_caps; dbr_ring_cap = target_psoc_get_dbr_ring_caps(tgt_psoc_info); 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); 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); qdf_mem_free(dbr_pdev_obj); return status; } 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; if (!dbr_pdev_obj->num_modules) { direct_buf_rx_info("Number of modules = %d", num_modules); return QDF_STATUS_SUCCESS; } 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"); wlan_objmgr_pdev_component_obj_detach(pdev, WLAN_TARGET_IF_COMP_DIRECT_BUF_RX, dbr_pdev_obj); qdf_mem_free(dbr_pdev_obj); return QDF_STATUS_E_NOMEM; } return QDF_STATUS_SUCCESS; } 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); qdf_mem_free(dbr_pdev_obj->dbr_mod_param); dbr_pdev_obj->dbr_mod_param = NULL; 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_create_handler( struct wlan_objmgr_psoc *psoc, void *data) { 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_destroy_handler( struct wlan_objmgr_psoc *psoc, void *data) { 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, uint32_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; 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", map_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) { uint32_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; } if (dbr_psoc_obj->hal_soc == NULL || dbr_psoc_obj->osdev == NULL) { direct_buf_rx_err("dir buf rx target attach failed"); 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 = lmac_get_pdev_wmi_handle(pdev); 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; /* 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); if (!dbr_pdev_obj->dbr_mod_param) { direct_buf_rx_err("dbr_pdev_obj->dbr_mod_param is NULL"); return QDF_STATUS_E_FAILURE; } if (mod_id >= dbr_pdev_obj->num_modules) { direct_buf_rx_err("Module %d not supported in target", mod_id); return QDF_STATUS_E_FAILURE; } 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 = 0; 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"); wlan_objmgr_pdev_release_ref(pdev, WLAN_DIRECT_BUF_RX_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"); wlan_objmgr_pdev_release_ref(pdev, WLAN_DIRECT_BUF_RX_ID); return QDF_STATUS_E_FAILURE; } mod_param = &(dbr_pdev_obj->dbr_mod_param[dbr_rsp.mod_id]); if (!mod_param) { direct_buf_rx_err("dir buf rx module param is null"); wlan_objmgr_pdev_release_ref(pdev, WLAN_DIRECT_BUF_RX_ID); 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)); if (dbr_rsp.num_meta_data_entry > dbr_rsp.num_buf_release_entry) { direct_buf_rx_err("More than expected number of metadata"); wlan_objmgr_pdev_release_ref(pdev, WLAN_DIRECT_BUF_RX_ID); return QDF_STATUS_E_FAILURE; } 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); qdf_mem_free(dbr_rsp.dbr_entries); wlan_objmgr_pdev_release_ref(pdev, WLAN_DIRECT_BUF_RX_ID); 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"); qdf_mem_free(dbr_rsp.dbr_entries); wlan_objmgr_pdev_release_ref(pdev, WLAN_DIRECT_BUF_RX_ID); return QDF_STATUS_E_FAILURE; } dbr_data.meta_data_valid = false; if (i < dbr_rsp.num_meta_data_entry) { if (wmi_extract_dbr_buf_metadata( GET_WMI_HDL_FROM_PSOC(psoc), data_buf, i, &dbr_data.meta_data) == QDF_STATUS_SUCCESS) dbr_data.meta_data_valid = true; } 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"); qdf_mem_free(dbr_rsp.dbr_entries); wlan_objmgr_pdev_release_ref(pdev, WLAN_DIRECT_BUF_RX_ID); return QDF_STATUS_E_FAILURE; } } qdf_mem_free(dbr_rsp.dbr_entries); wlan_objmgr_pdev_release_ref(pdev, WLAN_DIRECT_BUF_RX_ID); 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) { uint32_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; if (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); mod_param->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; 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); direct_buf_rx_info("dbr_ring_cap %pK", mod_param->dbr_ring_cap); target_if_dbr_deinit_srng(pdev, mod_param); qdf_mem_free(mod_param->dbr_ring_cap); mod_param->dbr_ring_cap = NULL; qdf_mem_free(mod_param->dbr_ring_cfg); mod_param->dbr_ring_cfg = NULL; return QDF_STATUS_SUCCESS; } 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_unified_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_info("event handler not supported", ret); 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_unified_hdl_from_psoc(psoc), wmi_dma_buf_release_event_id); return QDF_STATUS_SUCCESS; } QDF_STATUS target_if_direct_buf_rx_print_ring_stat( struct wlan_objmgr_pdev *pdev) { struct direct_buf_rx_psoc_obj *dbr_psoc_obj; struct direct_buf_rx_pdev_obj *dbr_pdev_obj; struct wlan_objmgr_psoc *psoc; void *srng, *hal_soc; uint32_t hp = 0, tp = 0; struct direct_buf_rx_module_param *mod_param; struct direct_buf_rx_ring_cfg *dbr_ring_cfg; uint8_t num_modules, mod_idx; if (!pdev) { direct_buf_rx_err("pdev is null"); return QDF_STATUS_E_INVAL; } psoc = wlan_pdev_get_psoc(pdev); dbr_pdev_obj = wlan_objmgr_pdev_get_comp_private_obj(pdev, WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); dbr_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_TARGET_IF_COMP_DIRECT_BUF_RX); hal_soc = dbr_psoc_obj->hal_soc; num_modules = dbr_pdev_obj->num_modules; direct_buf_rx_err("--------------------------------------------------"); direct_buf_rx_err("| Module ID | Module | Head Idx | Tail Idx |"); direct_buf_rx_err("--------------------------------------------------"); for (mod_idx = 0; mod_idx < num_modules; mod_idx++) { mod_param = &dbr_pdev_obj->dbr_mod_param[mod_idx]; dbr_ring_cfg = mod_param->dbr_ring_cfg; srng = dbr_ring_cfg->srng; hal_api_get_tphp(hal_soc, srng, &tp, &hp); direct_buf_rx_err("|%11d|%14s|%10x|%10x|", mod_idx, g_dbr_module_name[mod_idx].module_name_str, hp, tp); } direct_buf_rx_err("--------------------------------------------------"); return QDF_STATUS_SUCCESS; }