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