// SPDX-License-Identifier: GPL-2.0 /* * S1G handling * Copyright(c) 2020 Adapt-IP */ #include #include "ieee80211_i.h" #include "driver-ops.h" #include #include "host_rpu_umac_if.h" #include "ieee80211_i.h" void nl80211_process_twt_event(struct ieee80211_sub_if_data *sdata, struct ieee80211_twt_setup *twt, struct ieee80211_twt_params *twt_resp); void nl80211_process_twt_teardown_event(struct wiphy *wiphy, u8 flow_id, unsigned char reason); void umac_send_twt_del_info(void); void umac_send_twt_info(u8 neg_type, u8 flow_type, u8 flow_id, u8 wkup_exponent, __le16 mantissa, __le64 twt, __le16 min_twt_dur, u8 trigger_enable); void ieee80211_send_twt_setup_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_twt_setup *twt) { int len = IEEE80211_MIN_ACTION_SIZE + 4 + twt->length; struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; struct sk_buff *skb; skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = skb_put_zero(skb, len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); memcpy(mgmt->da, sdata->vif.bss_conf.bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->vif.bss_conf.bssid, ETH_ALEN); mgmt->u.action.category = WLAN_CATEGORY_S1G; mgmt->u.action.u.s1g_twt.action_code = WLAN_S1G_TWT_SETUP; memcpy(mgmt->u.action.u.s1g_twt.variable, twt, 3 + twt->length); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_INTFL_MLME_CONN_TX | IEEE80211_TX_CTL_REQ_TX_STATUS; ieee80211_tx_skb(sdata, skb); } void ieee80211_s1g_send_twt_teardown(struct ieee80211_sub_if_data *sdata, u8 flowid) { struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; struct sk_buff *skb; u8 *id; skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_MIN_ACTION_SIZE + 2); if (!skb) return; skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = skb_put_zero(skb, IEEE80211_MIN_ACTION_SIZE + 2); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); memcpy(mgmt->da, sdata->vif.bss_conf.bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->vif.bss_conf.bssid, ETH_ALEN); mgmt->u.action.category = WLAN_CATEGORY_S1G; mgmt->u.action.u.s1g_twt.action_code = WLAN_S1G_TWT_TEARDOWN; id = (u8 *)mgmt->u.action.u.s1g_twt.variable; *id = flowid; IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_CTL_REQ_TX_STATUS; ieee80211_tx_skb(sdata, skb); } void img_umac_process_twt_response(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; struct ieee80211_twt_setup *twt = (struct ieee80211_twt_setup *)mgmt->u.action.u.s1g_twt.variable; struct ieee80211_twt_params *twt_resp = (void *)twt->params; struct wireless_dev *wdev = &sdata->wdev; struct wiphy *wiphy = wdev->wiphy; unsigned int twt_setup_type = 0; u8 neg_type = 0; u8 flow_type = 0, trig_enable = 0; u8 wkup_exponent = 0, flow_id = 0; unsigned int exponent_interval = 0, wakeup_interval = 0; unsigned int awake_duration = 0; /* twt_response event to host */ nl80211_process_twt_event(sdata, twt, twt_resp); /* twt response info to lmac */ twt_setup_type = ((twt_resp->req_type>>1) & 0x7); if (twt_setup_type != NRF_WIFI_REJECT_TWT) { neg_type = ((twt->control>>2) & 0x3); if (twt_resp->req_type & IEEE80211_TWT_REQTYPE_FLOWTYPE) flow_type = 1; if (twt_resp->req_type & IEEE80211_TWT_REQTYPE_TRIGGER) trig_enable = 1; flow_id = ((twt_resp->req_type>>7) & 0x7); wkup_exponent= ((twt_resp->req_type>>10) & 0x1F); if (wkup_exponent) exponent_interval = 2 << (wkup_exponent -1); else exponent_interval = 1; wakeup_interval = twt_resp->mantissa * exponent_interval; awake_duration = twt_resp->min_twt_dur * 256; if (wakeup_interval > awake_duration) { umac_send_twt_info(neg_type, flow_type, flow_id, wkup_exponent, twt_resp->mantissa, twt_resp->twt, twt_resp->min_twt_dur, trig_enable); } else { /* teardown pkt to lmac */ ieee80211_s1g_send_twt_teardown(sdata, flow_id); /* teradown event to host */ nl80211_process_twt_teardown_event(wiphy, flow_id, INVALID_TIME); } } } unsigned long long get_current_tsf() { unsigned long long tsfLsb, tsfMidsb, tsfMsb; unsigned long long currentTSF = 0; /* Get the difference bw beacon TSF and twt field */ tsfLsb = UCC_READ_PERIP(ABS_PMB_WLAN_TIMERS_TIM64_LSB); tsfMidsb = UCC_READ_PERIP(ABS_PMB_WLAN_TIMERS_TIM64_MIDSB); tsfMsb = UCC_READ_PERIP(ABS_PMB_WLAN_TIMERS_TIM64_MSB); currentTSF = tsfLsb + (tsfMidsb << 24) + (((unsigned long long) tsfMsb) << 48); return currentTSF; } int img_umac_proc_cmd_config_twt_req(struct nrf_wifi_umac_cmd_config_twt *cmd, struct wireless_dev *wdev) { struct ieee80211_sub_if_data *sdata=IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_twt_setup *twt; struct ieee80211_twt_params *twt_req; unsigned long long current_tsf; if (!sdata->vif.bss_conf.assoc) return 0; twt = kzalloc((sizeof(struct ieee80211_twt_params)+sizeof(struct ieee80211_twt_setup)), GFP_KERNEL); memset(twt, 0, (sizeof(struct ieee80211_twt_params)+sizeof(struct ieee80211_twt_setup))); twt_req=(void *)twt->params; twt->dialog_token = 1; twt->element_id = 216; twt->length = 15; twt->control |= (cmd->info.neg_type << 2); twt_req->req_type |= IEEE80211_TWT_REQTYPE_REQUEST; twt_req->req_type |= (cmd->info.setup_cmd << 1); if (cmd->info.ap_trigger_frame != 0) twt_req->req_type |= IEEE80211_TWT_REQTYPE_TRIGGER; if (cmd->info.is_implicit != 0) twt_req->req_type |= IEEE80211_TWT_REQTYPE_IMPLICIT; if (cmd->info.twt_flow_type != 0) twt_req->req_type |= IEEE80211_TWT_REQTYPE_FLOWTYPE; twt_req->req_type |= (cmd->info.twt_flow_id << 7); twt_req->req_type |= (cmd->info.twt_target_wake_interval_exponent << 10); twt_req->mantissa = cmd->info.twt_target_wake_interval_mantissa; current_tsf = get_current_tsf(); if (current_tsf > 0) { twt_req->twt = (current_tsf+400000); } else { twt_req->twt = cmd->info.target_wake_time; } twt_req->min_twt_dur = cmd->info.nominal_min_twt_wake_duration; twt_req->channel = 0; ieee80211_send_twt_setup_req(sdata, twt); kfree(twt); return 0; } void process_bc_twt(const struct ieee80211_bcast_twt_setup *bc_twt, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; u8 bc_persistence = 0; u8 setup_cmd = 0; u8 neg_type = 0; u8 flow_type = 0, trig_enable = 0; u8 wkup_exponent = 0, flow_id = 0; bc_persistence = ((bc_twt->broadcast_info >> 8) & 0xFF); setup_cmd = ((bc_twt->req_type >> 1) & 0x7); if (bc_persistence == 0) { sdata->local->bc_twt_previous_info = 0; /* del twt info to lmac */ umac_send_twt_del_info(); }else if ((setup_cmd != NRF_WIFI_REJECT_TWT) && !(sdata->local->bc_twt_previous_info)) { sdata->local->bc_twt_previous_info = 1; neg_type = ((bc_twt->control>>2) & 0x3); if (bc_twt->req_type & IEEE80211_TWT_REQTYPE_FLOWTYPE) flow_type = 1; if (bc_twt->req_type & IEEE80211_TWT_REQTYPE_TRIGGER) trig_enable = 1; wkup_exponent= ((bc_twt->req_type>>10) & 0x1F); flow_id = ((bc_twt->req_type>>7) & 0x7); /* send twt info to lmac */ umac_send_twt_info(neg_type, flow_type, flow_id, wkup_exponent, bc_twt->mantissa, bc_twt->twt, bc_twt->min_twt_dur, trig_enable); } } int img_umac_proc_cmd_twt_teardown(struct nrf_wifi_umac_cmd_teardown_twt *cmd, struct wireless_dev *wdev) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); u8 flow_id; /* twt del info to lmac */ umac_send_twt_del_info(); /* twt teardown pkt to lmac */ flow_id = cmd->info.twt_flow_id; ieee80211_s1g_send_twt_teardown(sdata, flow_id); return 0; } void img_umac_process_twt_teardown(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; struct wireless_dev *wdev = &sdata->wdev; struct wiphy *wiphy = wdev->wiphy; u8 twt_teardown_info = 0, flow_id = 0, reason_code = 0; twt_teardown_info = mgmt->u.action.u.s1g_twt.variable[0]; flow_id = (twt_teardown_info & 0x7); /* twt teardown event to host */ nl80211_process_twt_teardown_event(wiphy, flow_id, reason_code); /* twt del info to lmac */ umac_send_twt_del_info(); }