/****************************************************************************** * * Copyright(c) 2007 - 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 _RTW_WOW_C_ #include #if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN) #include #define ETH_TYPE_OFFSET 12 #define PROTOCOL_OFFSET 23 #define IP_OFFSET 30 #define IPv6_OFFSET 38 #define IPv6_PROTOCOL_OFFSET 20 #endif #ifdef CONFIG_WOWLAN void rtw_init_wow(_adapter *padapter) { struct registry_priv *registry_par = &padapter->registrypriv; struct pwrctrl_priv *pwrctrlpriv = adapter_to_pwrctl(padapter); struct wow_priv *wowpriv = adapter_to_wowlan(padapter); #ifdef CONFIG_GPIO_WAKEUP struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); void *phl = GET_PHL_INFO(dvobj); enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE; struct rtw_wow_gpio_info *wow_gpio = &wowpriv->wow_gpio; u8 toggle_pulse = DEV2HST_TOGGLE, gpio_time_unit = 1, gpio_pulse_count = 3, gpio_pulse_period = 10, gpio_pulse_dura = 5; u8 gpio_pulse_en_a = 0, customer_id = 0, gpio_duration_unit_a = 0, gpio_pulse_count_a = 0, gpio_duration_a = 0, special_reason_a = 0; #endif #if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_ANDROID_POWER) pwrctrlpriv->early_suspend.suspend = NULL; rtw_register_early_suspend(pwrctrlpriv); #endif /* CONFIG_HAS_EARLYSUSPEND || CONFIG_ANDROID_POWER */ pwrctrlpriv->wowlan_mode = _FALSE; pwrctrlpriv->wowlan_ap_mode = _FALSE; pwrctrlpriv->wowlan_p2p_mode = _FALSE; pwrctrlpriv->wowlan_in_resume = _FALSE; pwrctrlpriv->wowlan_last_wake_reason = 0; #ifdef CONFIG_GPIO_WAKEUP #ifdef ROKU_PRIVATE gpio_pulse_en_a = DEV2HST_PULSE; customer_id = 0x3f; gpio_duration_unit_a = 1; gpio_pulse_count_a = 1; gpio_duration_a = 5; special_reason_a = 0x21; #endif /* ROKU_PRIVATE */ /*default low active*/ wow_gpio->gpio_active = HIGH_ACTIVE_DEV2HST; pwrctrlpriv->hst2dev_high_active = HIGH_ACTIVE_HST2DEV; wow_gpio->dev2hst_gpio = WAKEUP_GPIO_IDX; wow_gpio->toggle_pulse = toggle_pulse; wow_gpio->gpio_time_unit = gpio_time_unit; wow_gpio->gpio_pulse_dura = gpio_pulse_dura; wow_gpio->gpio_pulse_period = gpio_pulse_period; wow_gpio->gpio_pulse_count = gpio_pulse_count; wow_gpio->customer_id = customer_id; wow_gpio->gpio_pulse_en_a = gpio_pulse_en_a; wow_gpio->gpio_duration_unit_a = gpio_duration_unit_a; wow_gpio->gpio_duration_a = gpio_duration_a; wow_gpio->special_reason_a = special_reason_a; wow_gpio->gpio_pulse_count_a = gpio_pulse_count_a; #ifdef CONFIG_RTW_ONE_PIN_GPIO wow_gpio->dev2hst_gpio_mode = RTW_AX_SW_IO_MODE_INPUT; status = rtw_phl_cfg_wow_set_sw_gpio_mode(phl, wow_gpio); #else #ifdef CONFIG_WAKEUP_GPIO_INPUT_MODE wow_gpio->dev2hst_gpio_mode = RTW_AX_SW_IO_MODE_OUTPUT_OD; #else wow_gpio->dev2hst_gpio_mode = RTW_AX_SW_IO_MODE_OUTPUT_PP; #endif /*CONFIG_WAKEUP_GPIO_INPUT_MODE*/ /* switch GPIO to open-drain or push-pull */ status = rtw_phl_cfg_wow_set_sw_gpio_mode(phl, wow_gpio); wow_gpio->dev2hst_high = wow_gpio->gpio_active == 0 ? 1 : 0; status = rtw_phl_cfg_wow_sw_gpio_ctrl(phl, wow_gpio); RTW_INFO("%s: set GPIO_%d %d as default. status=%d\n", __func__, WAKEUP_GPIO_IDX, wow_gpio->dev2hst_high, status); #endif /* CONFIG_RTW_ONE_PIN_GPIO */ #endif /* CONFIG_GPIO_WAKEUP */ #ifdef CONFIG_WOWLAN #ifdef CONFIG_LPS_1T1R #define WOW_LPS_1T1R_FMT ", WOW_LPS_1T1R=%d" #define WOW_LPS_1T1R_ARG , pwrctrlpriv->wowlan_lps_1t1r #else #define WOW_LPS_1T1R_FMT "" #define WOW_LPS_1T1R_ARG #endif pwrctrlpriv->wowlan_power_mgmt = padapter->registrypriv.wow_power_mgnt; pwrctrlpriv->wowlan_lps_level = padapter->registrypriv.wow_lps_level; #ifdef CONFIG_LPS_1T1R pwrctrlpriv->wowlan_lps_1t1r = padapter->registrypriv.wow_lps_1t1r; #endif RTW_INFO("%s: WOW_LPS_mode=%d, WOW_LPS_level=%d"WOW_LPS_1T1R_FMT"\n", __func__, pwrctrlpriv->wowlan_power_mgmt, pwrctrlpriv->wowlan_lps_level WOW_LPS_1T1R_ARG ); if (!(registry_par->wakeup_event & BIT(3))) rtw_wow_pattern_clean(padapter, RTW_CUSTOMIZED_PATTERN); rtw_wow_pattern_clean(padapter, RTW_DEFAULT_PATTERN); #ifdef CONFIG_PNO_SUPPORT pwrctrlpriv->pno_inited = _FALSE; pwrctrlpriv->pnlo_info = NULL; pwrctrlpriv->pscan_info = NULL; pwrctrlpriv->pno_ssid_list = NULL; #endif /* CONFIG_PNO_SUPPORT */ _rtw_mutex_init(&pwrctrlpriv->wowlan_pattern_cam_mutex); pwrctrlpriv->wowlan_aoac_rpt_loc = 0; #endif /* CONFIG_WOWLAN */ } void rtw_free_wow(_adapter *adapter) { struct pwrctrl_priv *pwrctrlpriv = adapter_to_pwrctl(adapter); #ifdef CONFIG_PNO_SUPPORT if (pwrctrlpriv->pnlo_info != NULL) printk("****** pnlo_info memory leak********\n"); if (pwrctrlpriv->pscan_info != NULL) printk("****** pscan_info memory leak********\n"); if (pwrctrlpriv->pno_ssid_list != NULL) printk("****** pno_ssid_list memory leak********\n"); #endif _rtw_mutex_free(&pwrctrlpriv->wowlan_pattern_cam_mutex); #if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_ANDROID_POWER) rtw_unregister_early_suspend(pwrctrlpriv); #endif /* CONFIG_HAS_EARLYSUSPEND || CONFIG_ANDROID_POWER */ } void rtw_wowlan_set_pattern_cast_type(_adapter *adapter, struct rtw_wowcam_upd_info *wowcam_info) { if (is_broadcast_mac_addr(wowcam_info->ptrn)) wowcam_info->bc = 1; else if (is_multicast_mac_addr(wowcam_info->ptrn)) wowcam_info->mc = 1; else if (!memcmp(wowcam_info->ptrn, adapter_mac_addr(adapter), ETH_ALEN)) wowcam_info->uc = 1; } inline bool _rtw_wow_chk_cap(_adapter *adapter, u8 cap) { struct dvobj_priv *dvobj = adapter_to_dvobj(adapter); struct wow_ctl_t *wow_ctl = &dvobj->wow_ctl; if (wow_ctl->wow_cap & cap) return _TRUE; return _FALSE; } bool rtw_wowlan_parser_pattern_cmd(u8 *input, char *pattern, int *pattern_len, char *bit_mask) { char *cp = NULL; size_t len = 0; int pos = 0, mask_pos = 0, res = 0; u8 member[2] = {0}; /* To get the pattern string after "=", when we use : * iwpriv wlanX pattern=XX:XX:..:XX */ cp = strchr(input, '='); if (cp) { *cp = 0; cp++; input = cp; } /* To take off the newline character '\n'(0x0a) at the end of pattern string, * when we use echo xxxx > /proc/xxxx */ cp = strchr(input, '\n'); if (cp) *cp = 0; while (input) { cp = strsep((char **)(&input), ":"); if (bit_mask && (strcmp(cp, "-") == 0 || strcmp(cp, "xx") == 0 || strcmp(cp, "--") == 0)) { /* skip this byte and leave mask bit unset */ } else { u8 hex; if (strlen(cp) != 2) { RTW_ERR("%s:[ERROR] hex len != 2, input=[%s]\n", __func__, cp); goto error; } if (hexstr2bin(cp, &hex, 1) < 0) { RTW_ERR("%s:[ERROR] pattern is invalid, input=[%s]\n", __func__, cp); goto error; } pattern[pos] = hex; mask_pos = pos / 8; if (bit_mask) bit_mask[mask_pos] |= 1 << (pos % 8); } pos++; } (*pattern_len) = pos; return _TRUE; error: return _FALSE; } u8 rtw_wow_pattern_set(_adapter *adapter, struct rtw_wowcam_upd_info *wowcam_info, enum pattern_type set_type) { struct wow_priv *wowpriv = adapter_to_wowlan(adapter); struct dvobj_priv *dvobj = adapter_to_dvobj(adapter); void *phl = GET_PHL_INFO(dvobj); enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE; u8 cam_idx = MAX_WKFM_CAM_NUM; rtw_wowlan_set_pattern_cast_type(adapter, wowcam_info); status = rtw_phl_add_wow_ptrn_info(phl, wowcam_info, &cam_idx); if (status != RTW_PHL_STATUS_SUCCESS) { RTW_INFO("Add wow pattern fail(%d)\n", status); return _FAIL; } wowpriv->wow_ptrn_valid[cam_idx] = set_type; RTW_INFO("wowcam_id: %d, type: %d\n", cam_idx, set_type); return _SUCCESS; } void rtw_wow_pattern_clean(_adapter *adapter, enum pattern_type clean_type) { struct wow_priv *wowpriv = adapter_to_wowlan(adapter); struct dvobj_priv *dvobj = adapter_to_dvobj(adapter); void *phl = GET_PHL_INFO(dvobj); u8 cam_idx; for (cam_idx = 0; cam_idx < MAX_WKFM_CAM_NUM; cam_idx++) { if (wowpriv->wow_ptrn_valid[cam_idx] == clean_type) { rtw_phl_remove_wow_ptrn_info(phl, cam_idx); wowpriv->wow_ptrn_valid[cam_idx] = RTW_INVALID_PATTERN; } } } void rtw_set_default_pattern(_adapter *adapter) { struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(adapter); struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; struct rtw_wowcam_upd_info wowcam_info = {0}; u8 index = 0; u8 multicast_addr[3] = {0x01, 0x00, 0x5e}; u8 multicast_ip[4] = {0xe0, 0x28, 0x28, 0x2a}; u8 unicast_mask[5] = {0x3f, 0x70, 0x80, 0xc0, 0x03}; u8 icmpv6_mask[7] = {0x00, 0x70, 0x10, 0x00, 0xc0, 0xc0, 0x3f}; u8 multicast_mask[5] = {0x07, 0x70, 0x80, 0xc0, 0x03}; u8 ip_protocol[3] = {0x08, 0x00, 0x45}; u8 ipv6_protocol[3] = {0x86, 0xdd, 0x60}; u8 *target = NULL; /*TCP/ICMP unicast*/ for (index = 0 ; index < DEFAULT_PATTERN_NUM ; index++) { _rtw_memset((void *)&wowcam_info, 0, sizeof(wowcam_info)); switch (index) { case 0: target = wowcam_info.ptrn; _rtw_memcpy(target, adapter_mac_addr(adapter), ETH_ALEN); target += ETH_TYPE_OFFSET; _rtw_memcpy(target, &ip_protocol, sizeof(ip_protocol)); /* TCP */ target += (PROTOCOL_OFFSET - ETH_TYPE_OFFSET); _rtw_memset(target, 0x06, 1); target += (IP_OFFSET - PROTOCOL_OFFSET); _rtw_memcpy(target, pmlmeinfo->ip_addr, RTW_IP_ADDR_LEN); _rtw_memcpy(wowcam_info.mask, &unicast_mask, sizeof(unicast_mask)); wowcam_info.ptrn_len = IP_OFFSET + RTW_IP_ADDR_LEN; rtw_wow_pattern_set(adapter, &wowcam_info, RTW_DEFAULT_PATTERN); break; case 1: target = wowcam_info.ptrn; _rtw_memcpy(target, adapter_mac_addr(adapter), ETH_ALEN); target += ETH_TYPE_OFFSET; _rtw_memcpy(target, &ip_protocol, sizeof(ip_protocol)); /* ICMP */ target += (PROTOCOL_OFFSET - ETH_TYPE_OFFSET); _rtw_memset(target, 0x01, 1); target += (IP_OFFSET - PROTOCOL_OFFSET); _rtw_memcpy(target, pmlmeinfo->ip_addr, RTW_IP_ADDR_LEN); _rtw_memcpy(wowcam_info.mask, &unicast_mask, sizeof(unicast_mask)); wowcam_info.ptrn_len = IP_OFFSET + RTW_IP_ADDR_LEN; rtw_wow_pattern_set(adapter, &wowcam_info, RTW_DEFAULT_PATTERN); break; #ifdef CONFIG_IPV6 case 2: if (pwrpriv->wowlan_ns_offload_en == _TRUE) { target = wowcam_info.ptrn; target += ETH_TYPE_OFFSET; _rtw_memcpy(target, &ipv6_protocol, sizeof(ipv6_protocol)); /* ICMPv6 */ target += (IPv6_PROTOCOL_OFFSET - ETH_TYPE_OFFSET); _rtw_memset(target, 0x3a, 1); target += (IPv6_OFFSET - IPv6_PROTOCOL_OFFSET); _rtw_memcpy(target, pmlmeinfo->ip6_addr, RTW_IPv6_ADDR_LEN); _rtw_memcpy(wowcam_info.mask, &icmpv6_mask, sizeof(icmpv6_mask)); wowcam_info.ptrn_len = IPv6_OFFSET + RTW_IPv6_ADDR_LEN; rtw_wow_pattern_set(adapter, &wowcam_info, RTW_DEFAULT_PATTERN); } break; #endif /*CONFIG_IPV6*/ case 3: target = wowcam_info.ptrn; _rtw_memcpy(target, &multicast_addr, sizeof(multicast_addr)); target += ETH_TYPE_OFFSET; _rtw_memcpy(target, &ip_protocol, sizeof(ip_protocol)); /* UDP */ target += (PROTOCOL_OFFSET - ETH_TYPE_OFFSET); _rtw_memset(target, 0x11, 1); target += (IP_OFFSET - PROTOCOL_OFFSET); _rtw_memcpy(target, &multicast_ip, sizeof(multicast_ip)); _rtw_memcpy(wowcam_info.mask, &multicast_mask, sizeof(multicast_mask)); wowcam_info.ptrn_len = IP_OFFSET + sizeof(multicast_ip); rtw_wow_pattern_set(adapter, &wowcam_info, RTW_DEFAULT_PATTERN); break; default: break; } } return; } void rtw_dump_priv_pattern(_adapter *adapter, u8 idx) { struct pwrctrl_priv *pwrctl = adapter_to_pwrctl(adapter); char str_1[128]; char *p_str; u8 val8 = 0; int i = 0, j = 0, len = 0, max_len = 0; RTW_INFO("=========[%d]========\n", idx); RTW_INFO(">>>priv_pattern_content:\n"); p_str = str_1; max_len = sizeof(str_1); for (i = 0 ; i < MAX_WKFM_PATTERN_SIZE / 8 ; i++) { _rtw_memset(p_str, 0, max_len); len = 0; for (j = 0 ; j < 8 ; j++) { val8 = pwrctl->patterns[idx].content[i * 8 + j]; len += snprintf(p_str + len, max_len - len, "%02x ", val8); } RTW_INFO("%s\n", p_str); } RTW_INFO(">>>priv_pattern_mask:\n"); for (i = 0 ; i < MAX_WKFM_SIZE / 8 ; i++) { _rtw_memset(p_str, 0, max_len); len = 0; for (j = 0 ; j < 8 ; j++) { val8 = pwrctl->patterns[idx].mask[i * 8 + j]; len += snprintf(p_str + len, max_len - len, "%02x ", val8); } RTW_INFO("%s\n", p_str); } RTW_INFO(">>>priv_pattern_len:\n"); RTW_INFO("%s: len: %d\n", __func__, pwrctl->patterns[idx].len); } void rtw_wow_pattern_sw_dump(_adapter *adapter) { int i; RTW_INFO("********[RTK priv-patterns]*********\n"); for (i = 0 ; i < MAX_WKFM_CAM_NUM; i++) rtw_dump_priv_pattern(adapter, i); } void rtw_get_sec_iv(_adapter *padapter, u8 *pcur_dot11txpn, u8 *StaAddr) { struct sta_info *psta; struct security_priv *psecpriv = &padapter->securitypriv; _rtw_memset(pcur_dot11txpn, 0, 8); if (NULL == StaAddr) return; psta = rtw_get_stainfo(&padapter->stapriv, StaAddr); RTW_INFO("%s(): StaAddr: %02x %02x %02x %02x %02x %02x\n", __func__, StaAddr[0], StaAddr[1], StaAddr[2], StaAddr[3], StaAddr[4], StaAddr[5]); if (psta) { if ((psecpriv->dot11PrivacyAlgrthm == _AES_) || (psecpriv->dot11PrivacyAlgrthm == _CCMP_256_)) AES_IV(pcur_dot11txpn, psta->dot11txpn, 0); else if (psecpriv->dot11PrivacyAlgrthm == _TKIP_) TKIP_IV(pcur_dot11txpn, psta->dot11txpn, 0); else if ((psecpriv->dot11PrivacyAlgrthm == _GCMP_) || (psecpriv->dot11PrivacyAlgrthm == _GCMP_256_)) GCMP_IV(pcur_dot11txpn, psta->dot11txpn, 0); } } void rtw_construct_remote_control_info(_adapter *adapter, struct rtw_remote_wake_ctrl_info *ctrl_info) { struct security_priv *securitypriv = &adapter->securitypriv; struct stainfo_rxcache *rxcache = NULL; struct sta_info *sta = NULL; u8 gtk_rx_iv[4][IV_LENGTH] = {0}; u8 tid_id = 0; u8 i = 0; sta = rtw_get_stainfo(&adapter->stapriv, get_bssid(&adapter->mlmepriv)); if (!sta) { rtw_warn_on(1); return; } rxcache = &sta->sta_recvpriv.rxcache; rtw_get_sec_iv(adapter, ctrl_info->ptk_tx_iv, get_bssid(&adapter->mlmepriv)); RTW_INFO("[wow] ptk_tx_iv = " IV_FMT "\n", IV_ARG(ctrl_info->ptk_tx_iv)); ctrl_info->valid_check = REMOTECTRL_INFO_VALID_CHECK; ctrl_info->symbol_check_en |= REMOTECTRL_INFO_SYMBOL_CHK_PTK | REMOTECTRL_INFO_SYMBOL_CHK_GTK; ctrl_info->gtk_key_idx = securitypriv->dot118021XGrpKeyid; RTW_INFO("[wow] gtk_key_idx = %d\n", ctrl_info->gtk_key_idx); tid_id = rxcache->last_tid; _rtw_memcpy(ctrl_info->ptk_rx_iv, rxcache->iv[tid_id], IV_LENGTH); RTW_INFO("[wow] ptk_rx_iv = " IV_FMT "\n", IV_ARG(ctrl_info->ptk_rx_iv)); for (i = 0; i < 4; i++) { rtw_pn_to_iv(securitypriv->iv_seq[i], gtk_rx_iv[i], i, securitypriv->dot118021XGrpPrivacy); RTW_INFO("[wow] gtk_rx_iv[%u] = " IV_FMT "\n", i, IV_ARG(gtk_rx_iv[i])); } _rtw_memcpy(ctrl_info->gtk_rx_iv_idx0, gtk_rx_iv[0], IV_LENGTH); _rtw_memcpy(ctrl_info->gtk_rx_iv_idx1, gtk_rx_iv[1], IV_LENGTH); _rtw_memcpy(ctrl_info->gtk_rx_iv_idx2, gtk_rx_iv[2], IV_LENGTH); _rtw_memcpy(ctrl_info->gtk_rx_iv_idx3, gtk_rx_iv[3], IV_LENGTH); } void rtw_wow_lps_level_decide(_adapter *adapter, u8 wow_en) { struct dvobj_priv *dvobj = adapter_to_dvobj(adapter); struct pwrctrl_priv *pwrpriv = dvobj_to_pwrctl(dvobj); if (wow_en) { pwrpriv->lps_level_bk = pwrpriv->lps_level; pwrpriv->lps_level = pwrpriv->wowlan_lps_level; #ifdef CONFIG_LPS_1T1R pwrpriv->lps_1t1r_bk = pwrpriv->lps_1t1r; pwrpriv->lps_1t1r = pwrpriv->wowlan_lps_1t1r; #endif } else { pwrpriv->lps_level = pwrpriv->lps_level_bk; #ifdef CONFIG_LPS_1T1R pwrpriv->lps_1t1r = pwrpriv->lps_1t1r_bk; #endif } } int rtw_pm_set_wow_lps(_adapter *padapter, u8 mode) { int ret = 0; struct pwrctrl_priv *pwrctrlpriv = adapter_to_pwrctl(padapter); if (mode < PM_PS_MODE_NUM) { if (pwrctrlpriv->wowlan_power_mgmt != mode) pwrctrlpriv->wowlan_power_mgmt = mode; } else ret = -EINVAL; return ret; } int rtw_pm_set_wow_lps_level(_adapter *padapter, u8 level) { int ret = 0; struct pwrctrl_priv *pwrctrlpriv = adapter_to_pwrctl(padapter); if (level < LPS_LEVEL_MAX) pwrctrlpriv->wowlan_lps_level = level; else ret = -EINVAL; return ret; } #ifdef CONFIG_LPS_1T1R int rtw_pm_set_wow_lps_1t1r(_adapter *padapter, u8 en) { int ret = 0; struct pwrctrl_priv *pwrctrlpriv = adapter_to_pwrctl(padapter); en = en ? 1 : 0; pwrctrlpriv->wowlan_lps_1t1r = en; return ret; } #endif /* CONFIG_LPS_1T1R */ #ifdef CONFIG_GTK_OL void _update_aoac_rpt_phase_0(_adapter *adapter, struct rtw_aoac_report *aoac_info) { struct security_priv *securitypriv = &adapter->securitypriv; struct stainfo_rxcache *rxcache = NULL; struct sta_info *sta = NULL; u8 pn[8] = {0}; u8 gtk_key_idx = 0; u8 i = 0; /* handle ptk rx iv */ /* This Rx IV has no effect, the driver does not drop unicast packets * due to different PNs. (Refer to recv_ucast_pn_decache) */ sta = rtw_get_stainfo(&adapter->stapriv, get_bssid(&adapter->mlmepriv)); if (sta) { if (rtw_iv_to_pn(aoac_info->ptk_rx_iv, pn, NULL, securitypriv->dot11PrivacyAlgrthm)) { rxcache = &sta->sta_recvpriv.rxcache; for (i = 0; i < TID_NUM; i++) _rtw_memcpy(rxcache->iv[i], aoac_info->ptk_rx_iv, IV_LENGTH); sta->dot11rxpn.val = RTW_GET_LE64(pn); RTW_INFO("[wow] ptk_rx_pn = " PN_FMT "\n", PN_ARG(pn)); } } /* handle gtk rx iv */ gtk_key_idx = aoac_info->key_idx; if (rtw_iv_to_pn(aoac_info->gtk_rx_iv[gtk_key_idx], pn, NULL, securitypriv->dot118021XGrpPrivacy)) { _rtw_memcpy(securitypriv->iv_seq[gtk_key_idx], pn, 8); RTW_INFO("[wow] gtk_rx_pn[%u] = " PN_FMT "\n", gtk_key_idx, PN_ARG(pn)); } } void _update_aoac_rpt_phase_1(_adapter *adapter, struct rtw_aoac_report *aoac_info) { struct security_priv *securitypriv = &adapter->securitypriv; struct sta_info *sta = NULL; u8 pn[8] = {0}; u8 gtk_key_idx = 0; u8 key_len = 0; u8 i = 0; /* handle ptk tx iv */ sta = rtw_get_stainfo(&adapter->stapriv, get_bssid(&adapter->mlmepriv)); if (sta) { if (rtw_iv_to_pn(aoac_info->ptk_tx_iv, pn, NULL, securitypriv->dot11PrivacyAlgrthm)) { sta->dot11txpn.val = RTW_GET_LE64(pn); RTW_INFO("[wow] ptk_tx_pn = " PN_FMT "\n", PN_ARG(pn)); } } if (aoac_info->rekey_ok) { /* update gtk key */ gtk_key_idx = aoac_info->key_idx; securitypriv->dot118021XGrpKeyid = gtk_key_idx; switch (securitypriv->dot118021XGrpPrivacy) { case _TKIP_: case _AES_: case _GCMP_: key_len = 16; break; case _GCMP_256_: case _CCMP_256_: key_len = 32; break; default: key_len = 0; } if (key_len) _rtw_memcpy(securitypriv->dot118021XGrpKey[gtk_key_idx].skey, aoac_info->gtk, key_len); /* update tkip dot118021XGrptxmickey dot118021XGrprxmickey */ if (securitypriv->dot118021XGrpPrivacy == _TKIP_) { /* The order of the GTK Tx/Rx mic keys in the AOAC report is * reversed compared to the GTK Tx/Rx mic keys provided by * wpa_supplicant. */ _rtw_memcpy(securitypriv->dot118021XGrptxmickey[gtk_key_idx].skey, &aoac_info->gtk[24], 8); _rtw_memcpy(securitypriv->dot118021XGrprxmickey[gtk_key_idx].skey, &aoac_info->gtk[16], 8); } rtw_set_key(adapter, securitypriv, gtk_key_idx, 1, _TRUE); /* update eapol replay_counter */ _rtw_memcpy(sta->replay_ctr, aoac_info->eapol_key_replay_count, RTW_REPLAY_CTR_LEN); } else { RTW_INFO("[wow] no rekey event\n"); } for (i = 0; i < 4; i++) { if (rtw_iv_to_pn(aoac_info->gtk_rx_iv[i], pn, NULL, securitypriv->dot118021XGrpPrivacy)) { _rtw_memcpy(securitypriv->iv_seq[i], pn, 8); RTW_INFO("[wow] gtk_rx_pn[%u] = " PN_FMT "\n", i, PN_ARG(pn)); } } } void rtw_update_gtk_ofld_info(void *drv_priv, struct rtw_aoac_report *aoac_info, u8 aoac_report_get_ok, u8 rx_ready) { struct dvobj_priv *dvobj = (struct dvobj_priv *)drv_priv; _adapter *adapter = dvobj_get_primary_adapter(dvobj); if (!rx_ready) _update_aoac_rpt_phase_0(adapter, aoac_info); else _update_aoac_rpt_phase_1(adapter, aoac_info); } #endif /* CONFIG_GTK_OL */ #endif /* CONFIG_WOWLAN */ #ifdef CONFIG_PNO_SUPPORT #define CSCAN_TLV_TYPE_SSID_IE 'S' #define CIPHER_IE "key_mgmt=" #define CIPHER_NONE "NONE" #define CIPHER_WPA_PSK "WPA-PSK" #define CIPHER_WPA_EAP "WPA-EAP IEEE8021X" /* * SSIDs list parsing from cscan tlv list */ int rtw_parse_ssid_list_tlv(char **list_str, pno_ssid_t *ssid, int max, int *bytes_left) { char *str; int idx = 0; if ((list_str == NULL) || (*list_str == NULL) || (*bytes_left < 0)) { RTW_INFO("%s error paramters\n", __func__); return -1; } str = *list_str; while (*bytes_left > 0) { if (str[0] != CSCAN_TLV_TYPE_SSID_IE) { *list_str = str; RTW_INFO("nssid=%d left_parse=%d %d\n", idx, *bytes_left, str[0]); return idx; } /* Get proper CSCAN_TLV_TYPE_SSID_IE */ *bytes_left -= 1; str += 1; if (str[0] == 0) { /* Broadcast SSID */ ssid[idx].SSID_len = 0; _rtw_memset((char *)ssid[idx].SSID, 0x0, WLAN_SSID_MAXLEN); *bytes_left -= 1; str += 1; RTW_INFO("BROADCAST SCAN left=%d\n", *bytes_left); } else if (str[0] <= WLAN_SSID_MAXLEN) { /* Get proper SSID size */ ssid[idx].SSID_len = str[0]; *bytes_left -= 1; str += 1; /* Get SSID */ if (ssid[idx].SSID_len > *bytes_left) { RTW_INFO("%s out of memory range len=%d but left=%d\n", __func__, ssid[idx].SSID_len, *bytes_left); return -1; } _rtw_memcpy((char *)ssid[idx].SSID, str, ssid[idx].SSID_len); *bytes_left -= ssid[idx].SSID_len; str += ssid[idx].SSID_len; RTW_INFO("%s :size=%d left=%d\n", (char *)ssid[idx].SSID, ssid[idx].SSID_len, *bytes_left); } else { RTW_INFO("### SSID size more that %d\n", str[0]); return -1; } if (idx++ > max) { RTW_INFO("%s number of SSIDs more that %d\n", __func__, idx); return -1; } } *list_str = str; return idx; } int rtw_parse_cipher_list(struct pno_nlo_info *nlo_info, char *list_str) { char *pch, *pnext, *pend; u8 key_len = 0, index = 0; pch = list_str; if (nlo_info == NULL || list_str == NULL) { RTW_INFO("%s error paramters\n", __func__); return -1; } while (strlen(pch) != 0) { pnext = strstr(pch, "key_mgmt="); if (pnext != NULL) { pch = pnext + strlen(CIPHER_IE); pend = strstr(pch, "}"); if (strncmp(pch, CIPHER_NONE, strlen(CIPHER_NONE)) == 0) nlo_info->ssid_cipher_info[index] = 0x00; else if (strncmp(pch, CIPHER_WPA_PSK, strlen(CIPHER_WPA_PSK)) == 0) nlo_info->ssid_cipher_info[index] = 0x66; else if (strncmp(pch, CIPHER_WPA_EAP, strlen(CIPHER_WPA_EAP)) == 0) nlo_info->ssid_cipher_info[index] = 0x01; index++; pch = pend + 1; } else break; } return 0; } int rtw_dev_nlo_info_set(struct pno_nlo_info *nlo_info, pno_ssid_t *ssid, int num, int pno_time, int pno_repeat, int pno_freq_expo_max) { int i = 0; struct file *fp; mm_segment_t fs; loff_t pos = 0; u8 *source = NULL; long len = 0; RTW_INFO("+%s+\n", __func__); nlo_info->fast_scan_period = pno_time; nlo_info->ssid_num = num & BIT_LEN_MASK_32(8); nlo_info->hidden_ssid_num = num & BIT_LEN_MASK_32(8); nlo_info->slow_scan_period = (pno_time * 2); nlo_info->fast_scan_iterations = 5; if (nlo_info->hidden_ssid_num > 8) nlo_info->hidden_ssid_num = 8; /* TODO: channel list and probe index is all empty. */ for (i = 0 ; i < num ; i++) { nlo_info->ssid_length[i] = ssid[i].SSID_len; } /* cipher array */ fp = filp_open("/data/misc/wifi/wpa_supplicant.conf", O_RDONLY, 0644); if (IS_ERR(fp)) { RTW_INFO("Error, wpa_supplicant.conf doesn't exist.\n"); RTW_INFO("Error, cipher array using default value.\n"); return 0; } len = i_size_read(fp->f_path.dentry->d_inode); if (len < 0 || len > 2048) { RTW_INFO("Error, file size is bigger than 2048.\n"); RTW_INFO("Error, cipher array using default value.\n"); return 0; } fs = get_fs(); set_fs(KERNEL_DS); source = rtw_zmalloc(2048); if (source != NULL) { len = vfs_read(fp, source, len, &pos); rtw_parse_cipher_list(nlo_info, source); rtw_mfree(source, 2048); } set_fs(fs); filp_close(fp, NULL); RTW_INFO("-%s-\n", __func__); return 0; } int rtw_dev_ssid_list_set(struct pno_ssid_list *pno_ssid_list, pno_ssid_t *ssid, u8 num) { int i = 0; if (num > MAX_PNO_LIST_COUNT) num = MAX_PNO_LIST_COUNT; for (i = 0 ; i < num ; i++) { _rtw_memcpy(&pno_ssid_list->node[i].SSID, ssid[i].SSID, ssid[i].SSID_len); pno_ssid_list->node[i].SSID_len = ssid[i].SSID_len; } return 0; } int rtw_dev_scan_info_set(_adapter *padapter, pno_ssid_t *ssid, unsigned char ch, unsigned char ch_offset, unsigned short bw_mode) { struct pwrctrl_priv *pwrctl = adapter_to_pwrctl(padapter); struct pno_scan_info *scan_info = pwrctl->pscan_info; u8 band = ch <= 14 ? BAND_ON_24G : BAND_ON_5G; int i; scan_info->channel_num = MAX_SCAN_LIST_COUNT; scan_info->orig_ch = ch; scan_info->orig_bw = bw_mode; scan_info->orig_40_offset = ch_offset; for (i = 0 ; i < scan_info->channel_num ; i++) { if (i < 11) scan_info->ssid_channel_info[i].active = 1; else scan_info->ssid_channel_info[i].active = 0; scan_info->ssid_channel_info[i].timeout = 100; scan_info->ssid_channel_info[i].tx_power = phy_get_tx_power_index_ex(padapter, 0, CCK, MGN_1M, bw_mode, band, i + 1); scan_info->ssid_channel_info[i].channel = i + 1; } RTW_INFO("%s, channel_num: %d, orig_ch: %d, orig_bw: %d orig_40_offset: %d\n", __func__, scan_info->channel_num, scan_info->orig_ch, scan_info->orig_bw, scan_info->orig_40_offset); return 0; } int rtw_dev_pno_set(struct net_device *net, pno_ssid_t *ssid, int num, int pno_time, int pno_repeat, int pno_freq_expo_max) { _adapter *padapter = (_adapter *)rtw_netdev_priv(net); struct pwrctrl_priv *pwrctl = adapter_to_pwrctl(padapter); struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; int ret = -1; if (num == 0) { RTW_INFO("%s, nssid is zero, no need to setup pno ssid list\n", __func__); return 0; } if (pwrctl == NULL) { RTW_INFO("%s, ERROR: pwrctl is NULL\n", __func__); return -1; } else { pwrctl->pnlo_info = (pno_nlo_info_t *)rtw_zmalloc(sizeof(pno_nlo_info_t)); pwrctl->pno_ssid_list = (pno_ssid_list_t *)rtw_zmalloc(sizeof(pno_ssid_list_t)); pwrctl->pscan_info = (pno_scan_info_t *)rtw_zmalloc(sizeof(pno_scan_info_t)); } if (pwrctl->pnlo_info == NULL || pwrctl->pscan_info == NULL || pwrctl->pno_ssid_list == NULL) { RTW_INFO("%s, ERROR: alloc nlo_info, ssid_list, scan_info fail\n", __func__); goto failing; } pwrctl->wowlan_in_resume = _FALSE; pwrctl->pno_inited = _TRUE; /* NLO Info */ ret = rtw_dev_nlo_info_set(pwrctl->pnlo_info, ssid, num, pno_time, pno_repeat, pno_freq_expo_max); /* SSID Info */ ret = rtw_dev_ssid_list_set(pwrctl->pno_ssid_list, ssid, num); /* SCAN Info */ ret = rtw_dev_scan_info_set(padapter, ssid, pmlmeext->chandef.chan, pmlmeext->chandef.offset, pmlmeext->chandef.bw); RTW_INFO("+%s num: %d, pno_time: %d, pno_repeat:%d, pno_freq_expo_max:%d+\n", __func__, num, pno_time, pno_repeat, pno_freq_expo_max); return 0; failing: if (pwrctl->pnlo_info) { rtw_mfree((u8 *)pwrctl->pnlo_info, sizeof(pno_nlo_info_t)); pwrctl->pnlo_info = NULL; } if (pwrctl->pno_ssid_list) { rtw_mfree((u8 *)pwrctl->pno_ssid_list, sizeof(pno_ssid_list_t)); pwrctl->pno_ssid_list = NULL; } if (pwrctl->pscan_info) { rtw_mfree((u8 *)pwrctl->pscan_info, sizeof(pno_scan_info_t)); pwrctl->pscan_info = NULL; } return -1; } #ifdef CONFIG_PNO_SET_DEBUG void rtw_dev_pno_debug(struct net_device *net) { _adapter *padapter = (_adapter *)rtw_netdev_priv(net); struct pwrctrl_priv *pwrctl = adapter_to_pwrctl(padapter); int i = 0, j = 0; RTW_INFO("*******NLO_INFO********\n"); RTW_INFO("ssid_num: %d\n", pwrctl->pnlo_info->ssid_num); RTW_INFO("fast_scan_iterations: %d\n", pwrctl->pnlo_info->fast_scan_iterations); RTW_INFO("fast_scan_period: %d\n", pwrctl->pnlo_info->fast_scan_period); RTW_INFO("slow_scan_period: %d\n", pwrctl->pnlo_info->slow_scan_period); for (i = 0 ; i < MAX_PNO_LIST_COUNT ; i++) { RTW_INFO("%d SSID (%s) length (%d) cipher(%x) channel(%d)\n", i, pwrctl->pno_ssid_list->node[i].SSID, pwrctl->pnlo_info->ssid_length[i], pwrctl->pnlo_info->ssid_cipher_info[i], pwrctl->pnlo_info->ssid_channel_info[i]); } RTW_INFO("******SCAN_INFO******\n"); RTW_INFO("ch_num: %d\n", pwrctl->pscan_info->channel_num); RTW_INFO("orig_ch: %d\n", pwrctl->pscan_info->orig_ch); RTW_INFO("orig bw: %d\n", pwrctl->pscan_info->orig_bw); RTW_INFO("orig 40 offset: %d\n", pwrctl->pscan_info->orig_40_offset); for (i = 0 ; i < MAX_SCAN_LIST_COUNT ; i++) { RTW_INFO("[%02d] avtive:%d, timeout:%d, tx_power:%d, ch:%02d\n", i, pwrctl->pscan_info->ssid_channel_info[i].active, pwrctl->pscan_info->ssid_channel_info[i].timeout, pwrctl->pscan_info->ssid_channel_info[i].tx_power, pwrctl->pscan_info->ssid_channel_info[i].channel); } RTW_INFO("*****************\n"); } #endif /* CONFIG_PNO_SET_DEBUG */ #endif /* CONFIG_PNO_SUPPORT */