/****************************************************************************** * * Copyright(c) 2019 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * *****************************************************************************/ #include "hal_headers.h" /** * _dump_csi_buf_status * Dump all of the csi buffer status; * @csi_obj: (struct hal_csi_obj *) **/ void _dump_csi_buf_status(struct hal_csi_obj *csi_obj) { struct hal_csi_buf *csi_buf = NULL; u8 i = 0; PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "===> DUMP HAL CSI Buffer Status\n"); for (i = 0; i < csi_obj->max_csi_buf_nr; i++) { csi_buf = &csi_obj->csi_buf[i]; PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[CSI BUF][%d] status 0x%x \n", csi_buf->idx, csi_buf->sub_idx); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[CSI BUF] 20UU (%d) \n", IS_SUB20_BUSY(csi_buf, 3)); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[CSI BUF] 20UL (%d) \n", IS_SUB20_BUSY(csi_buf, 2)); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[CSI BUF] 20LU (%d) \n", IS_SUB20_BUSY(csi_buf, 1)); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[CSI BUF] 20LL (%d) \n", IS_SUB20_BUSY(csi_buf, 0)); } PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "<=== DUMP HAL CSI Buffer Status\n"); } /** * __query_avl_buf_idx_20 * Get available sub 20MHz csi buffer * input : * @csi_buf: (struct hal_csi_buf *) 80MHz hal csi buffer for checking avaible * return * @sub_idx: (u8) csi buffer sub index **/ u8 __query_avl_buf_idx_20(struct hal_csi_buf *csi_buf) { u8 sub_idx = CSI_BUF_SUB_IDX_NON; do { if (IS_SUB20_BUSY(csi_buf, 0) == CSI_BUF_STS_IDLE) { SET_SUB20_BUSY(csi_buf, 0); sub_idx = CSI_BUF_SUB_IDX_20_LL; break; } if (IS_SUB20_BUSY(csi_buf, 1) == CSI_BUF_STS_IDLE) { SET_SUB20_BUSY(csi_buf, 1); sub_idx = CSI_BUF_SUB_IDX_20_LU; break; } if (IS_SUB20_BUSY(csi_buf, 2) == CSI_BUF_STS_IDLE) { SET_SUB20_BUSY(csi_buf, 2); sub_idx = CSI_BUF_SUB_IDX_20_UL; break; } if (IS_SUB20_BUSY(csi_buf, 3) == CSI_BUF_STS_IDLE) { SET_SUB20_BUSY(csi_buf, 3); sub_idx = CSI_BUF_SUB_IDX_20_UU; break; } } while (0); return sub_idx; } /** * __query_avl_buf_idx_40 * Get available sub 40MHz csi buffer * input : * @csi_buf: (struct hal_csi_buf *) 80MHz hal csi buffer for checking avaible * return * @sub_idx: (u8) csi buffer sub index **/ u8 __query_avl_buf_idx_40(struct hal_csi_buf *csi_buf) { u8 sub_idx = CSI_BUF_SUB_IDX_NON; do { if(IS_40L_BUSY(csi_buf) == CSI_BUF_STS_IDLE) { SET_40L_BUSY(csi_buf); sub_idx = CSI_BUF_SUB_IDX_40_L; break; } if (IS_40U_BUSY(csi_buf) == CSI_BUF_STS_IDLE) { SET_40U_BUSY(csi_buf); sub_idx = CSI_BUF_SUB_IDX_40_U; break; } } while (0); return sub_idx; } /** * __query_avl_buf_idx_80 * Get available sub 80MHz csi buffer * input : * @csi_buf: (struct hal_csi_buf *) 80MHz hal csi buffer for checking avaible * return * @sub_idx: (u8) csi buffer sub index **/ u8 __query_avl_buf_idx_80(struct hal_csi_buf *csi_buf) { u8 sub_idx = CSI_BUF_SUB_IDX_NON; if (csi_buf->sub_idx == CSI_BUF_STS_IDLE) { sub_idx = CSI_BUF_SUB_IDX_80; csi_buf->sub_idx = 0xF; } return sub_idx; } /** * _query_csi_buf_su * Get available sub 40MHz csi buffer * input : * @hal_info: (struct hal_info_t *) * @size:enum hal_csi_buf_size 20/40/80 MHz * return * @sub_id: (u8) available csi buffer sub index * @csi_buf: (struct hal_csi_buf *) original hal csi buffer **/ struct hal_csi_buf *_query_csi_buf_su( struct hal_info_t *hal_info, enum hal_csi_buf_size size, u8 *sub_id) { struct rtw_hal_com_t *hal_com = hal_info->hal_com; struct hal_csi_obj *csi_obj = (struct hal_csi_obj *)hal_com->csi_obj; struct hal_csi_buf *csi_buf = NULL; u8 i = 0; u8 e_idx_su = csi_obj->max_csi_buf_nr_su; *sub_id = CSI_BUF_SUB_IDX_NON; switch (size) { case HAL_CSI_BUF_SIZE_20: for (i = 0; i < e_idx_su; i++) { *sub_id = __query_avl_buf_idx_20(&csi_obj->csi_buf[i]); if(*sub_id != CSI_BUF_SUB_IDX_NON) { csi_buf = &csi_obj->csi_buf[i]; break; } } break; case HAL_CSI_BUF_SIZE_40: for (i = 0; i < e_idx_su; i++) { *sub_id = __query_avl_buf_idx_40(&csi_obj->csi_buf[i]); if(*sub_id != CSI_BUF_SUB_IDX_NON) { csi_buf = &csi_obj->csi_buf[i]; break; } } break; case HAL_CSI_BUF_SIZE_80: for (i = 0; i < e_idx_su; i++) { *sub_id = __query_avl_buf_idx_80(&csi_obj->csi_buf[i]); if(*sub_id != CSI_BUF_SUB_IDX_NON) { csi_buf = &csi_obj->csi_buf[i]; break; } } break; default: break; } if (*sub_id == CSI_BUF_SUB_IDX_NON) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "warring :_query_csi_buf_su fail !!!\n"); } return csi_buf; } /** * _query_csi_buf_mu * Get available sub 40MHz csi buffer * input : * @hal_info: (struct hal_info_t *) * @size:enum hal_csi_buf_size 20/40/80 MHz * return * @sub_id: (u8) available csi buffer sub index * @csi_buf: (struct hal_csi_buf *) original hal csi buffer **/ struct hal_csi_buf *_query_csi_buf_mu( struct hal_info_t *hal_info, enum hal_csi_buf_size size, u8 *sub_id) { struct rtw_hal_com_t *hal_com = hal_info->hal_com; struct hal_csi_obj *csi_obj = (struct hal_csi_obj *)hal_com->csi_obj; struct hal_csi_buf *csi_buf = NULL; u8 i = 0; u8 s_idx_mu = csi_obj->max_csi_buf_nr_su; u8 e_idx_mu = csi_obj->max_csi_buf_nr; *sub_id = CSI_BUF_SUB_IDX_NON; switch (size) { case HAL_CSI_BUF_SIZE_20: for (i = s_idx_mu; i < e_idx_mu; i++) { *sub_id = __query_avl_buf_idx_20(&csi_obj->csi_buf[i]); if(*sub_id != CSI_BUF_SUB_IDX_NON) { csi_buf = &csi_obj->csi_buf[i]; break; } } break; case HAL_CSI_BUF_SIZE_40: for (i = s_idx_mu; i < e_idx_mu; i++) { *sub_id = __query_avl_buf_idx_40(&csi_obj->csi_buf[i]); if(*sub_id != CSI_BUF_SUB_IDX_NON) { csi_buf = &csi_obj->csi_buf[i]; break; } } break; case HAL_CSI_BUF_SIZE_80: for (i = s_idx_mu; i < e_idx_mu; i++) { *sub_id = __query_avl_buf_idx_80(&csi_obj->csi_buf[i]); if(*sub_id != CSI_BUF_SUB_IDX_NON) { csi_buf = &csi_obj->csi_buf[i]; break; } } break; default: break; } if (*sub_id == CSI_BUF_SUB_IDX_NON) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "warring :_query_csi_buf_mu fail !!!\n"); } return csi_buf; } /** * _bw2csi * phl channel width to hal csi buffer size * input : * @bw: (struct hal_info_t *) * return * @ret: enum hal_csi_buf_size **/ enum hal_csi_buf_size _bw2csi(enum channel_width bw) { enum hal_csi_buf_size ret = HAL_CSI_BUF_SIZE_NONE; switch (bw){ case CHANNEL_WIDTH_20: ret = HAL_CSI_BUF_SIZE_20; break; case CHANNEL_WIDTH_40: ret = HAL_CSI_BUF_SIZE_40; break; case CHANNEL_WIDTH_80: ret = HAL_CSI_BUF_SIZE_80; break; default: break; } return ret; } enum rtw_hal_status _hal_csi_init_buf(struct hal_info_t *hal_info, u8 num) { enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS; void *drv_priv = hal_to_drvpriv(hal_info); struct rtw_hal_com_t *hal_com = hal_info->hal_com; struct hal_csi_obj *csi_obj = hal_com->csi_obj; struct hal_csi_buf *csi_buf = NULL; u8 idx = 0; do { csi_obj->csi_buf = _os_mem_alloc(drv_priv, sizeof(*csi_buf) * num); _os_mem_set(drv_priv, csi_obj->csi_buf, 0, sizeof(*csi_buf) * num); if (NULL == csi_obj->csi_buf) { status = RTW_HAL_STATUS_RESOURCE; break; } csi_buf = csi_obj->csi_buf; for (idx = 0; idx < num ; idx++) { csi_buf[idx].idx = idx; csi_buf[idx].sub_idx = 0; } } while (0); return status; } /** * hal_csi_init * hal csi module initialize * input : * @hal_info: struct hal_info_t * @su_buf_nr: Number of HW SU CSI Buffer for HAL * @mu_buf_nr: Number of HW MU CSI Buffer for HAL **/ enum rtw_hal_status hal_csi_init(struct hal_info_t *hal_info, u8 su_buf_nr, u8 mu_buf_nr) { enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS; struct rtw_hal_com_t *hal_com = hal_info->hal_com; struct hal_csi_obj *csi_obj = NULL; void *drv_priv = hal_to_drvpriv(hal_info); u8 idx = 0; FUNCIN(); do { csi_obj = _os_mem_alloc(drv_priv, sizeof(*csi_obj)); if (csi_obj == NULL) { status = RTW_HAL_STATUS_RESOURCE; break; } hal_com->csi_obj = csi_obj; _os_spinlock_init(drv_priv, &csi_obj->csi_lock); if ((su_buf_nr + mu_buf_nr) == 0) { PHL_ERR("hal_csi_init fail : csi buffer number = 0\n"); status = RTW_HAL_STATUS_RESOURCE; break; } _hal_csi_init_buf(hal_info, mu_buf_nr + su_buf_nr); csi_obj->max_csi_buf_nr_mu = mu_buf_nr; csi_obj->max_csi_buf_nr_su = su_buf_nr; csi_obj->max_csi_buf_nr = su_buf_nr + mu_buf_nr; for (idx = su_buf_nr; idx < csi_obj->max_csi_buf_nr; idx++){ csi_obj->csi_buf[idx].type = HAL_CSI_BUF_TYPE_MU; } } while (0); if (RTW_HAL_STATUS_SUCCESS != status) { hal_csi_deinit(hal_info); } FUNCOUT(); return status; } /** * hal_csi_deinit * hal csi module deinitialize * input : * @hal_info: struct hal_info_t **/ void hal_csi_deinit(struct hal_info_t *hal_info) { struct rtw_hal_com_t *hal_com = hal_info->hal_com; struct hal_csi_obj *csi_obj = (struct hal_csi_obj *)hal_com->csi_obj; void *drv_priv = hal_to_drvpriv(hal_info); struct hal_csi_buf *csi_buf = csi_obj->csi_buf; if (csi_obj != NULL) { if (csi_buf != NULL) { _os_mem_free(hal_to_drvpriv(hal_info), csi_buf, sizeof(struct hal_csi_buf) * csi_obj->max_csi_buf_nr); csi_obj->csi_buf = NULL; } _os_spinlock_free(drv_priv, &csi_obj->csi_lock); /* bf obj need free as last */ _os_mem_free(hal_to_drvpriv(hal_info), csi_obj, sizeof(struct hal_csi_obj)); hal_com->csi_obj = NULL; } } /** * hal_csi_query_idle_csi_buf * Get available CSI buffer * input : * @hal_info: struct hal_info_t * @mu: Request CSI Type : 1 = MU / 0 = SU * @bw: Request CSI BW : enum channel_width (20/40/80MHz) * @buf: (struct hal_csi_buf *)input/return : memory for CSI buffer index+subindex * (the call shall provied 1byte memory to save csi index) **/ enum rtw_hal_status hal_csi_query_idle_csi_buf( struct hal_info_t *hal_info, u8 mu, enum channel_width bw, void *buf) { enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE; struct rtw_hal_com_t *hal_com = hal_info->hal_com; struct hal_csi_obj *csi_obj = (struct hal_csi_obj *)hal_com->csi_obj; void *drv_priv = hal_to_drvpriv(hal_info); struct hal_csi_buf *csi_buf = (struct hal_csi_buf *)buf; struct hal_csi_buf *tmp_csi_buf = NULL; u8 sub_idx = CSI_BUF_SUB_IDX_NON; enum hal_csi_buf_size size = HAL_CSI_BUF_SIZE_NONE; enum hal_csi_buf_type type = HAL_CSI_BUF_TYPE_SU; FUNCIN(); if (csi_buf == NULL) { return status; } size = _bw2csi(bw); type = mu ? HAL_CSI_BUF_TYPE_MU : HAL_CSI_BUF_TYPE_SU; PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "size = 0x%x , type = 0x%x \n", size, type); switch (type) { case HAL_CSI_BUF_TYPE_SU: { _os_spinlock(drv_priv, &csi_obj->csi_lock, _ps, NULL); tmp_csi_buf = _query_csi_buf_su(hal_info, size, &sub_idx); _os_spinunlock(drv_priv, &csi_obj->csi_lock, _ps, NULL); } break; case HAL_CSI_BUF_TYPE_MU: { _os_spinlock(drv_priv, &csi_obj->csi_lock, _ps, NULL); tmp_csi_buf = _query_csi_buf_mu(hal_info, size, &sub_idx); _os_spinunlock(drv_priv, &csi_obj->csi_lock, _ps, NULL); } break; default: break; } if (tmp_csi_buf == NULL || sub_idx == CSI_BUF_SUB_IDX_NON) { status = RTW_HAL_STATUS_RESOURCE; PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "hal_csi_query_idle_csi_buf Fail\n"); } else { csi_buf->idx = tmp_csi_buf->idx; csi_buf->sub_idx = sub_idx; csi_buf->type = type; status = RTW_HAL_STATUS_SUCCESS; } FUNCOUT(); return status; } /** * hal_csi_release_csi_buf * Release the CSI buffer * input : * @hal_info: struct hal_info_t * @buf: (struct hal_csi_buf *)input/return : CSI buffer index to release **/ enum rtw_hal_status hal_csi_release_csi_buf( struct hal_info_t *hal_info, void *buf) { struct rtw_hal_com_t *hal_com = hal_info->hal_com; struct hal_csi_obj *csi_obj = (struct hal_csi_obj *)hal_com->csi_obj; void *drv_priv = hal_to_drvpriv(hal_info); enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE; struct hal_csi_buf *tmp_csi_buf = NULL; struct hal_csi_buf *csi_buf = (struct hal_csi_buf *)buf; if (csi_buf == NULL) { return status; } if (csi_buf->idx < csi_obj->max_csi_buf_nr) { tmp_csi_buf = &csi_obj->csi_buf[csi_buf->idx]; _os_spinlock(drv_priv, &csi_obj->csi_lock, _ps, NULL); switch (csi_buf->sub_idx) { case CSI_BUF_SUB_IDX_80: tmp_csi_buf->sub_idx = 0; break; case CSI_BUF_SUB_IDX_40_U: CLEAR_CSI_STS_BIT(tmp_csi_buf, 3); CLEAR_CSI_STS_BIT(tmp_csi_buf, 2); break; case CSI_BUF_SUB_IDX_40_L: CLEAR_CSI_STS_BIT(tmp_csi_buf, 1); CLEAR_CSI_STS_BIT(tmp_csi_buf, 0); break; case CSI_BUF_SUB_IDX_20_UU: CLEAR_CSI_STS_BIT(tmp_csi_buf, 3); break; case CSI_BUF_SUB_IDX_20_UL: CLEAR_CSI_STS_BIT(tmp_csi_buf, 2); break; case CSI_BUF_SUB_IDX_20_LU: CLEAR_CSI_STS_BIT(tmp_csi_buf, 1); break; case CSI_BUF_SUB_IDX_20_LL: CLEAR_CSI_STS_BIT(tmp_csi_buf, 0); break; default: break; } csi_buf->idx = 0; csi_buf->sub_idx = CSI_BUF_SUB_IDX_NON; _os_spinunlock(drv_priv, &csi_obj->csi_lock, _ps, NULL); status = RTW_HAL_STATUS_SUCCESS; } return status; } /** * hal_is_csi_buf_valid : check the csi buffer index is valid or not. * @hal_info: struct hal_info_t * @buf: (struct hal_csi_buf *)input/return : CSI buffer index to release **/ u8 hal_is_csi_buf_valid(struct hal_info_t *hal_info, void *buf) { struct hal_csi_buf *csi_buf = (struct hal_csi_buf *)buf; struct rtw_hal_com_t *hal_com = hal_info->hal_com; struct hal_csi_obj *csi_obj = (struct hal_csi_obj *)hal_com->csi_obj; u8 ret = _SUCCESS; do { if(csi_buf->idx >= csi_obj->max_csi_buf_nr){ ret = _FAIL; break; } if(csi_buf->sub_idx == CSI_BUF_SUB_IDX_NON) { ret = _FAIL; break; } } while(0); return ret; } /** * rtw_hal_get_csi_buf_bw : Get the csi buf bandwidth; * @buf: (struct hal_csi_buf *) * return * @ret: enum channel_width **/ enum channel_width rtw_hal_get_csi_buf_bw(void *buf) { struct hal_csi_buf *csi_buf = (struct hal_csi_buf *)buf; enum channel_width bw = CHANNEL_WIDTH_20; switch (csi_buf->sub_idx) { case CSI_BUF_SUB_IDX_80: bw = CHANNEL_WIDTH_80; break; case CSI_BUF_SUB_IDX_40_U: case CSI_BUF_SUB_IDX_40_L: bw = CHANNEL_WIDTH_40; break; case CSI_BUF_SUB_IDX_20_UU: case CSI_BUF_SUB_IDX_20_UL: case CSI_BUF_SUB_IDX_20_LU: case CSI_BUF_SUB_IDX_20_LL: bw = CHANNEL_WIDTH_20; break; case CSI_BUF_SUB_IDX_NON: default: bw = CHANNEL_WIDTH_MAX; break; } return bw; } /** * rtw_hal_get_csi_buf_type : check the csi buffer index is valid or not. * @buf: (struct hal_csi_buf *) * return: * @ret: true = TYPE MU ; false = TYPE SU **/ bool rtw_hal_get_csi_buf_type(void *buf) { struct hal_csi_buf *csi_buf = (struct hal_csi_buf *)buf; bool ret = false; if (csi_buf != NULL) { ret = (csi_buf->type == HAL_CSI_BUF_TYPE_MU) ? true : false; } return ret; }