/******************************************************************************
*
* 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 */