/****************************************************************************** * * 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. * *****************************************************************************/ #define _PHL_TWT_C_ #include "phl_headers.h" #ifdef CONFIG_PHL_TWT #include "phl_twt.h" void _twt_transfer_config_state(enum phl_twt_action action, enum twt_config_state *state) { if (PHL_TWT_ACTION_FREE == action) *state = twt_config_state_free; else if (PHL_TWT_ACTION_ALLOC == action) *state = twt_config_state_idle; else if (PHL_TWT_ACTION_ENABLE == action) *state = twt_config_state_enable; else if (PHL_TWT_ACTION_DISABLE == action) *state = twt_config_state_idle; else if (PHL_TWT_ACTION_UP_ERROR == action) *state = twt_config_state_error; } /* * Calculate map of macid * @map_offset: number of offset for wait_macid_map * @macid_map: map of macid * Ex: macid_map = 0x80(bit7), offset = 2, macid 71(7 + 2*32) wait announce */ void _twt_calc_macid_map_info(u16 macid, u8 *map_offset, u32 *macid_map) { *map_offset = (u8)(macid / 32); *macid_map = BIT(macid % 32); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_calc_macid_map_info(): macid:%d, map_offset:%d, macid_map:0x%x\n", macid, *map_offset, *macid_map); } u32 _twt_calc_intvl(u8 exp, u16 mantissa) { u32 intvl = 0; intvl = mantissa * (1 << exp); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_calc_intvl(): exp:%u, mantissa:%u, intvl=%u\n", exp, mantissa, intvl); return intvl; } u32 _twt_calc_wakeup_dur(u8 dur, enum rtw_phl_wake_dur_unit dur_unit) { u32 dur_t = 0; if (RTW_PHL_WAKE_256US == dur_unit) dur_t = dur * 256; else if (RTW_PHL_WAKE_1TU == dur_unit) dur_t = dur * 1024; return dur_t; } enum rtw_phl_status _twt_fill_individual_twt_para_set( struct rtw_phl_indiv_twt_para_set *para, bool ndp_paging, u8 *buf, u8 *length) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct rtw_phl_req_type_indiv *req_type = ¶->req_type; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_fill_individual_twt_para_set(): twt_request(%d), twt_setup_cmd(%d), trigger(%d), implicit(%d), flow_type(%d), twt_flow_id(%d), twt_wake_int_exp(%d), twt_protection(%d)\n", req_type->twt_request, req_type->twt_setup_cmd, req_type->trigger, req_type->implicit, req_type->flow_type, req_type->twt_flow_id, req_type->twt_wake_int_exp, req_type->twt_protection); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_fill_individual_twt_para_set(): target_wake_t_h(0x%08x), target_wake_t_l(0x%08x), nom_min_twt_wake_dur(%d), twt_wake_int_mantissa(%d), twt_channel(%d)\n", para->target_wake_t_h, para->target_wake_t_l, para->nom_min_twt_wake_dur, para->twt_wake_int_mantissa, para->twt_channel); *length = 0; /*Request Type*/ SET_TWT_REQ_TYPE_TWT_REQUEST(buf, req_type->twt_request); SET_TWT_REQ_TYPE_TWT_SETUP_COMMAND(buf, req_type->twt_setup_cmd); SET_TWT_REQ_TYPE_TRIGGER(buf, req_type->trigger); SET_TWT_REQ_TYPE_IMPLICIT(buf, req_type->implicit); SET_TWT_REQ_TYPE_FLOW_TYPE(buf, req_type->flow_type); SET_TWT_REQ_TYPE_TWT_FLOW_IDENTIFER(buf, req_type->twt_flow_id); SET_TWT_REQ_TYPE_TWT_WAKE_INTERVAL_EXPONENT(buf, req_type->twt_wake_int_exp); SET_TWT_REQ_TYPE_TWT_PROTECTION(buf, req_type->twt_protection); *length += REQUEST_TYPE_LENGTH; if (RTW_PHL_TWT_GROUPING == req_type->twt_setup_cmd) { /*TODO*/ } else { SET_TWT_TARGET_WAKE_TIME_L(buf, para->target_wake_t_l); SET_TWT_TARGET_WAKE_TIME_H(buf, para->target_wake_t_h); *length += TARGET_WAKE_TIME_LENGTH; } SET_TWT_NOMINAL_MINIMUM_TWT_WAKE_DURATION(buf, *length, para->nom_min_twt_wake_dur); *length += NOMINAL_MINIMUM_TWT_WAKE_DURATION_LENGTH; SET_TWT_TWT_WAKE_INTERVAL_MANTISSA(buf, *length, para->twt_wake_int_mantissa); *length += TWT_WAKE_INTERVAL_MANTISSA_LENGTH; SET_TWT_TWT_CHANNEL(buf, *length, para->twt_channel); *length += TWT_CHANNEL_LENGTH; if (true == ndp_paging) { /*TODO*/ } pstatus = RTW_PHL_STATUS_SUCCESS; return pstatus; } enum rtw_phl_status _twt_parse_individual_twt_para(u8 *twt_ele, u16 length, struct rtw_phl_twt_element *element) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct rtw_phl_indiv_twt_para_set *para = &element->info.i_twt_para_set; struct rtw_phl_req_type_indiv *req_type = ¶->req_type; u8 *next_buf = twt_ele + ELEM_ID_LEN + ELEM_LEN_LEN + CONTROL_LENGTH; req_type->twt_request = GET_TWT_REQ_TYPE_TWT_REQUEST(next_buf); req_type->twt_setup_cmd = GET_TWT_REQ_TYPE_TWT_SETUP_COMMAND(next_buf); req_type->trigger = GET_TWT_REQ_TYPE_TRIGGER(next_buf); req_type->implicit = GET_TWT_REQ_TYPE_IMPLICIT(next_buf); req_type->flow_type = GET_TWT_REQ_TYPE_FLOW_TYPE(next_buf); req_type->twt_flow_id = GET_TWT_REQ_TYPE_TWT_FLOW_IDENTIFER(next_buf); req_type->twt_wake_int_exp = GET_TWT_REQ_TYPE_TWT_WAKE_INTERVAL_EXPONENT(next_buf); req_type->twt_protection = GET_TWT_REQ_TYPE_TWT_PROTECTION(next_buf); next_buf += REQUEST_TYPE_LENGTH; if (RTW_PHL_TWT_GROUPING == req_type->twt_setup_cmd) { //Todo } else { para->target_wake_t_l = GET_TWT_TARGET_WAKE_TIME_L(next_buf); para->target_wake_t_h = GET_TWT_TARGET_WAKE_TIME_H(next_buf); next_buf += TARGET_WAKE_TIME_LENGTH; } para->nom_min_twt_wake_dur = GET_TWT_NOMINAL_MINIMUM_TWT_WAKE_DURATION(next_buf); next_buf += NOMINAL_MIN_TWT_WAKE_DURATION_LENGTH; para->twt_wake_int_mantissa = GET_TWT_TWT_WAKE_INTERVAL_MANTISSA(next_buf); next_buf += TWT_WAKE_INTERVAL_MANTISSA_LENGTH; para->twt_channel = GET_TWT_TWT_CHANNEL(next_buf); next_buf += TWT_CHANNEL_LENGTH; if (element->twt_ctrl.ndp_paging_indic) { /*TODO*/ } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_parse_individual_twt_para(): twt_request:%d, twt_setup_cmd:%d, trigger:%d, implicit:%d, flow_type:%d, twt_flow_id:%d, twt_wake_int_exp:%d, twt_protection:%d\n", req_type->twt_request, req_type->twt_setup_cmd, req_type->trigger, req_type->implicit, req_type->flow_type, req_type->twt_flow_id, req_type->twt_wake_int_exp, req_type->twt_protection); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_parse_individual_twt_para(): target_wake_t_h:0x%08X, target_wake_t_l:0x%08X, nom_min_twt_wake_dur:%d, twt_wake_int_mantissa:%d, twt_channel:%d\n", para->target_wake_t_h, para->target_wake_t_l, para->nom_min_twt_wake_dur, para->twt_wake_int_mantissa, para->twt_channel); pstatus = RTW_PHL_STATUS_SUCCESS; return pstatus; } enum rtw_phl_status _twt_announce_info_enqueue(struct phl_info_t *phl_info, struct phl_queue *twt_annc_q, struct _twt_announce_info *twt_annc) { void *drv = phl_to_drvpriv(phl_info); _os_spinlockfg sp_flags; if (!twt_annc) return RTW_PHL_STATUS_FAILURE; _os_spinlock(drv, &twt_annc_q->lock, _irq, &sp_flags); list_add_tail(&twt_annc->list, &twt_annc_q->queue); twt_annc_q->cnt++; _os_spinunlock(drv, &twt_annc_q->lock, _irq, &sp_flags); return RTW_PHL_STATUS_SUCCESS; } struct _twt_announce_info * _twt_announce_info_dequeue( struct phl_info_t *phl_info, struct phl_queue *twt_annc_q) { struct _twt_announce_info *twt_annc = NULL; void *drv = phl_to_drvpriv(phl_info); _os_spinlockfg sp_flags; _os_spinlock(drv, &twt_annc_q->lock, _irq, &sp_flags); if (list_empty(&twt_annc_q->queue)) { twt_annc = NULL; } else { twt_annc = list_first_entry(&twt_annc_q->queue, struct _twt_announce_info, list); list_del(&twt_annc->list); twt_annc_q->cnt--; } _os_spinunlock(drv, &twt_annc_q->lock, _irq, &sp_flags); return twt_annc; } enum rtw_phl_status _twt_sta_announce(struct phl_info_t *phl_info, struct phl_queue *annc_queue, u16 macid) { enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE; enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; void *drv = phl_to_drvpriv(phl_info); _os_spinlockfg sp_flags; struct _twt_announce_info *info = NULL; _os_list *annc_list = &annc_queue->queue; u8 offset = 0; u32 macid_map = 0; _twt_calc_macid_map_info(macid, &offset, &macid_map); _os_spinlock(drv, &annc_queue->lock, _irq, &sp_flags); phl_list_for_loop(info, struct _twt_announce_info, annc_list, list) { if (NULL == info) break; if (info->map_offset != offset) continue; if (!(info->wait_macid_map & macid_map)) continue; hstatus = rtw_hal_twt_sta_announce(phl_info->hal, (u8)macid); if (RTW_HAL_STATUS_SUCCESS == hstatus) { info->wait_macid_map &= (~macid_map); pstatus = RTW_PHL_STATUS_SUCCESS; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_sta_announce(): rtw_hal_twt_sta_announce success, macid:%d, map_offset:%d, wait_macid_map:0x%x\n", macid, info->map_offset, info->wait_macid_map); } else { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "_twt_sta_announce(): rtw_hal_twt_sta_announce fail, macid:%d, map_offset:%d, wait_macid_map:0x%x\n", macid, info->map_offset, info->wait_macid_map); } break; } _os_spinunlock(drv, &annc_queue->lock, _irq, &sp_flags); return pstatus; } enum rtw_phl_status _twt_set_sta_announce_state(struct phl_info_t *phl_info, struct phl_queue *annc_q, u16 macid, enum phl_wait_annc_type type) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; void *drv = phl_to_drvpriv(phl_info); _os_spinlockfg sp_flags; struct _twt_announce_info *info = NULL; _os_list *annc_list = &annc_q->queue; u8 offset = 0; u32 macid_map = 0; u8 bset = false; _twt_calc_macid_map_info(macid, &offset, &macid_map); _os_spinlock(drv, &annc_q->lock, _irq, &sp_flags); phl_list_for_loop(info, struct _twt_announce_info, annc_list, list) { if (NULL == info) break; if (info->map_offset != offset) continue; if (PHL_WAIT_ANNC_ENABLE == type) { info->wait_macid_map |= macid_map; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_set_sta_announce_state(): set macid:%d to wait annc state, map_offset:%d, wait_macid_map:0x%x\n", macid, info->map_offset, info->wait_macid_map); } else if (PHL_WAIT_ANNC_DISABLE == type) { info->wait_macid_map &= (~macid_map); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_set_sta_announce_state(): set macid:%d to annc state, map_offset:%d, wait_macid_map:0x%x\n", macid, info->map_offset, info->wait_macid_map); } else { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "_twt_set_sta_announce_state(): Unknown type:%d\n", type); break; } pstatus = RTW_PHL_STATUS_SUCCESS; bset = true; break; } _os_spinunlock(drv, &annc_q->lock, _irq, &sp_flags); if (true == bset) goto exit; if (PHL_WAIT_ANNC_ENABLE == type) { info = _os_mem_alloc(drv, sizeof(struct _twt_announce_info)); if (NULL == info) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "_twt_sta_wait_announce(): Fail to alloc new annc info\n"); } else { info->map_offset = offset; info->wait_macid_map = macid_map; _twt_announce_info_enqueue(phl_info, annc_q, info); pstatus = RTW_PHL_STATUS_SUCCESS; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_set_sta_announce_state(): add new Q and set macid:%d to annc state, map_offset:%d, wait_macid_map:0x%x\n", macid, info->map_offset, info->wait_macid_map); } } else if (PHL_WAIT_ANNC_DISABLE == type) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "_twt_set_sta_announce_state(): macid:%d is not in wait annc state\n", macid); } else { /*nothing*/ } exit: return pstatus; } #if 0 /* * Get macid of sta wait for announce form FW * @wait_case: C2HTWT_ANNOUNCE_WAIT_DISABLE_MACID = 0, C2HTWT_ANNOUNCE_WAIT_ENABLE_MACID = 1 * @macid0: macid of sta * @macid1: macid of sta * @macid2: macid of sta */ enum rtw_phl_status _twt_handle_c2h_wait_annc(struct phl_info_t *phl_info, u8 *content) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_twt_info *phl_twt_info = get_twt_info(phl_info); struct phl_queue *annc_q = &phl_twt_info->twt_annc_queue; u8 wait_case = 0, macid = 0; u8 i = 0; bool error = false; wait_case = (*((u8*)content)) & 0xf; /*BIT0-3*/ PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_handle_c2h_wait_annc(): content:0x%X, wait_case:%d, macid0:%d, macid1:%d, macid2:%d\n", *content, wait_case, *((u8*)content + 1), *((u8*)content + 2), *((u8*)content + 3)); for (i = 1; i < 4; i++) { macid = *((u8*)content + i); if (IGNORE_MACID == macid) continue; pstatus = _twt_set_sta_announce_state(phl_info, annc_q, macid, wait_case); if (RTW_PHL_STATUS_SUCCESS != pstatus) { PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_handle_c2h_wait_annc(): pstatus:%d, macid: %d, fail to set sta wait announce state\n", pstatus, macid); error = true; } } if (true == error) pstatus = RTW_PHL_STATUS_FAILURE; return pstatus; } #endif /* struct rtw_twt_sta_info * _twt_get_twt_sta( struct phl_info_t *phl_info, struct phl_queue *sta_queue, struct rtw_phl_stainfo_t *phl_sta, u8 id ) { void *drv = phl_to_drvpriv(phl_info); struct rtw_twt_sta_info *twt_sta = NULL, *ret_twt_sta = NULL; _os_list *sta_list = &sta_queue->queue; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> _twt_get_twt_sta()\n"); _os_spinlock(drv, &sta_queue->lock, _bh, NULL); phl_list_for_loop(twt_sta, struct rtw_twt_sta_info, sta_list, list) { if (twt_sta == NULL) break; if (phl_sta != twt_sta->phl_sta) continue; if (DELETE_ALL != id && id != twt_sta->id) continue; ret_twt_sta = twt_sta; } _os_spinunlock(drv, &sta_queue->lock, _bh, NULL); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== _twt_get_twt_sta()\n"); return ret_twt_sta; } */ void _twt_fill_config_info_indiv(struct rtw_phl_twt_info *twt_info, struct rtw_phl_indiv_twt_para_set *para_set) { struct rtw_phl_req_type_indiv *req_type = ¶_set->req_type; twt_info->trigger = req_type->trigger; twt_info->flow_type = req_type->flow_type; twt_info->implicit_lastbcast = req_type->implicit; twt_info->twt_protection = req_type->twt_protection; twt_info->twt_wake_int_exp = req_type->twt_wake_int_exp; twt_info->twt_wake_int_mantissa = para_set->twt_wake_int_mantissa; twt_info->nom_min_twt_wake_dur = para_set->nom_min_twt_wake_dur; twt_info->target_wake_time_h = para_set->target_wake_t_h; twt_info->target_wake_time_l = para_set->target_wake_t_l; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_fill_config_info_indiv(): twt_info: trigger:%d, flow_type:%d, implicit_lastbcast:%d, twt_protection:%d, twt_wake_int_exp:%d, twt_wake_int_mantissa:%d, nom_min_twt_wake_dur:%d, target_wake_time_h:0x%08X, target_wake_time_l:0x%08X\n", twt_info->trigger, twt_info->flow_type, twt_info->implicit_lastbcast, twt_info->twt_protection, twt_info->twt_wake_int_exp, twt_info->twt_wake_int_mantissa, twt_info->nom_min_twt_wake_dur, twt_info->target_wake_time_h, twt_info->target_wake_time_l); } void _twt_fill_config_info(struct rtw_phl_twt_info *twt_info, struct rtw_phl_twt_setup_info *setup_info) { struct rtw_phl_twt_element *twt_ele = &setup_info->twt_element; struct rtw_phl_twt_control *twt_ctrl = &twt_ele->twt_ctrl; twt_info->responder_pm_mode = twt_ctrl->responder_pm_mode; twt_info->nego_type = twt_ctrl->nego_type; twt_info->wake_dur_unit = twt_ctrl->wake_dur_unit; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_fill_config_info(): twt_info: responder_pm_mode:%d, nego_type:%d, wake_dur_unit:%d\n", twt_info->responder_pm_mode, twt_info->nego_type, twt_info->wake_dur_unit); if (RTW_PHL_INDIV_TWT == twt_info->nego_type) { _twt_fill_config_info_indiv(twt_info, &twt_ele->info.i_twt_para_set); } else { /*todo*/ } } void _twt_reset_config_info(struct phl_info_t *phl, struct phl_twt_config *config) { config->role = NULL; _os_mem_set(phl_to_drvpriv(phl), &config->twt_info, 0, sizeof(struct rtw_phl_twt_info)); } u8 _twt_compare_twt_para(struct rtw_phl_twt_info *twt_info, struct rtw_phl_twt_setup_info *twt_setup) { u8 ret = false; u64 twt1 = 0, twt2 = 0, diff_t = 0; u32 intvl = 0; struct rtw_phl_twt_info cmp_info = {0}; _twt_fill_config_info(&cmp_info, twt_setup); do { if (cmp_info.responder_pm_mode != twt_info->responder_pm_mode) break; if (cmp_info.nego_type != twt_info->nego_type) break; if (cmp_info.trigger != twt_info->trigger) break; if (cmp_info.flow_type != twt_info->flow_type) break; if (cmp_info.implicit_lastbcast != twt_info->implicit_lastbcast) break; if (cmp_info.twt_protection != twt_info->twt_protection) break; if ((_twt_calc_wakeup_dur(cmp_info.nom_min_twt_wake_dur , cmp_info.wake_dur_unit)) != (_twt_calc_wakeup_dur(twt_info->nom_min_twt_wake_dur, twt_info->wake_dur_unit))) break; if ((_twt_calc_intvl(cmp_info.twt_wake_int_exp, cmp_info.twt_wake_int_mantissa)) != (_twt_calc_intvl(twt_info->twt_wake_int_exp, twt_info->twt_wake_int_mantissa))) break; /*compare target wake time*/ intvl = _twt_calc_intvl(twt_info->twt_wake_int_exp, twt_info->twt_wake_int_mantissa); twt1 = cmp_info.target_wake_time_h; twt1 = twt1 << 32; twt1 |= cmp_info.target_wake_time_l; twt2 = twt_info->target_wake_time_h; twt2 = twt2 << 32; twt2 |= twt_info->target_wake_time_l; if (twt1 > twt2) { /*cmp_info target_wake_time > twt_info target_wake_time*/ diff_t = _os_minus64(twt1, twt2); } else { diff_t = _os_minus64(twt2, twt1); } if (_os_modular64(diff_t, intvl) != 0) break; ret = true; } while(false); return ret; } u8 _twt_is_same_config(struct phl_twt_config *config, struct _twt_compare *compare_info) { bool found = false; do { if (config->role != compare_info->role) break; if (!(twt_config_state_idle == config->state || twt_config_state_enable == config->state)) break; if (_twt_compare_twt_para(&config->twt_info, &compare_info->twt_setup)) found = true; } while(false); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== _twt_is_same_config(): found(%d)\n", found); return found; } void _twt_dump_twt_cfg_info(struct phl_twt_cfg_info *twt_cfg_i) { struct phl_twt_config *config = NULL; u8 i = 0; config = (struct phl_twt_config *)twt_cfg_i->twt_cfg_ring; for (i = 0; i < twt_cfg_i->twt_cfg_num; i++) { PHL_TRACE(COMP_PHL_TWT, _PHL_DEBUG_, "_twt_dump_twt_cfg_info(): loop i(%d), cfg id(%d), state(%d)\n", i, config[i].idx, config[i].state); } } enum rtw_phl_status _twt_operate_twt_config(struct phl_info_t *phl_info, struct phl_twt_cfg_info *twt_cfg_i, enum phl_operate_config_type type, u8 *para, struct phl_twt_config **ret_config) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_twt_config *config = (struct phl_twt_config *)twt_cfg_i->twt_cfg_ring; u8 i = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_DEBUG_, "==> _twt_operate_twt_config(): type(%d)\n", type); if (type == PHL_GET_CONFIG_BY_ID) { if (*para >= twt_cfg_i->twt_cfg_num) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_operate_twt_config(): get cfg by id(%d) fail, out of range(%d)\n", *para, twt_cfg_i->twt_cfg_num); goto exit; } if (twt_config_state_free == config[*para].state){ PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_operate_twt_config(): get cfg by id(%d) fail, cfg state is in twt_config_state_free\n", *para); goto exit; } *ret_config = &config[*para]; pstatus = RTW_PHL_STATUS_SUCCESS; goto exit; } else if (type == PHL_FREE_CONFIG) { if (*para >= twt_cfg_i->twt_cfg_num) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_operate_twt_config(): free cfg by id(%d) fail, out of range(%d)\n", *para, twt_cfg_i->twt_cfg_num); goto exit; } if (twt_config_state_free == config[*para].state){ PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_operate_twt_config(): free cfg by id(%d) fail, cfg state is in twt_config_state_free\n", *para); goto exit; } _twt_transfer_config_state(PHL_TWT_ACTION_FREE, &config[*para].state); pstatus = RTW_PHL_STATUS_SUCCESS; goto exit; } else if (type == PHL_GET_HEAD_CONFIG) { *ret_config = config; pstatus = RTW_PHL_STATUS_SUCCESS; goto exit; } else if (type == PHL_GET_NEXT_CONFIG) { u8 next_id = 0; if (*para >= twt_cfg_i->twt_cfg_num) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_operate_twt_config(): get cfg by id(%d) fail, out of range(%d)\n", *para, twt_cfg_i->twt_cfg_num); goto exit; } next_id = *para + 1; if (next_id == twt_cfg_i->twt_cfg_num) next_id = 0; *ret_config = &config[next_id]; pstatus = RTW_PHL_STATUS_SUCCESS; goto exit; } for (i = 0; i < twt_cfg_i->twt_cfg_num; i++) { PHL_TRACE(COMP_PHL_TWT, _PHL_DEBUG_, "_twt_operate_twt_config(): loop i(%d), cfg id(%d), state(%d)\n", i, config[i].idx, config[i].state); if (type == PHL_GET_NEW_CONFIG) { if (twt_config_state_free != config[i].state) continue; _twt_reset_config_info(phl_info, &config[i]); _twt_transfer_config_state(PHL_TWT_ACTION_ALLOC, &config[i].state); config[i].twt_info.twt_id = config[i].idx; *ret_config = &config[i]; pstatus = RTW_PHL_STATUS_SUCCESS; break; } else if (type == PHL_GET_CONFIG_BY_ROLE) { if (twt_config_state_free == config[i].state) continue; if ((struct rtw_wifi_role_t *)para != config[i].role) continue; *ret_config = &config[i]; pstatus = RTW_PHL_STATUS_SUCCESS; break; } else if (type == PHL_GET_CONFIG_BY_PARA) { if (twt_config_state_free == config[i].state) continue; if (!_twt_is_same_config(&config[i], (struct _twt_compare *)para)) continue; *ret_config = &config[i]; pstatus = RTW_PHL_STATUS_SUCCESS; break; } } exit: _twt_dump_twt_cfg_info(twt_cfg_i); PHL_TRACE(COMP_PHL_TWT, _PHL_DEBUG_, "<== _twt_operate_twt_config(): pstatus:%d\n", pstatus); return pstatus; } enum rtw_phl_status _twt_sta_update(void *hal, u16 macid, u8 twt_id, enum rtw_phl_twt_sta_action action) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE; hstatus = rtw_hal_twt_sta_update(hal, (u8)macid, twt_id, action); if (hstatus != RTW_HAL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "twt sta update fail: hstatus:%d, macid:%d, twt_id:%d, action:%d\n", hstatus, macid, twt_id, action); } else { PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "twt sta update ok: macid:%d, twt_id:%d, action:%d\n", macid, twt_id, action); pstatus = RTW_PHL_STATUS_SUCCESS; } return pstatus; } enum rtw_phl_status _twt_all_sta_update(struct phl_info_t *phl_info, u8 config_id, struct phl_queue *sta_queue, enum rtw_phl_twt_sta_action action) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; void *drv = phl_to_drvpriv(phl_info); _os_list *sta_list = &sta_queue->queue; struct rtw_twt_sta_info *psta = NULL; _os_spinlock(drv, &sta_queue->lock, _bh, NULL); phl_list_for_loop(psta, struct rtw_twt_sta_info, sta_list, list) { if (NULL == psta) break; pstatus = _twt_sta_update(phl_info->hal, psta->phl_sta->macid, config_id, action); if (RTW_PHL_STATUS_SUCCESS != pstatus) break; } _os_spinunlock(drv, &sta_queue->lock, _bh, NULL); return pstatus; } struct rtw_twt_sta_info * _twt_get_sta_info(struct phl_info_t *phl_info, struct phl_queue *sta_queue, struct rtw_phl_stainfo_t *phl_sta) { void *drv = phl_to_drvpriv(phl_info); _os_list *sta_list = &sta_queue->queue; struct rtw_twt_sta_info *psta = NULL, *ret_sta = NULL; _os_spinlock(drv, &sta_queue->lock, _bh, NULL); phl_list_for_loop(psta, struct rtw_twt_sta_info, sta_list, list) { if (NULL == psta) break; if (phl_sta == psta->phl_sta) { ret_sta = psta; break; } } _os_spinunlock(drv, &sta_queue->lock, _bh, NULL); return ret_sta; } enum rtw_phl_status _twt_sta_enqueue(struct phl_info_t *phl_info, struct phl_queue *sta_q, struct rtw_twt_sta_info *psta) { void *drv = phl_to_drvpriv(phl_info); if (!psta) return RTW_PHL_STATUS_FAILURE; _os_spinlock(drv, &sta_q->lock, _bh, NULL); list_add_tail(&psta->list, &sta_q->queue); sta_q->cnt++; _os_spinunlock(drv, &sta_q->lock, _bh, NULL); return RTW_PHL_STATUS_SUCCESS; } struct rtw_twt_sta_info * _twt_sta_dequeue(struct phl_info_t *phl_info, struct phl_queue *sta_q, u16 *cnt) { struct rtw_twt_sta_info *psta = NULL; void *drv = phl_to_drvpriv(phl_info); _os_spinlock(drv, &sta_q->lock, _bh, NULL); if (list_empty(&sta_q->queue)) { psta = NULL; } else { psta = list_first_entry(&sta_q->queue, struct rtw_twt_sta_info, list); list_del(&psta->list); sta_q->cnt--; *cnt = (u16)sta_q->cnt; } _os_spinunlock(drv, &sta_q->lock, _bh, NULL); return psta; } /* * Delete all sta entry from queue * @sta_queue: twt sta Q */ void _twt_delete_all_sta(struct phl_info_t *phl_info, struct phl_queue *sta_queue) { struct rtw_twt_sta_info *twt_sta; void *drv = phl_to_drvpriv(phl_info); u16 cnt; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> _twt_delete_all_sta()\n"); do { twt_sta = _twt_sta_dequeue(phl_info, sta_queue, &cnt); if (NULL != twt_sta) _os_mem_free(drv, twt_sta, sizeof(struct rtw_twt_sta_info)); } while (twt_sta != NULL); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== _twt_delete_all_sta()\n"); } /* * Delete twt sta entry by specific sta and id from queue * @sta_queue: twt sta Q * @phl_sta: specific sta * @id: specific twt folw id/broadcast twt id or delete all * @cnt: total num of sta entery in Q */ enum rtw_phl_status _twt_delete_sta(struct phl_info_t *phl_info, struct phl_queue *sta_q, struct rtw_phl_stainfo_t *phl_sta, u8 id, u16 *cnt) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct rtw_twt_sta_info *twt_sta, *f_sta; void *drv = phl_to_drvpriv(phl_info); f_sta = _twt_sta_dequeue(phl_info, sta_q, cnt); twt_sta = f_sta; do { if (twt_sta == NULL) break; if ((phl_sta == twt_sta->phl_sta) && (DELETE_ALL == id || id == twt_sta->id)) { _os_mem_free(drv, twt_sta, sizeof(struct rtw_twt_sta_info)); pstatus = RTW_PHL_STATUS_SUCCESS; break; } _twt_sta_enqueue(phl_info, sta_q, twt_sta); twt_sta = _twt_sta_dequeue(phl_info, sta_q, cnt); if (NULL != twt_sta && twt_sta == f_sta) { _twt_sta_enqueue(phl_info, sta_q, twt_sta); break; } } while (true); return pstatus; } /* * Does sta exist in twt sta entry by specific sta and id * @sta_queue: twt sta Q * @phl_sta: specific sta * @id: specific twt folw id/broadcast twt id or delete all */ enum rtw_phl_status _twt_sta_exist(struct phl_info_t *phl_info, struct phl_queue *sta_q, struct rtw_phl_stainfo_t *phl_sta, u8 id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct rtw_twt_sta_info *twt_sta = NULL; void *drv = phl_to_drvpriv(phl_info); _os_list *q_list = &sta_q->queue; _os_spinlock(drv, &sta_q->lock, _bh, NULL); phl_list_for_loop(twt_sta, struct rtw_twt_sta_info, q_list, list) { if (NULL == twt_sta) break; if ((phl_sta == twt_sta->phl_sta) && (DELETE_ALL == id || id == twt_sta->id)) { pstatus = RTW_PHL_STATUS_SUCCESS; break; } } _os_spinunlock(drv, &sta_q->lock, _bh, NULL); return pstatus; } enum rtw_phl_status _twt_add_sta(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *phl_sta, struct phl_queue *sta_q, u8 id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct rtw_twt_sta_info *twt_sta; void *drv = phl_to_drvpriv(phl_info); twt_sta = _os_mem_alloc(drv, sizeof(struct rtw_twt_sta_info)); if (NULL == twt_sta) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "_phl_twt_add_sta(): alloc rtw_twt_sta_info failed\n"); } else { twt_sta->phl_sta = phl_sta; twt_sta->id = id; _twt_sta_enqueue(phl_info, sta_q, twt_sta); pstatus = RTW_PHL_STATUS_SUCCESS; } return pstatus; } enum rtw_phl_status _twt_delete_sta_info(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *phl_sta, u8 ignore_type, enum rtw_phl_nego_type nego_type, u8 id, u8 *bitmap) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_twt_info *phl_twt_info = get_twt_info(phl_info); struct phl_twt_cfg_info *twt_cfg_i = &phl_twt_info->twt_cfg_info; struct phl_twt_config *config = NULL, *f_config = NULL; enum rtw_phl_nego_type type = nego_type; bool delete_error = false; u16 cnt; u8 delete_id = ignore_type ? DELETE_ALL : id; *bitmap = 0; if (RTW_PHL_MANAGE_BCAST_TWT == nego_type) type = RTW_PHL_BCAST_TWT; if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl_info, twt_cfg_i, PHL_GET_HEAD_CONFIG, NULL, &config)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_delete_sta_info(): Fail to get first allocate config\n"); goto exit; } f_config = config; do { PHL_TRACE(COMP_PHL_TWT, _PHL_DEBUG_, "_twt_delete_sta_info(): while loop, twt_id:%d\n", config->twt_info.twt_id); if (twt_config_state_free == config->state) goto next_cfg; if (config->role != phl_sta->wrole) goto next_cfg; if (false == ignore_type && config->twt_info.nego_type != type) goto next_cfg; if (RTW_PHL_STATUS_SUCCESS != _twt_sta_exist(phl_info, &config->twt_sta_queue, phl_sta, delete_id)) goto next_cfg; if (RTW_PHL_STATUS_SUCCESS != _twt_sta_update(phl_info->hal, phl_sta->macid, config->twt_info.twt_id, TWT_STA_DEL_MACID)) { delete_error = true; goto next_cfg; } if (RTW_PHL_STATUS_SUCCESS != _twt_delete_sta(phl_info, &config->twt_sta_queue, phl_sta, delete_id, &cnt)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_delete_sta_info(): Fail to delete sta from twt_sta Q, macid(0x%x), delete_id(%d)\n", phl_sta->macid, delete_id); delete_error = true; goto next_cfg; } if (0 == cnt) *bitmap |= (1 << config->twt_info.twt_id); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_delete_sta_info(): Delete sta success, config id = %d, twt_sta_queue cnt:%d, bitmap:0x%X\n", config->twt_info.twt_id, cnt, *bitmap); if (DELETE_ALL != delete_id) break; next_cfg: if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl_info, twt_cfg_i, PHL_GET_NEXT_CONFIG, (u8 *)&config->idx, &config)) { delete_error = true; break; } } while (config != f_config); if (false == delete_error) pstatus = RTW_PHL_STATUS_SUCCESS; else pstatus = RTW_PHL_STATUS_FAILURE; exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_delete_sta_info(): pstatus:%d, nego_type = %d, id:%d, bitmap:0x%x\n", pstatus, nego_type, id, *bitmap); return pstatus; } enum rtw_phl_status _twt_info_update(struct phl_info_t *phl_info, struct phl_twt_config *config, enum phl_twt_action action) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE; enum rtw_phl_twt_cfg_action config_action; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> _twt_info_update()\n"); if (PHL_TWT_ACTION_ENABLE == action) { config_action = TWT_CFG_ADD; } else if (PHL_TWT_ACTION_DISABLE == action) { config_action = TWT_CFG_DELETE; } else { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_info_update(): Unexpected action:%d\n", action); goto exit; } hstatus = rtw_hal_twt_info_update(phl_info, config->twt_info, config->role, config_action); if (hstatus == RTW_HAL_STATUS_SUCCESS) { _twt_transfer_config_state(action, &config->state); pstatus = RTW_PHL_STATUS_SUCCESS; PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "_twt_info_update(): update ok\n"); } else { _twt_transfer_config_state(PHL_TWT_ACTION_UP_ERROR, &config->state); PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_info_update(): update fail, hstatus:%d\n", hstatus); } exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<==_twt_info_update(): hstatus:%d, twt_id:%d, action:%d\n", pstatus, config->twt_info.twt_id, action); return pstatus; } /* void _twt_free_config( void *phl, struct phl_twt_config *config ) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; _twt_info_update(phl_info->hal, config, PHL_TWT_ACTION_DISABLE); _twt_delete_all_sta(phl_info, &config->twt_sta_queue); _twt_reset_config_info(config); _twt_transfer_config_state(PHL_TWT_ACTION_FREE, &config->state); } */ bool _twt_exist_same_twt_config(struct phl_info_t *phl, struct phl_twt_cfg_info *twt_cfg_i, struct rtw_wifi_role_t *role, struct rtw_phl_twt_setup_info setup_info, struct phl_twt_config **ret_config) { bool exist = false; struct _twt_compare compare_info = {0}; struct phl_twt_config *config = NULL; compare_info.role = role; compare_info.twt_setup = setup_info; if (RTW_PHL_STATUS_SUCCESS == _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_CONFIG_BY_PARA, (u8 *)&compare_info, &config)) { *ret_config = config; exist = true; } return exist; } /* u8 _twt_get_new_config_entry( struct phl_info_t *phl, struct phl_queue *twt_queue, struct phl_twt_config **ret_config ) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_twt_config *config = NULL; u8 bget = false; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> _twt_get_new_config_entry()\n"); if (RTW_PHL_STATUS_SUCCESS == _twt_operate_twt_config(phl, twt_queue, PHL_GET_NEW_CONFIG, NULL, &config)) { *ret_config = config; bget = true; } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== _twt_get_new_config_entry(): bget:%d\n", bget); return bget; } */ bool _twt_new_config_is_available(struct phl_info_t *phl_i) { struct phl_twt_info *phl_twt_info = get_twt_info(phl_i); struct phl_twt_cfg_info *twt_cfg_i = &phl_twt_info->twt_cfg_info; struct phl_twt_config *config = NULL; u8 available = false; if (RTW_PHL_STATUS_SUCCESS == _twt_operate_twt_config(phl_i, twt_cfg_i, PHL_GET_NEW_CONFIG, NULL, &config)) { _twt_operate_twt_config(phl_i, twt_cfg_i, PHL_FREE_CONFIG, &config->twt_info.twt_id, NULL); available = true; } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_new_config_is_available(): bavailable:%d\n", available); return available; } /* * Whether the twt flow id of sta exist in any twt config entry. * @phl_sta: the specific sta * @role: specific role for search twt config entry * @id: twt flow id * Note: for sta mode. */ u8 _twt_flow_id_exist(void *phl, struct rtw_phl_stainfo_t *phl_sta, struct rtw_wifi_role_t *role, u8 id) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = get_twt_info(phl_info); struct phl_twt_cfg_info *twt_cfg_i = &phl_twt_info->twt_cfg_info; struct phl_twt_config *config = NULL, *f_config = NULL; struct rtw_twt_sta_info *twt_sta = NULL; bool exist = false; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> _twt_flow_id_exist()\n"); if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_HEAD_CONFIG, NULL, &config)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_flow_id_exist(): Fail to get first allocate config\n"); goto exit; } f_config = config; do { PHL_TRACE(COMP_PHL_TWT, _PHL_DEBUG_, "_twt_flow_id_exist(): while loop\n"); if (twt_config_state_free == config->state) goto next_cfg; if (config->role != phl_sta->wrole || RTW_PHL_INDIV_TWT != config->twt_info.nego_type) goto next_cfg; twt_sta = _twt_get_sta_info(phl_info, &config->twt_sta_queue, phl_sta); if (NULL != twt_sta && id == twt_sta->id) { exist = true; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_flow_id_exist(): exist the twt_flow_id:%d\n", id); break; } next_cfg: if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_NEXT_CONFIG, (u8 *)&config->idx, &config)) break; } while(config != f_config); exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== _twt_flow_id_exist(): twt flow id:%d, bexist:%d\n", id, exist); return exist; } enum rtw_phl_status _twt_accept_bcast_by_sta(struct phl_info_t *phl, struct rtw_phl_twt_setup_info *setup_info, struct rtw_phl_stainfo_t *phl_sta, u8 *config_id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; /*TODO*/ PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "==> _twt_accept_bcast_by_sta(): not support, todo\n"); pstatus = RTW_PHL_STATUS_FAILURE; return pstatus; } enum rtw_phl_status _twt_accept_indiv_by_sta(struct phl_info_t *phl, struct rtw_phl_twt_setup_info *setup_info, struct rtw_phl_stainfo_t *phl_sta, u8 *config_id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct rtw_phl_twt_element *twt_ele = &setup_info->twt_element; struct rtw_phl_twt_control *twt_ctrl = &twt_ele->twt_ctrl; struct rtw_phl_indiv_twt_para_set *para = &twt_ele->info.i_twt_para_set; struct rtw_phl_req_type_indiv *req_type = ¶->req_type; u8 bitmap = 0, id = 0, i = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> _twt_accept_indiv_by_sta()\n"); if (_twt_flow_id_exist(phl, phl_sta, phl_sta->wrole, req_type->twt_flow_id)) { pstatus = _twt_delete_sta_info(phl, phl_sta, false, twt_ctrl->nego_type, req_type->twt_flow_id, &bitmap); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_accept_indiv_by_sta(): twt flow id(%d) exist, first, delete twt sta, pstatus:%d, bitmap:0x%x\n", req_type->twt_flow_id, pstatus, bitmap); if (RTW_PHL_STATUS_SUCCESS == pstatus && bitmap != 0) { id = 0; do { i = ((bitmap >> id) & BIT0); if (i != 0) { bitmap &= ~(BIT(id)); break; } id++; } while (true); pstatus = rtw_phl_twt_free_twt_config(phl, id); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_accept_indiv_by_sta():sta Q is empty in twt config entry(%d), we free it, pstatus:%d \n", id, pstatus); if (bitmap !=0) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "_twt_accept_indiv_by_sta(): TWT config entry bitmap(0x%x) != 0, some twt config entry not free. please check code\n", bitmap); } } } pstatus = rtw_phl_twt_alloc_twt_config(phl, phl_sta->wrole, *setup_info, true, &id); if (RTW_PHL_STATUS_SUCCESS == pstatus) { pstatus = rtw_phl_twt_add_sta_info(phl, phl_sta, id, req_type->twt_flow_id); if (RTW_PHL_STATUS_SUCCESS != pstatus) { rtw_phl_twt_free_twt_config(phl, id); } else { *config_id = id; } } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "_twt_accept_indiv_by_sta(): pstatus:%d, config_id:%d\n", pstatus, *config_id); return pstatus; } /* * Initialize twt */ enum rtw_phl_status phl_twt_init(void *phl) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv = phl_to_drvpriv(phl_info); struct phl_twt_info *phl_twt_i = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config = NULL; u16 len = 0; u8 i = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> phl_twt_init()\n"); if (NULL == phl_info->phl_twt_info) { phl_info->phl_twt_info = _os_mem_alloc(drv, sizeof(struct phl_twt_info)); if (NULL == phl_info->phl_twt_info) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "phl_twt_init(): Failed to allocate phl_twt_info\n"); goto exit; } _os_mem_set(phl_to_drvpriv(phl_info), phl_info->phl_twt_info, 0, sizeof(struct phl_twt_info)); } else { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "phl_twt_init(): Duplicate init1, please check code\n"); } phl_twt_i = get_twt_info(phl_info); twt_cfg_i = &phl_twt_i->twt_cfg_info; if (NULL == twt_cfg_i->twt_cfg_ring) { twt_cfg_i->twt_cfg_num = MAX_NUM_HW_TWT_CONFIG; len = sizeof(struct phl_twt_config) * twt_cfg_i->twt_cfg_num; twt_cfg_i->twt_cfg_ring = _os_mem_alloc(drv, len); if (NULL == twt_cfg_i->twt_cfg_ring) { twt_cfg_i->twt_cfg_num = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "phl_twt_init(): Failed to allocate twt_cfg_ring\n"); goto exit; } config = (struct phl_twt_config *)twt_cfg_i->twt_cfg_ring; for (i = 0; i < twt_cfg_i->twt_cfg_num; i++) { _os_mem_set(phl_to_drvpriv(phl_info), config, 0, sizeof(struct phl_twt_config)); config->idx = i; pq_init(drv, &config->twt_sta_queue); config++; } } else { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "phl_twt_init(): Duplicate init2, please check code\n"); } /* init for twt_annc_queue */ pq_init(drv, &phl_twt_i->twt_annc_queue); pstatus = RTW_PHL_STATUS_SUCCESS; exit: PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "<== phl_twt_init(): pstatus:%d\n", pstatus); return pstatus; } /* * Deinitialize twt */ void phl_twt_deinit(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv = phl_to_drvpriv(phl_info); struct phl_twt_info *phl_twt_i = get_twt_info(phl_info); struct phl_twt_config *config = NULL; struct _twt_announce_info *annc_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; u8 i = 0; u16 len = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> phl_twt_deinit()\n"); if (NULL == phl_twt_i) goto exit; twt_cfg_i = &phl_twt_i->twt_cfg_info; if (NULL == twt_cfg_i->twt_cfg_ring) goto free_twt_info; config = (struct phl_twt_config *)(twt_cfg_i->twt_cfg_ring); for (i = 0; i < twt_cfg_i->twt_cfg_num; i++) { if (config->twt_sta_queue.cnt > 0) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "rtw_phl_twt_deinit(): config_id: %d, twt_sta_queue.cnt(%d) >0, force delete all\n", config->idx ,config->twt_sta_queue.cnt); _twt_delete_all_sta(phl_info, &config->twt_sta_queue); } pq_deinit(drv, &config->twt_sta_queue); config++; } len = sizeof(struct phl_twt_config) * twt_cfg_i->twt_cfg_num; _os_mem_free(drv, twt_cfg_i->twt_cfg_ring, len); do { annc_info = _twt_announce_info_dequeue(phl_info, &phl_twt_i->twt_annc_queue); if (NULL == annc_info) break; _os_mem_free(drv, annc_info, sizeof(struct _twt_announce_info)); } while(true); pq_deinit(drv, &phl_twt_i->twt_annc_queue); free_twt_info: _os_mem_free(drv, phl_info->phl_twt_info, sizeof(struct phl_twt_info)); exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_deinit()\n"); } /* * Allocate new twt config * @role: the user of twt config * @setup_info: twt setup info * @benable: whether to enable the twt config to fw, * if benable is equal to false, only allocate twt config entry * @id: Output the id of twt confi entry */ enum rtw_phl_status rtw_phl_twt_alloc_twt_config(void *phl, struct rtw_wifi_role_t *role, struct rtw_phl_twt_setup_info setup_info, u8 benable, u8 *id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config = NULL; bool alloc = false; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_alloc_twt_config()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_alloc_twt_config(): twt_sup == false\n"); return pstatus; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_alloc_twt_config(): twt_init == false\n"); return pstatus; } phl_twt_info = get_twt_info(phl_info); twt_cfg_i = &phl_twt_info->twt_cfg_info; /* if (true == _twt_exist_same_twt_config(phl_info, twt_cfg_i, role, setup_info, &config)) { PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "[TWT]alloc from existing config, id = %d\n", config->twt_info.twt_id); alloc = true; } else */{ if (RTW_PHL_STATUS_SUCCESS == _twt_operate_twt_config(phl_info, twt_cfg_i, PHL_GET_NEW_CONFIG, NULL, &config)) { PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "[TWT]alloc from new config, id = %d\n", config->twt_info.twt_id); alloc = true; } else { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "[TWT]fail to alloc new config\n"); pstatus = RTW_PHL_STATUS_RESOURCE; } } if (true == alloc) { *id = config->twt_info.twt_id; config->role = role; _twt_fill_config_info(&config->twt_info, &setup_info); if (benable) { pstatus = _twt_info_update(phl_info->hal, config, PHL_TWT_ACTION_ENABLE); if (RTW_PHL_STATUS_SUCCESS != pstatus) { /*todo*/ } } else { pstatus = RTW_PHL_STATUS_SUCCESS; } } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_alloc_twt_config(): pstatus:%d\n", pstatus); return pstatus; } /* * Free twt config entry by specific config ID * @id: id of twt config entry */ enum rtw_phl_status rtw_phl_twt_free_twt_config(void *phl, u8 id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config = NULL; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_free_twt_config()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_free_twt_config(): twt_sup == false\n"); return pstatus; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_free_twt_config(): twt_init == false\n"); return pstatus; } phl_twt_info = get_twt_info(phl_info); twt_cfg_i = &phl_twt_info->twt_cfg_info; if (RTW_PHL_STATUS_SUCCESS == _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_CONFIG_BY_ID, &id, &config)) { _twt_info_update(phl_info->hal, config, PHL_TWT_ACTION_DISABLE); _twt_delete_all_sta(phl_info, &config->twt_sta_queue); _twt_operate_twt_config(phl, twt_cfg_i, PHL_FREE_CONFIG, &id, NULL); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "[TWT]Free twt config success, id = %d\n", id); pstatus = RTW_PHL_STATUS_SUCCESS; } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_free_twt_config(): pstatus:%d\n", pstatus); return pstatus; } /* * Enable twt config by specific config id * @id: id of twt confi entry */ enum rtw_phl_status rtw_phl_twt_enable_twt_config(void *phl, u8 id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config = NULL; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_enable_twt_config()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_enable_twt_config(): twt_sup == false\n"); return pstatus; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_enable_twt_config(): twt_init == false\n"); return pstatus; } phl_twt_info = get_twt_info(phl_info); twt_cfg_i = &phl_twt_info->twt_cfg_info; if (RTW_PHL_STATUS_SUCCESS == _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_CONFIG_BY_ID, &id, &config)) { pstatus = _twt_info_update(phl_info->hal, config, PHL_TWT_ACTION_ENABLE); if (RTW_PHL_STATUS_SUCCESS == pstatus) { _twt_all_sta_update(phl_info, id, &config->twt_sta_queue, TWT_STA_ADD_MACID); } } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_enable_twt_config(): pstatus:%d, id = %d\n", pstatus, id); return pstatus; } /* * Free all twt config by specific role * @role: specific role for search twt config entry */ enum rtw_phl_status rtw_phl_twt_free_all_twt_by_role(void *phl, struct rtw_wifi_role_t *role) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config; u8 id; bool free = false; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_free_all_twt_by_role()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_free_all_twt_by_role(): twt_sup == false\n"); return pstatus; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_free_all_twt_by_role(): twt_init == false\n"); return pstatus; } phl_twt_info = get_twt_info(phl_info); twt_cfg_i = &phl_twt_info->twt_cfg_info; do { pstatus = _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_CONFIG_BY_ROLE, (u8 *)role, &config); if (RTW_PHL_STATUS_SUCCESS != pstatus) break; id = config->twt_info.twt_id; _twt_info_update(phl_info->hal, config, PHL_TWT_ACTION_DISABLE); _twt_delete_all_sta(phl_info, &config->twt_sta_queue); _twt_operate_twt_config(phl, twt_cfg_i, PHL_FREE_CONFIG, &id, NULL); free = true; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "Free twt config success, id = %d\n", id); } while(true); if (true == free) pstatus = RTW_PHL_STATUS_SUCCESS; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_free_all_twt_by_role(): pstatus:%d\n", pstatus); return pstatus; } /* * Pause all twt config by specific role * @role: specific role for search twt config entry */ enum rtw_phl_status rtw_phl_twt_disable_all_twt_by_role(void *phl, struct rtw_wifi_role_t *role) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config = NULL, *f_config = NULL; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_disable_all_twt_by_role()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_disable_all_twt_by_role(): twt_sup == false\n"); goto exit; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_disable_all_twt_by_role(): twt_init == false\n"); goto exit; } phl_twt_info = get_twt_info(phl_info); twt_cfg_i = &phl_twt_info->twt_cfg_info; if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_HEAD_CONFIG, NULL, &config)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_disable_all_twt_by_role(): Fail to get first allocate config\n"); goto exit; } f_config = config; do { PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "rtw_phl_twt_disable_all_twt_by_role(): while loop, twt_id:%d\n", config->twt_info.twt_id); if (twt_config_state_free == config->state) goto next_cfg; if (config->role == role) _twt_info_update(phl_info->hal, config, PHL_TWT_ACTION_DISABLE); next_cfg: if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_NEXT_CONFIG, (u8 *)&config->idx, &config)) goto exit; } while(config != f_config); pstatus = RTW_PHL_STATUS_SUCCESS; exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_disable_all_twt_by_role(): pstatus:%d\n", pstatus); return pstatus; } /* * Enable all twt config by specific role * @role: specific role for search twt config entry */ enum rtw_phl_status rtw_phl_twt_enable_all_twt_by_role(void *phl, struct rtw_wifi_role_t *role) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config = NULL, *f_config = NULL; bool error = false; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_enable_all_twt_by_role()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_enable_all_twt_by_role(): twt_sup == false\n"); goto exit; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_enable_all_twt_by_role(): twt_init == false\n"); goto exit; } phl_twt_info = get_twt_info(phl_info); twt_cfg_i = &phl_twt_info->twt_cfg_info; if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_HEAD_CONFIG, NULL, &config)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_enable_all_twt_by_role(): Fail to get first allocate config\n"); goto exit; } f_config = config; do { PHL_TRACE(COMP_PHL_TWT, _PHL_DEBUG_, "rtw_phl_twt_enable_all_twt_by_role(): while loop, twt_id:%d\n", config->twt_info.twt_id); if (twt_config_state_free == config->state) goto next_cfg; if (config->role != role) goto next_cfg; pstatus = _twt_info_update(phl_info->hal, config, PHL_TWT_ACTION_ENABLE); if (RTW_PHL_STATUS_SUCCESS == pstatus) { pstatus = _twt_all_sta_update(phl_info, config->twt_info.twt_id, &config->twt_sta_queue, TWT_STA_ADD_MACID); if (RTW_PHL_STATUS_SUCCESS != pstatus) { error = true; PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "rtw_phl_twt_enable_all_twt_by_role(): Fail to update all twt sta, twt_id:%d\n", config->twt_info.twt_id); } } else { error = true; PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "rtw_phl_twt_enable_all_twt_by_role(): Fail to enable twt config, twt_id:%d\n", config->twt_info.twt_id); } next_cfg: if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_NEXT_CONFIG, (u8 *)&config->idx, &config)) { error = true; break; } } while(config != f_config); if (true == error) pstatus = RTW_PHL_STATUS_FAILURE; exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_enable_all_twt_by_role(): pstatus:%d\n", pstatus); return pstatus; } /* * Add twt sta to specifi twt conig entry * @phl_sta: sta entry that you wnat to add in specifi twt conig entry * @config_id: id of target twt config entry * @id: twt flow id/ broadcast twt id */ enum rtw_phl_status rtw_phl_twt_add_sta_info(void *phl, struct rtw_phl_stainfo_t *phl_sta, u8 config_id, u8 id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config = NULL; struct phl_queue *sta_q = NULL; u16 cnt = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_add_sta_info()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_add_sta_info(): twt_sup == false\n"); goto fail; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_add_sta_info(): twt_init == false\n"); goto fail; } phl_twt_info = get_twt_info(phl_info); twt_cfg_i = &phl_twt_info->twt_cfg_info; if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_CONFIG_BY_ID, &config_id, &config)) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "Fail to get TWT config by id(%d)\n", config_id); goto fail; } sta_q = &config->twt_sta_queue; if (RTW_PHL_STATUS_SUCCESS != _twt_add_sta(phl, phl_sta, sta_q, id)) { goto fail; } if (RTW_PHL_STATUS_SUCCESS != _twt_sta_update(phl_info->hal, phl_sta->macid, config_id, TWT_STA_ADD_MACID)) { _twt_delete_sta(phl, sta_q, phl_sta, id, &cnt); goto fail; } pstatus = RTW_PHL_STATUS_SUCCESS; fail: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_add_sta_info(): pstatus:%d\n", pstatus); return pstatus; } /* * Remove all twt sta from twt config entry by specific sta entry * @phl_sta: sta entry that you wnat to remove * @bitmap: Output the bitmap. Indicate the statue that twt sta don't exist in the twt config entry that twt sta removed from it. * ex: Bitmap=10: We remove the twt sta from id 1, id 3 and other id of twt config entry, * but after remove, there are no twt sta existing in the twt config entry of id 1 and id 3. * ex: Bitmap=0: We remove the twt sta, after remove, there are at least one twt sta existing in the twt config entry that twt sta removed from it. */ enum rtw_phl_status rtw_phl_twt_delete_all_sta_info(void *phl, struct rtw_phl_stainfo_t *phl_sta, u8 *bitmap) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_delete_all_sta_info()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_delete_all_sta_info(): twt_sup == false\n"); return pstatus; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_delete_all_sta_info(): twt_init == false\n"); return pstatus; } pstatus = _twt_delete_sta_info(phl, phl_sta, true, 0, 0, bitmap); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_delete_all_sta_info(): pstatus:%d, bitmap:0x%x\n", pstatus, *bitmap); return pstatus; } /* * Remove twt sta when tx/rx twt teardown frame * @phl_sta: sta entry that you wnat to remove * @twt_flow: twt flow field info * @bitmap: Output the bitmap. Indicate the statue that twt sta don't exist in the twt config entry that twt sta removed from it. * ex: Bitmap=10: We remove the twt sta from id 1, id 3 and other id of twt config entry, * but after remove, there are no twt sta existing in the twt config entry of id 1 and id 3. * ex: Bitmap=0: We remove the twt sta, after remove, there are at least one twt sta existing in the twt config entry that twt sta removed from it. */ enum rtw_phl_status rtw_phl_twt_teardown_sta(void *phl, struct rtw_phl_stainfo_t *phl_sta, struct rtw_phl_twt_flow_field *twt_flow, u8 *bitmap) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; u8 id = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_teardown_sta()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_teardown_sta(): twt_sup == false\n"); return pstatus; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_teardown_sta(): twt_init == false\n"); return pstatus; } if (RTW_PHL_INDIV_TWT == twt_flow->nego_type || RTW_PHL_WAKE_TBTT_INR == twt_flow->nego_type) { if (twt_flow->info.twt_flow01.teardown_all) id = DELETE_ALL; else id = twt_flow->info.twt_flow01.twt_flow_id; pstatus = RTW_PHL_STATUS_SUCCESS; } else if (RTW_PHL_BCAST_TWT == twt_flow->nego_type ) { /*Todo*/ } else if (RTW_PHL_MANAGE_BCAST_TWT == twt_flow->nego_type) { if (twt_flow->info.twt_flow3.teardown_all) id = DELETE_ALL; else id = twt_flow->info.twt_flow3.bcast_twt_id; pstatus = RTW_PHL_STATUS_SUCCESS; } else { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "rtw_phl_twt_teardown_sta(): Unknown nego_type:%d\n", twt_flow->nego_type); } if (RTW_PHL_STATUS_SUCCESS == pstatus) { pstatus = _twt_delete_sta_info(phl, phl_sta, false, twt_flow->nego_type, id, bitmap); } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_teardown_sta(): pstatus:%d, twt_flow->nego_type:%d, id:%d, bitmap:0x%x\n", pstatus, twt_flow->nego_type, id, *bitmap); return pstatus; } /* * Assign new flow id for twt setup of sta. * @phl_sta: the specific sta * @role: specific role for search twt config entry * @id: Output: twt flow id * Note: for sta mode. */ enum rtw_phl_status rtw_phl_twt_get_new_flow_id(void *phl, struct rtw_phl_stainfo_t *phl_sta, u8 *id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_RESOURCE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config = NULL, *f_config = NULL; struct rtw_twt_sta_info *twt_sta = NULL; u8 use_map = 0, unuse_map = 0; u8 i = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_get_new_flow_id()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_get_new_flow_id(): twt_sup == false\n"); goto exit; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_get_new_flow_id(): twt_init == false\n"); goto exit; } phl_twt_info = get_twt_info(phl_info); twt_cfg_i = &phl_twt_info->twt_cfg_info; if (false == _twt_new_config_is_available(phl_info)) goto exit; if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_HEAD_CONFIG, NULL, &config)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_get_new_flow_id(): Fail to get first allocate config\n"); goto exit; } f_config = config; do { PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "rtw_phl_twt_get_new_flow_id(): while loop\n"); if (twt_config_state_free == config->state) goto next_cfg; if (config->role != phl_sta->wrole || RTW_PHL_INDIV_TWT != config->twt_info.nego_type) goto next_cfg; twt_sta = _twt_get_sta_info(phl_info, &config->twt_sta_queue, phl_sta); if (NULL != twt_sta) { use_map |= (1 << twt_sta->id); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "rtw_phl_twt_get_new_flow_id(): config_ID:%d, get match sta, twt_sta->id:%d\n", config->twt_info.twt_id, twt_sta->id); } next_cfg: if (RTW_PHL_STATUS_SUCCESS != _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_NEXT_CONFIG, (u8 *)&config->idx, &config)) goto exit; } while(config != f_config); unuse_map = (~use_map) & 0xFF; i = 0; while ((unuse_map >> i) > 0) { if ((unuse_map >> i) & BIT0) { *id = i; pstatus = RTW_PHL_STATUS_SUCCESS; break; } i++; } exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_get_new_flow_id(): pstatus:%d, use_map:%d, unuse_map:%d, new_flow_id:%d\n", pstatus, use_map, unuse_map, *id); return pstatus; } /* * get target wake time * @port: port num of role * @id: reference id of twt configuration * @offset: unit: ms. An amount of time that you will start TWT from now * @tsf_h: return high 4-byte value of target wake time * @tsf_l: return low 4-byte value of target wake time */ enum rtw_phl_status rtw_phl_twt_get_target_wake_time(void *phl, u8 port, u8 id, u16 offset, u32 *tsf_h, u32 *tsf_l) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_twt_cfg_info *twt_cfg_i = NULL; struct phl_twt_config *config = NULL; u32 c_tsf_l = 0, c_tsf_h = 0, intvl = 0; u64 cur_tsf = 0, tgt_tsf = 0, ref_tsf = 0, dif_tsf = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_get_target_wake_time()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_get_target_wake_time(): twt_sup == false\n"); goto exit; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_get_target_wake_time(): twt_init == false\n"); goto exit; } phl_twt_info = get_twt_info(phl_info); twt_cfg_i = &phl_twt_info->twt_cfg_info; hstatus = rtw_hal_get_tsf(phl_info->hal, port, &c_tsf_h, &c_tsf_l); if (RTW_HAL_STATUS_FAILURE == hstatus) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "rtw_phl_twt_get_target_wake_time(): Fail to get tsf, hstatus:%d, port:%d\n", hstatus, port); goto exit; } cur_tsf = c_tsf_h; cur_tsf = cur_tsf << 32; cur_tsf |= c_tsf_l; if (IGNORE_CFG_ID == id) { tgt_tsf = _os_add64(cur_tsf, offset * 1000); } else { pstatus = _twt_operate_twt_config(phl, twt_cfg_i, PHL_GET_CONFIG_BY_ID, &id, &config); if (RTW_PHL_STATUS_SUCCESS != pstatus) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "rtw_phl_twt_get_target_wake_time(): Fail to get twt entry by id, pstatus:%d, id:%d\n", pstatus, id); goto exit; } ref_tsf = config->twt_info.target_wake_time_h; ref_tsf = ref_tsf << 32; ref_tsf |= config->twt_info.target_wake_time_l; intvl = _twt_calc_intvl(config->twt_info.twt_wake_int_exp, config->twt_info.twt_wake_int_mantissa); tgt_tsf = _os_add64(cur_tsf, offset * 1000); dif_tsf = _os_minus64(tgt_tsf, ref_tsf); tgt_tsf = _os_minus64(tgt_tsf, _os_modular64(dif_tsf, intvl)); tgt_tsf = _os_add64(tgt_tsf, intvl); } *tsf_h = (u32)(tgt_tsf >> 32); *tsf_l = (u32)(tgt_tsf); pstatus = RTW_PHL_STATUS_SUCCESS; exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_get_target_wake_time(): pstatus(%d), port:%d, twt_id:%d, offset:0x%08x, tsf_h: 0x%08X, tsf_l: 0x%08X\n", pstatus, port, id, offset, *tsf_h, *tsf_l); return pstatus; } /* * Fill twt element * @twt_ele: twt element info * @buf: fill memory * @len: the length of twt element */ enum rtw_phl_status rtw_phl_twt_fill_twt_element( struct rtw_phl_twt_element *twt_ele, u8 *buf, u8 *len) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; u8 twt_para_length = 0; struct rtw_phl_twt_control *twt_ctrl = NULL; if (twt_ele == NULL || buf == NULL || len == NULL) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_fill_twt_element(): twt_ele or buf or len = NULL\n"); return pstatus; } twt_ctrl = &twt_ele->twt_ctrl; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_fill_twt_element(): twt_ctrl: ndp_paging_indic(%d), responder_pm_mode(%d), nego_type(%d), twt_info_frame_disable(%d), wake_dur_unit(%d)\n", twt_ctrl->ndp_paging_indic, twt_ctrl->responder_pm_mode, twt_ctrl->nego_type, twt_ctrl->twt_info_frame_disable, twt_ctrl->wake_dur_unit); *len = 0; /*Control filed*/ SET_TWT_CONTROL_NDP_PAGING_INDICATOR(buf, twt_ctrl->ndp_paging_indic); SET_TWT_CONTROL_RESPONDER_PM_MODE(buf, twt_ctrl->responder_pm_mode); SET_TWT_CONTROL_NEGOTIATION_TYPE(buf, twt_ctrl->nego_type); SET_TWT_CONTROL_TWT_INFORMATION_FRAME_DISABLE(buf, twt_ctrl->twt_info_frame_disable); SET_TWT_CONTROL_WAKE_DURATION_UNIT(buf, twt_ctrl->wake_dur_unit); *len += CONTROL_LENGTH; /*TWT Parameter Information*/ if (RTW_PHL_INDIV_TWT == twt_ctrl->nego_type) { pstatus = _twt_fill_individual_twt_para_set( &twt_ele->info.i_twt_para_set, twt_ctrl->ndp_paging_indic, buf + *len, &twt_para_length); *len += twt_para_length; } else { /*todo*/ PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_fill_twt_element(): not support, todo, twt_ctrl->nego_type(%d)\n", twt_ctrl->nego_type); } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_fill_twt_element()\n"); return pstatus; } /* * Fill twt flow field of TWT teardown frame * @twt_flow: twt flow field info * @buf: fill memory * @length: the length of twt flow field */ enum rtw_phl_status rtw_phl_twt_fill_flow_field( struct rtw_phl_twt_flow_field *twt_flow, u8 *buf, u16 *length) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_fill_flow_field()\n"); if (twt_flow == NULL || buf == NULL || length == NULL) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_fill_flow_field(): twt_flow or buf or length = NULL\n"); return pstatus; } *length = 0; if (RTW_PHL_INDIV_TWT == twt_flow->nego_type || RTW_PHL_WAKE_TBTT_INR == twt_flow->nego_type) { struct rtw_phl_twt_flow_type01 *flow_info = &twt_flow->info.twt_flow01; SET_TWT_FLOW_ID(buf, flow_info->twt_flow_id); SET_NEGOTIATION_TYPE(buf, twt_flow->nego_type); SET_TEARDOWN_ALL_TWT(buf, flow_info->teardown_all); *length = TWT_FLOW_FIELD_LENGTH; pstatus = RTW_PHL_STATUS_SUCCESS; } else if (RTW_PHL_BCAST_TWT == twt_flow->nego_type) { SET_NEGOTIATION_TYPE(buf, twt_flow->nego_type); *length = TWT_FLOW_FIELD_LENGTH; pstatus = RTW_PHL_STATUS_SUCCESS; } else if (RTW_PHL_MANAGE_BCAST_TWT == twt_flow->nego_type) { struct rtw_phl_twt_flow_type3 *flow_info = &twt_flow->info.twt_flow3; SET_BROADCAST_TWT_ID(buf, flow_info->bcast_twt_id); SET_NEGOTIATION_TYPE(buf, twt_flow->nego_type); SET_TEARDOWN_ALL_TWT(buf, flow_info->teardown_all); *length = TWT_FLOW_FIELD_LENGTH; pstatus = RTW_PHL_STATUS_SUCCESS; } else { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_fill_flow_field(): Unknown type(%d)\n", twt_flow->nego_type); } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_fill_flow_field()\n"); return pstatus; } /* * Parse twt element from pkt * @twt_ele: the address of twt elemant * @length: length of pkt * @twt_element: Parse info */ enum rtw_phl_status rtw_phl_twt_parse_element(u8 *twt_ele, u16 length, struct rtw_phl_twt_element *twt_element) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct rtw_phl_twt_control *twt_ctrl = NULL; u8 ele_len = 0, ele_id = 0; u8 *next_buf = twt_ele; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_parse_element()\n"); if (twt_ele == NULL || twt_element == NULL) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_parse_element(): twt_ele or twt_element = NULL\n"); return pstatus; } twt_ctrl = &twt_element->twt_ctrl; if (length < (MIN_TWT_ELE_LEN + ELEM_ID_LEN + ELEM_LEN_LEN)) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "rtw_phl_twt_parse_element(): error buffer length(%d) < %d\n", length, (MIN_TWT_ELE_LEN + ELEM_ID_LEN + ELEM_LEN_LEN)); goto exit; } ele_id = GET_ELE_ID(next_buf); next_buf += ELEM_ID_LEN; ele_len = GET_ELE_LEN(next_buf); next_buf += ELEM_LEN_LEN; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "rtw_phl_twt_parse_element(): ele_id:%d, ele_len:%d, length:%d\n", ele_id, ele_len, length); if (ele_len < MIN_TWT_ELE_LEN) { PHL_TRACE(COMP_PHL_TWT, _PHL_WARNING_, "rtw_phl_twt_parse_element(): error ele length(%d) < %d\n", ele_len, MIN_TWT_ELE_LEN); goto exit; } twt_ctrl->ndp_paging_indic = GET_TWT_CONTROL_NDP_PAGING_INDICATOR(next_buf); twt_ctrl->responder_pm_mode = GET_TWT_CONTROL_RESPONDER_PM_MODE(next_buf); twt_ctrl->nego_type = GET_TWT_CONTROL_NEGOTIATION_TYPE(next_buf); twt_ctrl->twt_info_frame_disable = GET_TWT_CONTROL_TWT_INFORMATION_FRAME_DISABLE(next_buf); twt_ctrl->wake_dur_unit = GET_TWT_CONTROL_WAKE_DURATION_UNIT(next_buf); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "rtw_phl_twt_parse_element(): twt_ctrl: ndp_paging_indic(%d), responder_pm_mode(%d), nego_type(%d), twt_info_frame_disable(%d), wake_dur_unit(%d)\n", twt_ctrl->ndp_paging_indic, twt_ctrl->responder_pm_mode, twt_ctrl->nego_type, twt_ctrl->twt_info_frame_disable, twt_ctrl->wake_dur_unit); if (RTW_PHL_INDIV_TWT == twt_ctrl->nego_type) { pstatus = _twt_parse_individual_twt_para(twt_ele, length, twt_element); } else { /*todo*/ PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_parse_element(): not support, todo, twt_ctrl->nego_type(%d)\n", twt_ctrl->nego_type); } exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_parse_element(): pstatus(%d)\n", pstatus); return pstatus; } /* * Parse twt setup info from pkt * @pkt: the address of Category of twt setup info frame * @length: length of pkt * @twt_setup_info: Parse info */ enum rtw_phl_status rtw_phl_twt_parse_setup_info(u8 *pkt, u16 length, struct rtw_phl_twt_setup_info *setup_info) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; u8 *twt_ele = NULL; u16 ele_length = length - TOKEN_OFFSET- TOKEN_LENGTH; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_parse_setup_info()\n"); if (pkt == NULL || setup_info == NULL) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_parse_setup_info(): pkt or setup_info = NULL\n"); return pstatus; } twt_ele = pkt + TOKEN_OFFSET + TOKEN_LENGTH; setup_info->dialog_token = GET_DIALOG_TOKEN(pkt + TOKEN_OFFSET); pstatus = rtw_phl_twt_parse_element(twt_ele, ele_length, &setup_info->twt_element); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_parse_setup_info(): pstatus(%d)\n", pstatus); return pstatus; } /* * Parse twt twt flow field from twt teardown frame * @pkt: the address of twt flow field * @length: length of pkt * @twt_flow_info: Parse info */ enum rtw_phl_status rtw_phl_twt_parse_flow_field(u8 *pkt, u16 length, struct rtw_phl_twt_flow_field *twt_flow) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_parse_flow_field()\n"); if (pkt == NULL || twt_flow == NULL) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_parse_flow_field(): pkt or twt_flow = NULL\n"); return pstatus; } twt_flow->nego_type = GET_NEGOTIATION_TYPE(pkt); if (RTW_PHL_INDIV_TWT == twt_flow->nego_type || RTW_PHL_WAKE_TBTT_INR == twt_flow->nego_type) { struct rtw_phl_twt_flow_type01 *flow_info = &twt_flow->info.twt_flow01; flow_info->twt_flow_id = GET_TWT_FLOW_ID(pkt); flow_info->teardown_all = GET_TEARDOWN_ALL_TWT(pkt); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "rtw_phl_twt_parse_flow_field(): nego_type:%d, twt_flow_id:%d, teardown_all:%d\n", twt_flow->nego_type, flow_info->twt_flow_id, flow_info->teardown_all); pstatus = RTW_PHL_STATUS_SUCCESS; } else if (RTW_PHL_BCAST_TWT == twt_flow->nego_type) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_parse_flow_field(): not support, todo, twt_flow->nego_type(%d)\n", twt_flow->nego_type); } else if (RTW_PHL_MANAGE_BCAST_TWT == twt_flow->nego_type) { struct rtw_phl_twt_flow_type3 *flow_info = &twt_flow->info.twt_flow3; flow_info->bcast_twt_id = GET_BROADCAST_TWT_ID(pkt); flow_info->teardown_all = GET_TEARDOWN_ALL_TWT(pkt); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "rtw_phl_twt_parse_flow_field(): nego_type:%d, bcast_twt_id:%d, teardown_all:%d\n", twt_flow->nego_type, flow_info->bcast_twt_id, flow_info->teardown_all); pstatus = RTW_PHL_STATUS_SUCCESS; } else { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_parse_flow_field(): Unknown type, twt_flow->nego_type(%d)\n", twt_flow->nego_type); } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_parse_flow_field(): pstatus(%d)\n", pstatus); return pstatus; } /* * Tell fw which macid is announced in awake state * @macid: macid of sta that is in awake state */ enum rtw_phl_status rtw_phl_twt_sta_announce_to_fw(void *phl, u16 macid) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_twt_info *phl_twt_info = NULL; struct phl_queue *annc_q = NULL; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_sta_announce_to_fw()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_sta_announce_to_fw(): twt_sup == false\n"); return pstatus; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_sta_announce_to_fw(): twt_init == false\n"); return pstatus; } phl_twt_info = get_twt_info(phl_info); annc_q = &phl_twt_info->twt_annc_queue; pstatus = _twt_sta_announce(phl_info, annc_q, macid); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_sta_announce_to_fw(): pstatus:%d, macid: %d\n", pstatus, macid); return pstatus; } #if 0 /* * Handle twt c2h * @c: c2h content */ enum rtw_phl_status rtw_phl_twt_handle_c2h(void *phl_com, void *c) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct rtw_phl_com_t *phl_com_info= (struct rtw_phl_com_t *)phl_com; struct phl_info_t *phl_info = (struct phl_info_t*)phl_com_info->phl_priv; struct rtw_c2h_info *c2h = (struct rtw_c2h_info *)c; if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_handle_c2h(): twt_sup == false\n"); return pstatus; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_handle_c2h(): twt_init == false\n"); return pstatus; } if (C2H_FUN_WAIT_ANNC == c2h->c2h_func) { pstatus = _twt_handle_c2h_wait_annc(phl_info, c2h->content); } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "rtw_phl_twt_handle_c2h(): pstatus:%d cat:%d, class:%d, func:%d, len:%d, content:0x%x\n", pstatus, c2h->c2h_cat, c2h->c2h_class, c2h->c2h_func, c2h->content_len, *(c2h->content)); return pstatus; } #endif /* * Handle sta to config twt when sta accept the twt agreement * @phl_sta: sta entry that you wnat to config twt * @setup_info: twt setup info * @id: Output the id of twt confi entry * Note: for sta mode */ enum rtw_phl_status rtw_phl_twt_accept_for_sta_mode(void *phl, struct rtw_phl_stainfo_t *phl_sta, struct rtw_phl_twt_setup_info *setup_info, u8 *id) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct rtw_phl_twt_element *element = &setup_info->twt_element; struct rtw_phl_twt_control *twt_ctrl =&element->twt_ctrl; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_accept_for_sta_mode()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_accept_for_sta_mode(): twt_sup == false\n"); return pstatus; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_accept_for_sta_mode(): twt_init == false\n"); return pstatus; } if (RTW_PHL_INDIV_TWT == twt_ctrl->nego_type) { pstatus = _twt_accept_indiv_by_sta(phl_info, setup_info, phl_sta, id); } else { pstatus = _twt_accept_bcast_by_sta(phl_info, setup_info, phl_sta, id); } PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_accept_for_sta_mode(): pstatus:%d, config_id:%d\n", pstatus, *id); return pstatus; } /* * Handle sta to disable twt when sta tx/rx twt teardown frame * @phl_sta: sta entry that you wnat to config twt * @twt_flow: twt flow field info * Note: for sta mode. */ enum rtw_phl_status rtw_phl_twt_teardown_for_sta_mode(void *phl, struct rtw_phl_stainfo_t *phl_sta, struct rtw_phl_twt_flow_field *twt_flow) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; u8 bitmap =0; /*bitmap of empty config of twt*/ u8 i = 0; PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "==> rtw_phl_twt_teardown_for_sta_mode()\n"); if (false == twt_sup(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_teardown_for_sta_mode(): twt_sup == false\n"); goto exit; } if (false == twt_init(phl_info)) { PHL_TRACE(COMP_PHL_TWT, _PHL_ERR_, "rtw_phl_twt_teardown_for_sta_mode(): twt_init == false\n"); goto exit; } pstatus = rtw_phl_twt_teardown_sta(phl, phl_sta, twt_flow, &bitmap); if (RTW_PHL_STATUS_SUCCESS != pstatus) goto exit; if (bitmap == 0) goto exit; i = 0; do { if (((bitmap >> i) & BIT0)) { pstatus = rtw_phl_twt_free_twt_config(phl, i); PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "rtw_phl_twt_teardown_for_sta_mode():sta Q is empty in twt config entry(%d), we free it, pstatus:%d \n", i, pstatus); } i++; } while ((bitmap >> i) != 0); exit: PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "<== rtw_phl_twt_teardown_for_sta_mode(): pstatus(%d)\n", pstatus); return pstatus; } #endif /* CONFIG_PHL_TWT */