linuxOS_AP06/external/rkwifibt/drivers/infineon/wl_twt.c
2025-06-03 12:28:32 +08:00

770 lines
21 KiB
C
Executable File

/*
* Target Wake Time Module which is responsible for acting as an
* interface between the userspace and firmware.
*
* Portions of this code are copyright (c) 2023 Cypress Semiconductor Corporation,
* an Infineon company
*
* This program is the proprietary software of infineon and/or
* its licensors, and may only be used, duplicated, modified or distributed
* pursuant to the terms and conditions of a separate, written license
* agreement executed between you and infineon (an "Authorized License").
* Except as set forth in an Authorized License, infineon grants no license
* (express or implied), right to use, or waiver of any kind with respect to
* the Software, and infineon expressly reserves all rights in and to the
* Software and all intellectual property rights therein. IF YOU HAVE NO
* AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS SOFTWARE IN ANY
* WAY, AND SHOULD IMMEDIATELY NOTIFY INFINEON AND DISCONTINUE ALL USE OF
* THE SOFTWARE.
*
* Except as expressly set forth in the Authorized License,
*
* 1. This program, including its structure, sequence and organization,
* constitutes the valuable trade secrets of infineon, and you shall use
* all reasonable efforts to protect the confidentiality thereof, and to
* use this information only in connection with your use of infineon
* integrated circuit products.
*
* 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
* "AS IS" AND WITH ALL FAULTS AND INFINEON MAKES NO PROMISES,
* REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR
* OTHERWISE, WITH RESPECT TO THE SOFTWARE. INFINEON SPECIFICALLY
* DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
* NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
* ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
* CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
* OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
*
* 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL
* INFINEON OR ITS LICENSORS BE LIABLE FOR (i) CONSEQUENTIAL, INCIDENTAL,
* SPECIAL, INDIRECT, OR EXEMPLARY DAMAGES WHATSOEVER ARISING OUT OF OR
* IN ANY WAY RELATING TO YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN
* IF INFINEON HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR (ii)
* ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE SOFTWARE ITSELF
* OR U.S. $1, WHICHEVER IS GREATER. THESE LIMITATIONS SHALL APPLY
* NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
*
*
* <<Infineon-WL-IPTag/Open:>>
*
* $Id$
*/
#ifdef WL11AX
#include "wl_twt.h"
static DEFINE_SPINLOCK(twt_session_list_lock);
/*
* Nominal Minimum Wake Duration derivation from Wake Duration
*/
inline void
wl_twt_wake_dur_to_min_twt(uint32 wake_dur, uint8 *min_twt, uint8 *min_twt_unit)
{
if(*min_twt_unit == 1) {
/*
* If min_twt_unit is 1, then min_twt is
* in units of TUs (i.e) 102400 usecs.
*/
*min_twt = wake_dur / 102400;
} else if(*min_twt_unit == 0) {
/*
* If min_twt_unit is 0, then min_twt is
* in units of 256 usecs.
*/
*min_twt = wake_dur / 256;
} else {
/* Invalid min_twt */
*min_twt = 0;
}
}
/*
* Wake Duration derivation from Nominal Minimum Wake Duration
*/
static inline uint32
wl_twt_min_twt_to_wake_dur(uint8 min_twt, uint8 min_twt_unit)
{
uint32 wake_dur;
if (min_twt_unit == 1) {
/*
* If min_twt_unit is 1, then min_twt is
* in units of TUs (i.e) 102400 usecs.
*/
wake_dur = (uint32)min_twt * 102400;
} else if (min_twt_unit == 0) {
/*
* If min_twt_unit is 0, then min_twt is
* in units of 256 usecs.
*/
wake_dur = (uint32)min_twt * 256;
} else {
/* Invalid min_twt */
wake_dur = 0;
}
return wake_dur;
}
/*
* Wake Interval Mantissa & Exponent derivation from Wake Interval
*/
inline void
wl_twt_uint32_to_float(uint32 val, uint8 *exp, uint16 *mant)
{
uint8 lzs = (uint8)__builtin_clz(val); /* leading 0's */
uint8 shift = lzs < 16 ? 16 - lzs : 0;
*mant = (uint16)(val >> shift);
*exp = shift;
}
/*
* Wake Interval derivation from Wake Interval Mantissa & Exponent
*/
static inline uint32
wl_twt_float_to_uint32(uint8 exponent, uint16 mantissa)
{
return (uint32)mantissa << exponent;
}
wl_twt_session_t* wl_twt_lookup_session_by_flow_id(struct list_head *twt_session_list,
uint8 flow_id)
{
wl_twt_session_t *iter = NULL;
list_for_each_entry(iter, twt_session_list, list) {
if (iter->twt_param.negotiation_type != IFX_TWT_PARAM_NEGO_TYPE_ITWT)
continue;
if (iter->twt_param.flow_id == flow_id)
return iter;
}
return NULL;
}
int wl_twt_add_session_to_list(struct list_head *twt_session_list, dhd_pub_t *dhd,
uint8 ifidx, wl_twt_param_t twt_param)
{
int ret = BCME_OK;
wl_twt_session_t *new_twt_session;
new_twt_session = (wl_twt_session_t *)MALLOCZ(dhd->osh,
sizeof(wl_twt_session_t));
if (!new_twt_session) {
WL_ERR(("TWT: Failed to alloc memory for new session"));
ret = BCME_NOMEM;
goto exit;
}
new_twt_session->ifidx = ifidx;
new_twt_session->twt_param = twt_param;
new_twt_session->state = TWT_SESSION_SETUP_COMPLETE;
spin_lock_bh(&twt_session_list_lock);
WL_INFORM(("TWT: Adding TWT session with flow ID: %d", twt_param.flow_id));
list_add_tail(&new_twt_session->list, twt_session_list);
exit:
spin_unlock_bh(&twt_session_list_lock);
return ret;
}
int wl_twt_del_session_by_flow_id(struct list_head *twt_session_list, uint8 flow_id)
{
int ret = BCME_OK;
wl_twt_session_t *twt_session = NULL;
spin_lock_bh(&twt_session_list_lock);
twt_session = wl_twt_lookup_session_by_flow_id(twt_session_list, flow_id);
if (twt_session) {
WL_INFORM(("TWT: Deleting TWT session with flow ID: %d", flow_id));
list_del(&twt_session->list);
kfree(twt_session);
} else {
WL_INFORM(("TWT: TWT session with flow ID: %d is not found to be deleted",
flow_id));
ret = -1;
goto exit;
}
exit:
spin_unlock_bh(&twt_session_list_lock);
return ret;
}
int wl_twt_count_session(struct list_head *twt_session_list)
{
wl_twt_session_t *twt_session = NULL;
int ct = 0;
list_for_each_entry(twt_session, twt_session_list, list) {
if (twt_session->twt_param.negotiation_type ==
IFX_TWT_PARAM_NEGO_TYPE_ITWT)
ct++;
}
return ct;
}
void wl_twt_flush_session_list(struct list_head *twt_session_list, u8 ifidx)
{
wl_twt_session_t *entry = NULL, *next = NULL;
spin_lock_bh(&twt_session_list_lock);
list_for_each_entry_safe(entry, next, twt_session_list, list) {
if ((ifidx != 0xFF) &&
(ifidx != entry->ifidx))
continue;
WL_INFORM(("TWT: Deleting TWT session with flow ID: %d",
entry->twt_param.flow_id));
list_del(&entry->list);
kfree(entry);
}
spin_unlock_bh(&twt_session_list_lock);
}
int wl_twt_cleanup_session_records(dhd_pub_t *dhd, u8 ifidx)
{
wl_twt_ctx_t *twt_ctx = (wl_twt_ctx_t *)dhd->twt_ctx;
int ret = BCME_OK;
NULL_CHECK(twt_ctx,
"TWT: Failed to cleanup session records, Module not initialized",
ret);
wl_twt_flush_session_list(&twt_ctx->twt_session_list, ifidx);
return ret;
}
int wl_twt_setup(wl_twt_ctx_t *twt_ctx, struct wireless_dev *wdev,
wl_twt_param_t twt_param)
{
wl_twt_setup_t val;
s32 bw = BCME_OK;
u8 mybuf[WLC_IOCTL_SMLEN] = {0};
u8 resp_buf[WLC_IOCTL_SMLEN] = {0};
uint8 *rem = mybuf;
uint16 rem_len = sizeof(mybuf);
wl_twt_session_t *twt_session = NULL;
bzero(&val, sizeof(val));
val.version = WL_TWT_SETUP_VER;
val.length = sizeof(val.version) + sizeof(val.length);
/* Default values, Override Below */
val.desc.flow_flags = 0x0;
val.desc.wake_dur = 0xFFFFFFFF;
val.desc.wake_int = 0xFFFFFFFF;
val.desc.wake_int_max = 0xFFFFFFFF;
/* TWT Negotiation_type */
val.desc.negotiation_type = (uint8)twt_param.negotiation_type;
switch (val.desc.negotiation_type) {
case IFX_TWT_PARAM_NEGO_TYPE_ITWT:
/* Flow ID */
val.desc.flow_id = twt_param.flow_id;
if (val.desc.flow_id == 0xFF) {
/* Let the FW choose the Flow ID */
break;
}
/* Lookup the active session list for the requested flow ID */
twt_session =
wl_twt_lookup_session_by_flow_id(&twt_ctx->twt_session_list,
twt_param.flow_id);
if (twt_session) {
WL_ERR(("TWT: Setup REQ: flow ID: %d is already active",
twt_param.flow_id));
bw = BCME_ERROR;
goto exit;
}
break;
case IFX_TWT_PARAM_NEGO_TYPE_BTWT:
/* Broadcast TWT ID */
val.desc.bid = twt_param.bcast_twt_id;
/* TODO: Handle the Broadcast TWT Setup REQ */
/* FALLTHRU */
default:
WL_ERR(("TWT: Setup REQ: Negotiation Type %d not handled",
twt_param.negotiation_type));
bw = BCME_UNSUPPORTED;
goto exit;
}
/* Setup command */
val.desc.setup_cmd = twt_param.setup_cmd;
/* Flow flags */
val.desc.flow_flags |= ((twt_param.negotiation_type & 0x02) >> 1 ?
WL_TWT_FLOW_FLAG_BROADCAST : 0);
val.desc.flow_flags |= (twt_param.implicit ? WL_TWT_FLOW_FLAG_IMPLICIT : 0);
val.desc.flow_flags |= (twt_param.flow_type ? WL_TWT_FLOW_FLAG_UNANNOUNCED : 0);
val.desc.flow_flags |= (twt_param.trigger ? WL_TWT_FLOW_FLAG_TRIGGER : 0);
val.desc.flow_flags |= ((twt_param.negotiation_type & 0x01) ?
WL_TWT_FLOW_FLAG_WAKE_TBTT_NEGO : 0);
val.desc.flow_flags |= (twt_param.requestor ? WL_TWT_FLOW_FLAG_REQUEST : 0);
val.desc.flow_flags |= (twt_param.protection ? WL_TWT_FLOW_FLAG_PROTECT : 0);
if (twt_param.twt) {
/* Target Wake Time parameter */
val.desc.wake_time_h = htod32((uint32)(twt_param.twt >> 32));
val.desc.wake_time_l = htod32((uint32)(twt_param.twt));
val.desc.wake_type = WL_TWT_TIME_TYPE_BSS;
} else if (twt_param.twt_offset) {
/* Target Wake Time offset parameter */
val.desc.wake_time_h = htod32((uint32)(twt_param.twt_offset >> 32));
val.desc.wake_time_l = htod32((uint32)(twt_param.twt_offset));
val.desc.wake_type = WL_TWT_TIME_TYPE_OFFSET;
} else {
/* Let the FW choose the Target Wake Time */
val.desc.wake_time_h = 0x0;
val.desc.wake_time_l = 0x0;
val.desc.wake_type = WL_TWT_TIME_TYPE_AUTO;
}
/* Wake Duration or Service Period */
val.desc.wake_dur = htod32(wl_twt_min_twt_to_wake_dur(twt_param.min_twt,
twt_param.min_twt_unit));
/* Wake Interval or Service Interval */
val.desc.wake_int = htod32(wl_twt_float_to_uint32(twt_param.exponent,
twt_param.mantissa));
bw = bcm_pack_xtlv_entry(&rem, &rem_len, WL_TWT_CMD_SETUP, sizeof(val),
(uint8 *)&val, BCM_XTLV_OPTION_ALIGN32);
if (bw != BCME_OK) {
WL_ERR(("TWT: Setup REQ: Failed to pack IOVAR, ret: %d", bw));
goto exit;
}
bw = wldev_iovar_setbuf(wdev_to_ndev(wdev), "twt", mybuf,
sizeof(mybuf) - rem_len, resp_buf,
WLC_IOCTL_SMLEN, NULL);
if (bw != BCME_OK) {
WL_ERR(("TWT: Setup REQ: Failed, ret: %d", bw));
goto exit;
}
WL_INFORM(("TWT: Setup REQ: Initiated\n"
"Setup command : %u\n"
"Flow flags : 0x %02x\n"
"Flow ID : %u\n"
"Broadcast TWT ID : %u\n"
"Wake Time H,L : 0x %08x %08x\n"
"Wake Type : %u\n"
"Wake Dururation : %u usecs\n"
"Wake Interval : %u usecs\n"
"Negotiation type : %u\n",
val.desc.setup_cmd,
val.desc.flow_flags,
val.desc.flow_id,
val.desc.bid,
val.desc.wake_time_h,
val.desc.wake_time_l,
val.desc.wake_type,
val.desc.wake_dur,
val.desc.wake_int,
val.desc.negotiation_type));
exit:
return bw;
}
int wl_twt_teardown(wl_twt_ctx_t *twt_ctx, struct wireless_dev *wdev,
wl_twt_param_t twt_param)
{
wl_twt_teardown_t val;
s32 bw = BCME_OK;
u8 mybuf[WLC_IOCTL_SMLEN] = {0};
u8 resp_buf[WLC_IOCTL_SMLEN] = {0};
uint8 *rem = mybuf;
uint16 rem_len = sizeof(mybuf);
wl_twt_session_t *twt_session = NULL;
bzero(&val, sizeof(val));
val.version = WL_TWT_TEARDOWN_VER;
val.length = sizeof(val.version) + sizeof(val.length);
/* TWT Negotiation_type */
val.teardesc.negotiation_type = (uint8)twt_param.negotiation_type;
switch (val.teardesc.negotiation_type) {
case IFX_TWT_PARAM_NEGO_TYPE_ITWT:
/* Teardown all Negotiated TWT */
val.teardesc.alltwt = twt_param.teardown_all_twt;
if (val.teardesc.alltwt) {
int sess_ct =
wl_twt_count_session(&twt_ctx->twt_session_list);
if (!sess_ct) {
WL_ERR(("TWT: Teardown REQ: No active sessions"));
bw = BCME_ERROR;
goto exit;
}
break;
}
/* Flow ID */
if (twt_param.flow_id >= 0 && twt_param.flow_id <= 0x7) {
val.teardesc.flow_id = twt_param.flow_id;
} else {
WL_ERR(("TWT: Teardown REQ: flow ID: %d is invalid",
twt_param.flow_id));
bw = BCME_ERROR;
goto exit;
}
/* Lookup the active session list for the same flow ID */
twt_session =
wl_twt_lookup_session_by_flow_id(&twt_ctx->twt_session_list,
twt_param.flow_id);
if (!twt_session) {
WL_ERR(("TWT: Teardown REQ: flow ID: %d is not active",
twt_param.flow_id));
bw = BCME_ERROR;
goto exit;
}
break;
case IFX_TWT_PARAM_NEGO_TYPE_BTWT:
/* Broadcast TWT ID */
val.teardesc.bid = twt_param.bcast_twt_id;
/* TODO: Handle the Broadcast TWT Teardown REQ */
/* FALLTHRU */
default:
WL_ERR(("TWT: Teardown REQ: Negotiation Type %d not handled",
twt_param.negotiation_type));
bw = BCME_UNSUPPORTED;
goto exit;
}
bw = bcm_pack_xtlv_entry(&rem, &rem_len, WL_TWT_CMD_TEARDOWN, sizeof(val),
(uint8 *)&val, BCM_XTLV_OPTION_ALIGN32);
if (bw != BCME_OK) {
WL_ERR(("TWT: Teardown REQ: Failed to pack IOVAR, ret: %d", bw));
goto exit;
}
bw = wldev_iovar_setbuf(wdev_to_ndev(wdev), "twt", mybuf,
sizeof(mybuf) - rem_len, resp_buf,
WLC_IOCTL_SMLEN, NULL);
if (bw != BCME_OK) {
WL_ERR(("TWT: Teardown REQ: Failed, ret: %d", bw));
goto exit;
}
WL_INFORM(("TWT: Teardown REQ: Initiated\n"
"Flow ID : %u\n"
"Broadcast TWT ID : %u\n"
"Negotiation type : %u\n"
"Teardown all TWT : %u\n",
val.teardesc.flow_id,
val.teardesc.bid,
val.teardesc.negotiation_type,
val.teardesc.alltwt));
exit:
return bw;
}
int wl_twt_oper(struct net_device *pri_ndev,
struct wireless_dev *wdev, wl_twt_param_t twt_param)
{
int ret = -1;
dhd_info_t *dhd_inf = *(dhd_info_t **)netdev_priv(pri_ndev);
dhd_pub_t *dhd = &dhd_inf->pub;
wl_twt_ctx_t *twt_ctx = NULL;
NULL_CHECK(dhd, "dhd is NULL", ret);
twt_ctx = (wl_twt_ctx_t *)dhd->twt_ctx;
NULL_CHECK(twt_ctx, "TWT: REQ: Failed, Module not initialized", ret);
switch (twt_param.twt_oper) {
case IFX_TWT_OPER_SETUP:
ret = wl_twt_setup(twt_ctx, wdev, twt_param);
break;
case IFX_TWT_OPER_TEARDOWN:
ret = wl_twt_teardown(twt_ctx, wdev, twt_param);
break;
default:
WL_ERR(("TWT: REQ: Requested operation %d not supported",
twt_param.twt_oper));
ret = BCME_UNSUPPORTED;
goto exit;
}
exit:
return ret;
}
int wl_twt_setup_event(wl_twt_ctx_t *twt_ctx, struct wireless_dev *wdev,
wl_event_msg_t *event, void *event_data)
{
wl_twt_setup_cplt_t *setup_complete;
wl_twt_sdesc_t *setup_desc;
wl_twt_param_t twt_param;
int ret = BCME_OK;
setup_complete = (wl_twt_setup_cplt_t *)event_data;
setup_desc = (wl_twt_sdesc_t *)(event_data + sizeof(wl_twt_setup_cplt_t));
/* TWT Negotiation_type */
twt_param.negotiation_type = setup_desc->negotiation_type;
switch (twt_param.negotiation_type) {
case IFX_TWT_PARAM_NEGO_TYPE_ITWT:
/* Flow ID */
twt_param.flow_id = setup_desc->flow_id;
break;
case IFX_TWT_PARAM_NEGO_TYPE_BTWT:
/* Broadcast TWT ID */
twt_param.bcast_twt_id = setup_desc->bid;
/* TODO: Handle the Broadcast TWT Setup Event */
/* FALLTHRU */
default:
WL_ERR(("TWT: Setup EVT: Negotiation Type %d not handled",
twt_param.negotiation_type));
ret = BCME_UNSUPPORTED;
goto exit;
}
/* Setup command */
if (setup_desc->setup_cmd != TWT_SETUP_CMD_ACCEPT_TWT) {
WL_ERR(("TWT: Setup EVT: Request not accepted by the AP"));
ret = BCME_ERROR;
goto exit;
}
twt_param.setup_cmd = setup_desc->setup_cmd;
/* Flow flags */
twt_param.implicit = (setup_desc->flow_flags & WL_TWT_FLOW_FLAG_IMPLICIT) ? 1 : 0;
twt_param.flow_type = (setup_desc->flow_flags & WL_TWT_FLOW_FLAG_UNANNOUNCED) ? 1 : 0;
twt_param.trigger = (setup_desc->flow_flags & WL_TWT_FLOW_FLAG_TRIGGER) ? 1 : 0;
twt_param.requestor = (setup_desc->flow_flags & WL_TWT_FLOW_FLAG_REQUEST) ? 1 : 0;
twt_param.protection = (setup_desc->flow_flags & WL_TWT_FLOW_FLAG_PROTECT) ? 1 : 0;
/* Target Wake Time */
twt_param.twt = dtoh64((uint64)setup_desc->wake_time_h << 32) |
dtoh64((uint64)setup_desc->wake_time_l);
/* Wake Duration or Service Period */
wl_twt_wake_dur_to_min_twt(htod32(setup_desc->wake_dur),
&twt_param.min_twt,
&twt_param.min_twt_unit);
/* Wake Interval or Service Interval */
wl_twt_uint32_to_float(dtoh32(setup_desc->wake_int),
&twt_param.exponent,
&twt_param.mantissa);
ret = wl_twt_add_session_to_list(&twt_ctx->twt_session_list,
twt_ctx->dhd, event->ifidx, twt_param);
if (ret) {
WL_ERR(("TWT: Setup EVT: Failed to add new session to list"));
goto exit;
}
WL_INFORM(("TWT: Setup EVT: Succeeded\n"
"Setup command : %u\n"
"Flow flags : 0x %02x\n"
"Flow ID : %u\n"
"Broadcast TWT ID : %u\n"
"Wake Time H,L : 0x %08x %08x\n"
"Wake Type : %u\n"
"Wake Dururation : %u usecs\n"
"Wake Interval : %u usecs\n"
"Negotiation type : %u\n",
setup_desc->setup_cmd,
setup_desc->flow_flags,
setup_desc->flow_id,
setup_desc->bid,
setup_desc->wake_time_h,
setup_desc->wake_time_l,
setup_desc->wake_type,
setup_desc->wake_dur,
setup_desc->wake_int,
setup_desc->negotiation_type));
exit:
return ret;
}
int wl_twt_teardown_event(wl_twt_ctx_t *twt_ctx, struct wireless_dev *wdev,
wl_event_msg_t *event, void *event_data)
{
dhd_pub_t *dhd = twt_ctx->dhd;
wl_twt_teardown_cplt_t *teardown_complete;
wl_twt_teardesc_t *teardown_desc;
wl_twt_param_t twt_param;
int ret = BCME_OK;
s32 ifidx = DHD_BAD_IF;
ifidx = dhd_net2idx(dhd->info, wdev_to_ndev(wdev));
if (ifidx == DHD_BAD_IF) {
ret = BCME_ERROR;
goto exit;
}
teardown_complete = (wl_twt_teardown_cplt_t *)event_data;
teardown_desc = (wl_twt_teardesc_t *)(event_data + sizeof(teardown_complete));
/* TWT Negotiation_type */
twt_param.negotiation_type = teardown_desc->negotiation_type;
/* Teardown all Negotiated TWT */
twt_param.teardown_all_twt = teardown_desc->alltwt;
if (twt_param.teardown_all_twt) {
wl_twt_flush_session_list(&twt_ctx->twt_session_list,
(u8)ifidx);
} else {
switch (twt_param.negotiation_type) {
case IFX_TWT_PARAM_NEGO_TYPE_ITWT:
/* Flow ID */
twt_param.flow_id = teardown_desc->flow_id;
ret = wl_twt_del_session_by_flow_id(&twt_ctx->twt_session_list,
twt_param.flow_id);
if (ret) {
WL_ERR(("TWT: Failed to del session from list"));
goto exit;
}
break;
case IFX_TWT_PARAM_NEGO_TYPE_BTWT:
/* Broadcast TWT ID */
twt_param.bcast_twt_id = teardown_desc->bid;
/* TODO */
/* FALLTHRU */
default:
WL_ERR(("TWT: Negotiation Type not handled\n"));
ret = BCME_UNSUPPORTED;
goto exit;
}
}
WL_INFORM(("TWT: Teardown EVT: Succeeded\n"
"Flow ID : %u\n"
"Broadcast TWT ID : %u\n"
"Negotiation type : %u\n"
"Teardown all TWT : %u\n",
teardown_desc->flow_id,
teardown_desc->bid,
teardown_desc->negotiation_type,
teardown_desc->alltwt));
exit:
return ret;
}
int wl_twt_event(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data)
{
int ret = BCME_OK;
wl_twt_ctx_t *twt_ctx = NULL;
uint32 event_type;
dhd_if_t *ifp = NULL;
struct net_device *ndev;
struct wireless_dev *wdev;
NULL_CHECK(dhd, "TWT: EVT: Failed, dhd is NULL", ret);
twt_ctx = (wl_twt_ctx_t *)dhd->twt_ctx;
NULL_CHECK(twt_ctx, "TWT: EVT: Failed, Module not initialized", ret);
ifp = dhd_get_ifp(dhd, event->ifidx);
NULL_CHECK(ifp, "TWT: EVT: Failed, ifp is NULL", ret);
ndev = ifp->net;
NULL_CHECK(ndev, "TWT: EVT: Failed, ndev is NULL", ret);
wdev = ndev_to_wdev(ndev);
NULL_CHECK(wdev, "TWT: EVT: Failed, wdev is NULL", ret);
NULL_CHECK(event_data, "TWT: EVT: Failed, event_data is NULL", ret);
event_type = ntoh32_ua((void *)&event->event_type);
switch(event_type) {
case WLC_E_TWT_SETUP:
ret = wl_twt_setup_event(twt_ctx, wdev, event, event_data);
if (ret) {
WL_ERR(("TWT: EVT: Failed to handle TWT Setup event"));
goto exit;
}
break;
case WLC_E_TWT_TEARDOWN:
ret = wl_twt_teardown_event(twt_ctx, wdev, event, event_data);
if (ret) {
WL_ERR(("TWT: EVT: Failed to handle TWT Teardown event"));
goto exit;
}
break;
default:
WL_ERR(("TWT: EVT: Received event %d not handeled",
event_type));
ret = BCME_UNSUPPORTED;
goto exit;
}
exit:
return ret;
}
int wl_twt_init(dhd_pub_t *dhd)
{
int ret = BCME_OK;
wl_twt_ctx_t *twt_ctx;
NULL_CHECK(dhd, "TWT: INIT: Failed, dhd is NULL", ret);
if (dhd->twt_ctx) {
WL_ERR(("TWT: INIT: Failed, Module already initialized"));
ret = BCME_ERROR;
goto exit;
}
dhd->twt_ctx = (wl_twt_ctx_t *)MALLOCZ(dhd->osh, sizeof(wl_twt_ctx_t));
if (dhd->twt_ctx == NULL) {
ret = BCME_NOMEM;
WL_ERR(("TWT: INIT: Failed to create TWT context"));
goto exit;
}
bzero(dhd->twt_ctx, sizeof(wl_twt_ctx_t));
twt_ctx = (wl_twt_ctx_t *)dhd->twt_ctx;
twt_ctx->dhd = dhd;
INIT_LIST_HEAD(&twt_ctx->twt_session_list);
WL_INFORM(("TWT: INIT: Module Initialized"));
exit:
return ret;
}
int wl_twt_deinit(dhd_pub_t *dhd)
{
int ret = BCME_OK;
wl_twt_ctx_t *twt_ctx;
NULL_CHECK(dhd, "TWT: DEINIT: Failed, dhd is NULL", ret);
twt_ctx = dhd->twt_ctx;
NULL_CHECK(twt_ctx,
"TWT: DEINIT: Failed, Module not Initialized to De-initialize", ret);
wl_twt_flush_session_list(&twt_ctx->twt_session_list, 0xFF);
kfree(twt_ctx);
dhd->twt_ctx = NULL;
WL_INFORM(("TWT: DEINIT: Module De-initialized"));
return ret;
}
#endif /* WL11AX */