// SPDX-License-Identifier: GPL-2.0 #include #include #include #include "core.h" #include "softmac_core.h" #include "p2p.h" #include "utils.h" #include "img_host_txrx_buffs.h" #include "ieee80211_i.h" #include "wep.h" #include "umac_wpa.h" #include "img_mac80211_osal.h" #include "img_utils.h" /* Its value will be the default mac address and it can only be updated with the * command line arguments */ unsigned int ht_support = 1; unsigned int vht_support = 1; RETENTION_MEM_SECTION_UNINITIALIZED extern unsigned int sleepEnable; int rpu_debug = RPU_DEBUG_UMACIF + RPU_DEBUG_CRYPTO + RPU_DEBUG_IF; module_param(rpu_debug, uint, 0); RETENTION_MEM_SECTION_UNINITIALIZED extern struct nrf_wifi_data_config_params data_config_params_g; #define CHAN2G(_freq, _idx, _flags) { \ .band = NL80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 20, \ .flags = (_flags), \ } #define CHAN5G(_freq, _idx, _flags) { \ .band = NL80211_BAND_5GHZ, \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 20, \ .flags = (_flags), \ } RETENTION_MEM_SECTION_UNINITIALIZED struct ieee80211_channel dsss_chantable[14]; RETENTION_MEM_SECTION_UNINITIALIZED struct ieee80211_channel ofdm_chantable[28]; RETENTION_MEM_SECTION_UNINITIALIZED struct ieee80211_rate ofdm_rates[8]; RETENTION_MEM_SECTION_UNINITIALIZED struct ieee80211_rate dsss_rates[12]; RETENTION_MEM_SECTION_UNINITIALIZED struct ieee80211_supported_band band_2ghz; RETENTION_MEM_SECTION_UNINITIALIZED struct ieee80211_supported_band band_5ghz; RETENTION_MEM_SECTION_UNINITIALIZED struct ieee80211_sband_iftype_data he_capa_2ghz; RETENTION_MEM_SECTION_UNINITIALIZED struct ieee80211_sband_iftype_data he_capa_5ghz; void setup_dsss_channel_table() { struct ieee80211_channel dsss_chantable_l[] = { CHAN2G(2412, 0, 0), /* Channel 1 */ CHAN2G(2417, 1, 0), /* Channel 2 */ CHAN2G(2422, 2, 0), /* Channel 3 */ CHAN2G(2427, 3, 0), /* Channel 4 */ CHAN2G(2432, 4, 0), /* Channel 5 */ CHAN2G(2437, 5, 0), /* Channel 6 */ CHAN2G(2442, 6, 0), /* Channel 7 */ CHAN2G(2447, 7, 0), /* Channel 8 */ CHAN2G(2452, 8, 0), /* Channel 9 */ CHAN2G(2457, 9, 0), /* Channel 10 */ CHAN2G(2462, 10, 0), /* Channel 11 */ CHAN2G(2467, 11, 0), /* Channel 12 */ CHAN2G(2472, 12, 0), /* Channel 13 */ CHAN2G(2484, 13, 0), /* Channel 14 */ }; memcpy(dsss_chantable, dsss_chantable_l, ARRAY_SIZE(dsss_chantable_l) * sizeof(struct ieee80211_channel)); } void setup_ofdm_channel_table() { struct ieee80211_channel ofdm_chantable_l[] = { CHAN5G(5180, 14, 0), /* Channel 36 */ CHAN5G(5200, 15, 0), /* Channel 40 */ CHAN5G(5220, 16, 0), /* Channel 44 */ CHAN5G(5240, 17, 0), /* Channel 48 */ CHAN5G(5260, 18, IEEE80211_CHAN_RADAR), /* Channel 52 */ CHAN5G(5280, 19, IEEE80211_CHAN_RADAR), /* Channel 56 */ CHAN5G(5300, 20, IEEE80211_CHAN_RADAR), /* Channel 60 */ CHAN5G(5320, 21, IEEE80211_CHAN_RADAR), /* Channel 64 */ CHAN5G(5500, 22, IEEE80211_CHAN_RADAR), /* Channel 100 */ CHAN5G(5520, 23, IEEE80211_CHAN_RADAR), /* Channel 104 */ CHAN5G(5540, 24, IEEE80211_CHAN_RADAR), /* Channel 108 */ CHAN5G(5560, 25, IEEE80211_CHAN_RADAR), /* Channel 112 */ CHAN5G(5580, 26, IEEE80211_CHAN_RADAR), /* Channel 116 */ CHAN5G(5600, 27, IEEE80211_CHAN_RADAR), /* Channel 120 */ CHAN5G(5620, 28, IEEE80211_CHAN_RADAR), /* Channel 124 */ CHAN5G(5640, 29, IEEE80211_CHAN_RADAR), /* Channel 128 */ CHAN5G(5660, 30, IEEE80211_CHAN_RADAR), /* Channel 132 */ CHAN5G(5680, 31, IEEE80211_CHAN_RADAR), /* Channel 136 */ CHAN5G(5700, 32, IEEE80211_CHAN_RADAR), /* Channel 140 */ CHAN5G(5720, 33, IEEE80211_CHAN_RADAR), /* Channel 144 */ CHAN5G(5745, 34, 0), /* Channel 149 */ CHAN5G(5765, 35, 0), /* Channel 153 */ CHAN5G(5785, 36, 0), /* Channel 157 */ CHAN5G(5805, 37, 0), /* Channel 161 */ CHAN5G(5825, 38, 0), /* Channel 165 */ CHAN5G(5845, 39, 0), /* Channel 169 */ CHAN5G(5865, 40, 0), /* Channel 173 */ CHAN5G(5885, 41, 0), /* Channel 177 */ }; memcpy(ofdm_chantable, ofdm_chantable_l, ARRAY_SIZE(ofdm_chantable_l) * sizeof(struct ieee80211_channel)); } void setup_ofdm_rate_table() { struct ieee80211_rate ofdm_rates_l[] = { { .bitrate = 60, .hw_value = 12}, { .bitrate = 90, .hw_value = 18}, { .bitrate = 120, .hw_value = 24}, { .bitrate = 180, .hw_value = 36}, { .bitrate = 240, .hw_value = 48}, { .bitrate = 360, .hw_value = 72}, { .bitrate = 480, .hw_value = 96}, { .bitrate = 540, .hw_value = 108} }; memcpy(ofdm_rates, ofdm_rates_l, ARRAY_SIZE(ofdm_rates_l) * sizeof(struct ieee80211_rate)); } void setup_dsss_rate_table() { struct ieee80211_rate dsss_rates_l[] = { { .bitrate = 10, .hw_value = 2}, { .bitrate = 20, .hw_value = 4, .flags = IEEE80211_RATE_SHORT_PREAMBLE}, { .bitrate = 55, .hw_value = 11, .flags = IEEE80211_RATE_SHORT_PREAMBLE}, { .bitrate = 110, .hw_value = 22, .flags = IEEE80211_RATE_SHORT_PREAMBLE}, { .bitrate = 60, .hw_value = 12}, { .bitrate = 90, .hw_value = 18}, { .bitrate = 120, .hw_value = 24}, { .bitrate = 180, .hw_value = 36}, { .bitrate = 240, .hw_value = 48}, { .bitrate = 360, .hw_value = 72}, { .bitrate = 480, .hw_value = 96}, { .bitrate = 540, .hw_value = 108} }; memcpy(dsss_rates, dsss_rates_l, ARRAY_SIZE(dsss_rates_l) * sizeof(struct ieee80211_rate)); } void setup_2ghz_he_capability_table() { struct ieee80211_sband_iftype_data he_capa_2ghz_l = { /* TODO: should we support other types, e.g., P2P?*/ .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), .he_cap = { .has_he = true, .he_cap_elem = { .mac_cap_info[0] = (IEEE80211_HE_MAC_CAP0_HTC_HE | IEEE80211_HE_MAC_CAP0_TWT_REQ), .mac_cap_info[2] = IEEE80211_HE_MAC_CAP2_BSR, .phy_cap_info[4] = (IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4), }, .he_mcs_nss_supp = { .rx_mcs_80 = cpu_to_le16(0xfffc), .tx_mcs_80 = cpu_to_le16(0xfffc), .rx_mcs_160 = cpu_to_le16(0xffff), .tx_mcs_160 = cpu_to_le16(0xffff), .rx_mcs_80p80 = cpu_to_le16(0xffff), .tx_mcs_80p80 = cpu_to_le16(0xffff), }, }, }; memcpy(&he_capa_2ghz,&he_capa_2ghz_l,sizeof(struct ieee80211_sband_iftype_data)); } void setup_5ghz_he_capability_table() { struct ieee80211_sband_iftype_data he_capa_5ghz_l = { /* TODO: should we support other types, e.g., P2P?*/ .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), .he_cap = { .has_he = true, .he_cap_elem = { .mac_cap_info[0] = (IEEE80211_HE_MAC_CAP0_HTC_HE | IEEE80211_HE_MAC_CAP0_TWT_REQ), .mac_cap_info[2] = IEEE80211_HE_MAC_CAP2_BSR, .phy_cap_info[4] = (IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4), }, .he_mcs_nss_supp = { .rx_mcs_80 = cpu_to_le16(0xfffc), .tx_mcs_80 = cpu_to_le16(0xfffc), .rx_mcs_160 = cpu_to_le16(0xffff), .tx_mcs_160 = cpu_to_le16(0xffff), .rx_mcs_80p80 = cpu_to_le16(0xffff), .tx_mcs_80p80 = cpu_to_le16(0xffff), }, }, }; memcpy(&he_capa_5ghz,&he_capa_5ghz_l,sizeof(struct ieee80211_sband_iftype_data)); } void setup_channel_rate_capabilities() { setup_dsss_channel_table(); setup_ofdm_channel_table(); setup_ofdm_rate_table(); setup_dsss_rate_table(); band_2ghz.channels = dsss_chantable; band_2ghz.n_channels = ARRAY_SIZE(dsss_chantable); band_2ghz.band = NL80211_BAND_2GHZ; band_2ghz.bitrates = dsss_rates; band_2ghz.n_bitrates = ARRAY_SIZE(dsss_rates); band_5ghz.channels = ofdm_chantable; band_5ghz.n_channels = ARRAY_SIZE(ofdm_chantable); band_5ghz.band = NL80211_BAND_5GHZ; band_5ghz.bitrates = ofdm_rates; band_5ghz.n_bitrates = ARRAY_SIZE(ofdm_rates); setup_2ghz_he_capability_table(); setup_5ghz_he_capability_table(); } #ifdef RPU_HE_SUPPORT void setup_he_cap(struct ieee80211_supported_band *sband) { if (sband->band == NL80211_BAND_2GHZ) sband->iftype_data = (struct ieee80211_sband_iftype_data *)&he_capa_2ghz; else if (sband->band == NL80211_BAND_5GHZ) sband->iftype_data = (struct ieee80211_sband_iftype_data *)&he_capa_5ghz; else return; sband->n_iftype_data = 1; } #endif /* Interface combinations for Virtual interfaces */ static const struct ieee80211_iface_limit if_limit1[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION)} }; static const struct ieee80211_iface_limit if_limit2[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_STATION)}, { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_P2P_GO)} }; static const struct ieee80211_iface_limit if_limit3[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_P2P_CLIENT)} }; static const struct ieee80211_iface_limit if_limit4[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC)}, { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT)} }; #ifdef not_yet static const struct ieee80211_iface_limit if_limit6[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP)} }; #endif /* not_yet */ static const struct ieee80211_iface_limit if_limit7[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_STATION)} }; static const struct ieee80211_iface_combination if_comb[] = { { .limits = if_limit1, .n_limits = ARRAY_SIZE(if_limit1), .max_interfaces = 2, .num_different_channels = 1}, { .limits = if_limit2, .n_limits = ARRAY_SIZE(if_limit2), .max_interfaces = 2, .num_different_channels = 1}, { .limits = if_limit3, .n_limits = ARRAY_SIZE(if_limit3), .max_interfaces = 2, .num_different_channels = 1}, { .limits = if_limit4, .n_limits = ARRAY_SIZE(if_limit4), .max_interfaces = 2, .num_different_channels = 1}, }; static const struct wiphy_wowlan_support uccp_wowlan_support = { .flags = WIPHY_WOWLAN_ANY, }; #ifdef ENABLE_COALESCING_TX_PKTS int ieee80211_tx_update_sequence(struct ieee80211_tx_data *tx, struct ieee80211_hdr *hdr); int umac_softmac_mux_update_sequence(struct ieee80211_tx_data *tx, struct ieee80211_hdr *hdr, __u16 fc) { ieee80211_tx_update_sequence(tx, hdr); return 0; } void umac_softmac_mux_update_crypto_hdr(struct ieee80211_tx_data *tx, struct ieee80211_key_conf *hw_key, struct ieee80211_hdr *hdr, unsigned int hdrlen) { struct ieee80211_local *local = tx->local; struct ieee80211_key *key = tx->key; unsigned char *crypto_hdr_ptr = (unsigned char *)hdr + hdrlen; /* update IV */ switch (hw_key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: ieee80211_wep_get_iv(local, hw_key->keylen, hw_key->keyidx, crypto_hdr_ptr); break; case WLAN_CIPHER_SUITE_TKIP: ieee80211_tkip_update_iv(crypto_hdr_ptr, key); break; case WLAN_CIPHER_SUITE_CCMP: ieee80211_ccmp_update_pn(crypto_hdr_ptr, key); break; case WLAN_CIPHER_SUITE_CCMP_256: ieee80211_ccmp_update_pn(crypto_hdr_ptr, key); break; case WLAN_CIPHER_SUITE_GCMP_256: ieee80211_gcmp_update_pn(crypto_hdr_ptr,key); break; default: hpriv->unexpected_code_path ++; break; } return; } void umac_softmac_mux_copy_crypto_hdr(struct ieee80211_tx_data *tx, struct ieee80211_key_conf *hw_key, struct ieee80211_hdr *dest_hdr, struct ieee80211_hdr *src_hdr, unsigned int hdrlen) { struct ieee80211_local *local = tx->local; struct ieee80211_key *key = tx->key; int cryptp_hdr_len = 0; /* update IV */ switch (hw_key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: cryptp_hdr_len = IEEE80211_WEP_IV_LEN; break; case WLAN_CIPHER_SUITE_TKIP: cryptp_hdr_len = IEEE80211_TKIP_IV_LEN; break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256: cryptp_hdr_len = IEEE80211_CCMP_HDR_LEN; break; #ifdef RPU_WAPI_SUPPORT case WLAN_CIPHER_SUITE_SMS4: cryptp_hdr_len = 18; break; #endif case WLAN_CIPHER_SUITE_GCMP_256: cryptp_hdr_len = IEEE80211_GCMP_HDR_LEN; break; default: hpriv->unexpected_code_path ++; break; } memcpy(((unsigned char *)dest_hdr) + hdrlen, ((unsigned char *)src_hdr) + hdrlen, cryptp_hdr_len); return; } int umac_softmac_prepare_mac80211_hdrs(struct sk_buff *skb) { DEFINE_SPIN_LOCK_IRQ(oldIPL); struct nrf_wifi_tx_buff *tx_buff; struct ieee80211_hdr *ieee80211_hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr; __le16 ext_fc; #ifdef EXTMEM_UNALIGNED_ACCESS_NOT_PERMITED #define EXT_MEM_ALIGN_FIX_CRYPTO_LEN 32 unsigned char core_hdr_buff[sizeof(struct ieee80211_hdr) + EXT_MEM_ALIGN_FIX_CRYPTO_LEN + sizeof(umac_rfc1042_header)] = {0}; unsigned int cryptot_hdr_len = 0; #endif /* EXTMEM_UNALIGNED_ACCESS_NOT_PERMITED */ unsigned int ieee80211_hdr_len = ieee80211_hdrlen(ieee80211_hdr->frame_control); unsigned int hdrlen; unsigned char *ieee80211_qc = NULL; unsigned char *qc; struct ieee80211_tx_data *tx = skb->tx; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_key_conf *hw_key = info->control.hw_key; unsigned int pkt_count; struct img_data_tx_buff_desc *tx_buff_desc; unsigned int ext_addr; tx_buff = img_data_get_tx_buff(skb); if (tx_buff == NULL) { /* packet may be generated internally. */ return 0; } tx_buff_desc = (struct img_data_tx_buff_desc *)skb->rpu_ref; if (tx_buff->tx_buff_info[0].pkt_length != skb->len) { hpriv->unexpected_code_path ++; } if (ieee80211_is_data_qos(ieee80211_hdr->frame_control)) { ieee80211_qc = ieee80211_get_qos_ctl(ieee80211_hdr); } hdr = (struct ieee80211_hdr *)core_hdr_buff; ext_fc = hdr->frame_control; hdr->frame_control = ieee80211_hdr->frame_control; ext_fc = ieee80211_hdr->frame_control; hdrlen = ieee80211_hdrlen(ext_fc); /*Needs to change for U-APSD*/ if (tx_buff->mac_hdr_info.more_data == 1){ hdr->frame_control |= 0x2000; } hdr->duration_id = ieee80211_hdr->duration_id; memcpy(hdr->addr1, ieee80211_hdr->addr1, ETH_ALEN); memcpy(hdr->addr2, ieee80211_hdr->addr2, ETH_ALEN); memcpy(hdr->addr3, ieee80211_hdr->addr3, ETH_ALEN); if (ieee80211_has_protected(ext_fc)) { cryptot_hdr_len = info->control.hw_key->iv_len; } memcpy(&core_hdr_buff[ieee80211_hdrlen(ext_fc) + cryptot_hdr_len], (skb->data + ieee80211_hdrlen(ext_fc) + cryptot_hdr_len), sizeof(umac_rfc1042_header)); for (pkt_count = 0 ; pkt_count < tx_buff->num_tx_pkts; pkt_count ++) { update_jiffie(); if (ieee80211_is_data_qos(ext_fc)) { qc = ieee80211_get_qos_ctl(hdr); *((unsigned short *)qc) = *((unsigned short *)ieee80211_qc); if (pkt_count == tx_buff->num_tx_pkts - 1){ if (test_sta_flag(tx->sta, WLAN_STA_SP)){ if (tx_buff->mac_hdr_info.eosp == 1){ *((unsigned short *)qc) |= IEEE80211_QOS_CTL_EOSP; clear_sta_flag(tx->sta, WLAN_STA_SP); } } } } if (pkt_count == 0) { /* first time sequence number is filled through rcvd skb so simply assign that */ hdr->seq_ctrl = ieee80211_hdr->seq_ctrl; } else { umac_softmac_mux_update_sequence(tx, hdr, ext_fc); } tx_buff_desc->tx_cmd_seq_num[pkt_count] = hdr->seq_ctrl; /* populate crypto header */ if (ieee80211_has_protected(ext_fc)) { if (pkt_count == 0) { umac_softmac_mux_copy_crypto_hdr(tx, hw_key, hdr, ieee80211_hdr, hdrlen); } else { umac_softmac_mux_update_crypto_hdr(tx, hw_key, hdr, hdrlen); } #ifdef EXTMEM_UNALIGNED_ACCESS_NOT_PERMITED cryptot_hdr_len = info->control.hw_key->iv_len; #endif /* EXTMEM_UNALIGNED_ACCESS_NOT_PERMITED */ } #ifdef EXTMEM_UNALIGNED_ACCESS_NOT_PERMITED spin_lock_irqsave(&oldIPL, 0); /* Copying mac80211_hdr (26bytes) + crypto_hdr (8bytes/16bytes) + LLC_hdr (6bytes) from skb->data into the ddr_ptr * after moving ddr_ptr forward 12bytes to remove DA & SA from IP packet * and moving back mac80211_hdr (26bytes) + crypto_hdr (8bytes/16bytes) + LLC_hdr (6bytes) */ ext_addr = img_umac_ext_ram_access_addr(tx_buff->tx_buff_info[pkt_count].ddr_ptr+ETH_ALEN+ETH_ALEN-(ieee80211_hdrlen(ext_fc) + cryptot_hdr_len + sizeof(umac_rfc1042_header))); memcpy_unaligned((void *)ext_addr, (void *)hdr, ieee80211_hdrlen(ext_fc) + cryptot_hdr_len + sizeof(umac_rfc1042_header)); tx_buff->tx_buff_info[pkt_count].ddr_ptr = (tx_buff->tx_buff_info[pkt_count].ddr_ptr-(ieee80211_hdrlen(ext_fc) + cryptot_hdr_len-sizeof(umac_rfc1042_header))); tx_buff->tx_buff_info[pkt_count].pkt_length = tx_buff->tx_buff_info[pkt_count].pkt_length - (ETH_ALEN + ETH_ALEN) + (ieee80211_hdrlen(ext_fc) + cryptot_hdr_len + sizeof(umac_rfc1042_header)); spin_unlock_irqrestore(&oldIPL, 0); #endif /* EXTMEM_UNALIGNED_ACCESS_NOT_PERMITED */ } return 0; } #endif /* ENABLE_COALESCING_TX_PKTS */ #ifdef RPU_WAPI_SUPPORT #define WAPI_HDR_LEN 18 #define WAPI_PN_OFFSET 2 #define WAPI_KEY_IDX_OFF 0 #define WAPI_KEY_IDX_MASK 0X01 #define WAPI_KEY_IDX_SHIFT 0 #define WAPI_MIC_LEN 16 static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, WLAN_CIPHER_SUITE_CCMP_256, WLAN_CIPHER_SUITE_AES_CMAC, WLAN_CIPHER_SUITE_SMS4, WLAN_CIPHER_SUITE_GCMP, WLAN_CIPHER_SUITE_GCMP_256, WLAN_CIPHER_SUITE_BIP_GMAC_128, WLAN_CIPHER_SUITE_BIP_GMAC_256, WLAN_CIPHER_SUITE_BIP_CMAC_256 }; static const struct ieee80211_cipher_scheme cipher_schemes[] = { { .cipher = WLAN_CIPHER_SUITE_SMS4, .iftype = BIT(NL80211_IFTYPE_STATION), .hdr_len = WAPI_HDR_LEN, .pn_len = WAPI_PN_LEN, .pn_off = WAPI_PN_OFFSET, .key_idx_off = WAPI_KEY_IDX_OFF, .key_idx_mask = WAPI_KEY_IDX_MASK, .key_idx_shift = WAPI_KEY_IDX_SHIFT, .mic_len = WAPI_MIC_LEN } }; void bignum_inc(unsigned char *bn, int len) { int count = -1; while (++count < len) { if (((unsigned short)bn[count] + 1) > 0xff) { bn[count] += 1; continue; } bn[count] += 1; break; } } void wapi_hdr_build(struct sk_buff *skb) { struct umac_vif *vif; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; u8 *wapi_hdr; if (!hw_key) return; if (hw_key->cipher != WLAN_CIPHER_SUITE_SMS4) return; if (!ieee80211_is_data(hdr->frame_control)){ // MAC80211 bug : undo protected bit, iv space for mgmt frames if (ieee80211_has_protected(hdr->frame_control)){ hdr->frame_control &= ~IEEE80211_FCTL_PROTECTED; memmove(skb->data + WAPI_HDR_LEN, skb->data, ieee80211_hdrlen(hdr->frame_control)); skb_pull(skb, WAPI_HDR_LEN); } return; } vif = (struct umac_vif *)(tx_info->control.vif->drv_priv); wapi_hdr = skb->data; wapi_hdr += ieee80211_hdrlen(hdr->frame_control); memset(wapi_hdr, 0x0, WAPI_HDR_LEN); // update keyIdx wapi_hdr[0] = hw_key->keyidx; // 8.2.4 Rules for using data message serial number PN // Before a unicast frame is sent, the ASUE or // AE shall first increase its PN value by 2 bignum_inc(vif->pn128, sizeof(vif->pn128)); bignum_inc(vif->pn128, sizeof(vif->pn128)); // update PN memcpy(wapi_hdr + 2, vif->pn128, sizeof(vif->pn128)); } void wapi_pn_init(struct umac_vif *vif) { char pn_asue[16]={ 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c, }; // Once the USK is updated, the ASUE and AE initialize // their PN values memcpy(vif->pn128, pn_asue, sizeof(pn_asue)); } void wapi_init(struct ieee80211_hw *hw) { hw->wiphy->cipher_suites = cipher_suites; hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); hw->cipher_schemes = cipher_schemes; hw->n_cipher_schemes = ARRAY_SIZE(cipher_schemes); } #endif /* RPU_WAPI_SUPPORT */ void tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctl, struct sk_buff *skb) { struct img_priv *priv = hw->priv; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct umac_vif *uvif; unsigned char null_bssid[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; struct lmac_event_noa noa_event; #ifdef RPU_WAPI_SUPPORT wapi_hdr_build(skb); #endif /* RPU_WAPI_SUPPORT */ IMG_TX_DBG_PARAM_INCR(softmac_tx, 1); if (tx_info->control.vif == NULL) { RPU_DEBUG_UMACIF("%s: Dropping injected TX frame\n", priv->name); dev_kfree_skb_any(skb); return; } uvif = (struct umac_vif *)(tx_info->control.vif->drv_priv); if (ether_addr_equal(hdr->addr3, null_bssid)) goto tx_status; if (uvif->vif->type != NL80211_IFTYPE_AP) { if ((priv->power_save == PWRSAVE_STATE_AWAKE) && (!wifi->params.disable_power_save) && (((hdr->frame_control & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) || is_bufferable_mgmt_frame(hdr))) { hdr->frame_control |= IEEE80211_FCTL_PM; } } if (uvif->noa_active) { memset(&noa_event, 0, sizeof(noa_event)); noa_event.if_index = uvif->vif_index; rpu_noa_event(FROM_TX, &noa_event, priv, skb); return; } rpu_tx_frame(skb, txctl->sta, priv, false); return; tx_status: tx_info->flags |= IEEE80211_TX_STAT_ACK; tx_info->status.rates[0].count = 1; ieee80211_tx_status(hw, skb); } int start(struct ieee80211_hw *hw) { struct img_priv *priv = (struct img_priv *)hw->priv; int ret = 0; RPU_DEBUG_UMACIF("%s-80211IF: In start\n", priv->name); mutex_lock(&priv->mutex); if ((rpu_core_init(priv, 0)) < 0) { RPU_DEBUG_UMACIF("%s-80211IF: umac init failed\n", priv->name); ret = -ENODEV; goto out; } INIT_DELAYED_WORK(&priv->roc_complete_work, rpu_roc_complete_work); priv->state = STARTED; out: mutex_unlock(&priv->mutex); return ret; } void stop(struct ieee80211_hw *hw) { struct img_priv *priv= (struct img_priv *)hw->priv; RPU_DEBUG_UMACIF("%s-80211IF:In stop\n", priv->name); mutex_lock(&priv->mutex); rpu_core_deinit(priv, 0); priv->state = STOPPED; mutex_unlock(&priv->mutex); } int add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct img_priv *priv= hw->priv; struct ieee80211_vif *v; struct umac_vif *uvif; int vif_index, iftype; mutex_lock(&priv->mutex); iftype = vif->type; v = vif; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; if (priv->current_vif_count == wifi->params.num_vifs) { pr_err("%s: Exceeded Maximum supported VIF's cur:%d max: %d.\n", __func__, priv->current_vif_count, wifi->params.num_vifs); mutex_unlock(&priv->mutex); return -ENOTSUPP; } if (!(iftype == NL80211_IFTYPE_STATION || iftype == NL80211_IFTYPE_ADHOC || iftype == NL80211_IFTYPE_AP)) { pr_err("Invalid Interface type\n"); return -ENOTSUPP; } for (vif_index = 0; vif_index < wifi->params.num_vifs; vif_index++) { if (!(priv->active_vifs & (1 << vif_index))) break; } /* This should never happen, we have taken care of this above */ if (vif_index == wifi->params.num_vifs) { pr_err("%s: All VIF's are busy: %pM\n", __func__, vif->addr); mutex_unlock(&priv->mutex); return -EINVAL; } uvif = (struct umac_vif *)&v->drv_priv; uvif->vif_index = vif_index; uvif->vif = v; uvif->priv = priv; uvif->seq_no = 0; rpu_vif_add(uvif); priv->active_vifs |= (1 << vif_index); priv->current_vif_count++; if (iftype == NL80211_IFTYPE_ADHOC) priv->tx_last_beacon = 0; rcu_assign_pointer(priv->vifs[vif_index], v); synchronize_rcu(); mutex_unlock(&priv->mutex); return 0; } void remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct img_priv *priv= hw->priv; struct ieee80211_vif *v; int vif_index; mutex_lock(&priv->mutex); v = vif; vif_index = ((struct umac_vif *)&v->drv_priv)->vif_index; rpu_vif_remove((struct umac_vif *)&v->drv_priv); priv->active_vifs &= ~(1 << vif_index); rcu_assign_pointer(priv->vifs[vif_index], NULL); synchronize_rcu(); priv->current_vif_count--; mutex_unlock(&priv->mutex); } int config(struct ieee80211_hw *hw, unsigned int changed) { struct img_priv *priv = hw->priv; struct ieee80211_conf *conf = &hw->conf; unsigned int pri_chnl_num; unsigned int freq_band; unsigned int ch_width; unsigned int center_freq = 0; unsigned int center_freq1 = 0; unsigned int center_freq2 = 0; int i = 0; int err = 0; struct ieee80211_vif *vif = NULL; int ret = 0; mutex_lock(&priv->mutex); if (changed & IEEE80211_CONF_CHANGE_POWER) { priv->txpower = conf->power_level; CALL_RPU(rpu_prog_txpower, priv->txpower); } /* Check for change in channel */ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { center_freq = conf->chandef.chan->center_freq; center_freq1 = conf->chandef.center_freq1; center_freq2 = conf->chandef.center_freq2; freq_band = conf->chandef.chan->band; ch_width = conf->chandef.width; pri_chnl_num = ieee80211_frequency_to_channel(center_freq); RPU_DEBUG_UMACIF("%s-80211IF:Primary Channel is %d\n", priv->name, pri_chnl_num); err = rpu_prog_channel(pri_chnl_num, center_freq1, center_freq2, ch_width, freq_band); if (err) { mutex_unlock(&priv->mutex); return err; } #ifdef not_yet if (conf->radar_enabled) { RPU_DEBUG_UMACIF("RADAR Detection ENABLED on "); RPU_DEBUG_UMACIF("PriChannel=%d with ch_width=%d\n", pri_chnl_num, ch_width); CALL_RPU(rpu_prog_radar_detect, RADAR_DETECT_OP_START); } #endif /* not_yet */ } /* Check for change in Power save state */ for (i = 0; i < MAX_VIFS; i++) { if (!(changed & IEEE80211_CONF_CHANGE_PS)) break; if (!(priv->active_vifs & (1 << i))) continue; /* When ROC is in progress, do not mess with * PS state */ if (priv->roc_params.roc_in_progress) continue; if (wifi->params.disable_power_save) continue; if (conf->flags & IEEE80211_CONF_PS) priv->power_save = SLEEP; else priv->power_save = AWAKE; RPU_DEBUG_UMACIF("%s-80211IF:PS state of VIF", priv->name); RPU_DEBUG_UMACIF(" %d changed to %d\n", i, priv->power_save); rcu_read_lock(); vif = rcu_dereference(priv->vifs[i]); rcu_read_unlock(); rpu_prog_ps_state(i, vif->addr, priv->power_save); } /* TODO: Make this global config as it effects all VIF's */ for (i = 0; i < MAX_VIFS; i++) { if (!(changed & IEEE80211_CONF_CHANGE_SMPS)) break; if (!(priv->active_vifs & (1 << i))) continue; RPU_DEBUG_UMACIF("%s-80211IF:MIMO PS state of VIF %d -> %d\n", priv->name, i, conf->smps_mode); rcu_read_lock(); vif = rcu_dereference(priv->vifs[i]); rcu_read_unlock(); rpu_prog_vif_smps(i, vif->addr, conf->smps_mode); } /* Check for change in Retry Limits */ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { RPU_DEBUG_UMACIF("%s-80211IF:Retry Limits changed", priv->name); RPU_DEBUG_UMACIF(" to %d and %d\n", conf->short_frame_max_tx_count, conf->long_frame_max_tx_count); } for (i = 0; i < MAX_VIFS; i++) { if (!(changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)) break; if (!(priv->active_vifs & (1 << i))) continue; rcu_read_lock(); vif = rcu_dereference(priv->vifs[i]); rcu_read_unlock(); rpu_prog_short_retry(i, vif->addr, conf->short_frame_max_tx_count); rpu_prog_long_retry(i, vif->addr, conf->long_frame_max_tx_count); } prog_rpu_fail: mutex_unlock(&priv->mutex); return ret; } u64 prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) { struct img_priv *priv = hw->priv; int i; struct netdev_hw_addr *ha; int mc_count = 0; int ret = 0; if (priv->state != STARTED) return 0; mc_count = netdev_hw_addr_list_count(mc_list); { if (mc_count > MCST_ADDR_LIMIT) { mc_count = 0; pr_warn("%s-80211IF:Disabling MCAST filter (cnt=%d)\n", priv->name, mc_count); goto out; } } RPU_DEBUG_UMACIF("%s-80211IF: Multicast filter count", priv->name); RPU_DEBUG_UMACIF("adding: %d removing: %d\n", mc_count, priv->mc_filter_count); if (priv->mc_filter_count > 0) { /* Remove all previous multicast addresses from the LMAC */ for (i = 0; i < priv->mc_filter_count; i++) rpu_prog_mcast_addr_cfg(priv->mc_filters[i], WLAN_MCAST_ADDR_REM); } i = 0; netdev_hw_addr_list_for_each(ha, mc_list) { /* Prog the multicast address into the LMAC */ CALL_RPU(rpu_prog_mcast_addr_cfg, ha->addr, WLAN_MCAST_ADDR_ADD); memcpy(priv->mc_filters[i], ha->addr, 6); i++; } priv->mc_filter_count = mc_count; out: return mc_count; prog_rpu_fail: return ret; } void configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, u64 mc_count) { struct img_priv *priv = hw->priv; int ret = 0; mutex_lock(&priv->mutex); changed_flags &= SUPPORTED_FILTERS; *new_flags &= SUPPORTED_FILTERS; if (priv->state != STARTED) { mutex_unlock(&priv->mutex); return; } if ((*new_flags & FIF_ALLMULTI) || (mc_count == 0)) { /* Disable the multicast filter in LMAC */ RPU_DEBUG_UMACIF("%s-80211IF: Multicast filters disabled\n", priv->name); CALL_RPU(rpu_prog_mcast_filter_control, MCAST_FILTER_DISABLE); } else if (mc_count) { /* Enable the multicast filter in LMAC */ RPU_DEBUG_UMACIF("%s-80211IF: Multicast filters enabled\n", priv->name); CALL_RPU(rpu_prog_mcast_filter_control, MCAST_FILTER_ENABLE); } if (changed_flags == 0) /* No filters which we support changed */ goto out; if (wifi->params.production_test == 0) { if (*new_flags & FIF_BCN_PRBRESP_PROMISC) { /* Receive all beacons and probe responses */ RPU_DEBUG_UMACIF("%s-80211IF: RCV ALL bcns\n", priv->name); CALL_RPU(rpu_prog_rcv_bcn_mode, RCV_ALL_BCNS); } else { /* Receive only network beacons and probe responses */ RPU_DEBUG_UMACIF("%s-80211IF: RCV NW bcns\n", priv->name); CALL_RPU(rpu_prog_rcv_bcn_mode, RCV_ALL_NETWORK_ONLY); } } out: prog_rpu_fail: mutex_unlock(&priv->mutex); } int conf_vif_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, unsigned short queue, const struct ieee80211_tx_queue_params *txq_params) { struct img_priv *priv = hw->priv; int vif_index, vif_active; struct edca_params params; struct ieee80211_vif *vif_local = NULL; mutex_lock(&priv->mutex); for (vif_index = 0; vif_index < wifi->params.num_vifs; vif_index++) { if (!(priv->active_vifs & (1 << vif_index))) continue; rcu_read_lock(); vif_local = rcu_dereference(priv->vifs[vif_index]); rcu_read_unlock(); if (ether_addr_equal(vif_local->addr, vif->addr)) break; } if (WARN_ON(vif_index == wifi->params.num_vifs)) { mutex_unlock(&priv->mutex); return -EINVAL; } vif_active = 0; if ((priv->active_vifs & (1 << vif_index))) vif_active = 1; memset(¶ms, 0, sizeof(params)); params.aifs = txq_params->aifs; params.txop = txq_params->txop; params.cwmin = txq_params->cw_min; params.cwmax = txq_params->cw_max; params.uapsd = txq_params->uapsd; rpu_vif_set_edca_params(queue, (struct umac_vif *)&vif->drv_priv, ¶ms, vif_active); mutex_unlock(&priv->mutex); return 0; } int set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { struct umac_key sec_key; unsigned int result = 0; struct img_priv *priv = hw->priv; unsigned int cipher_type, key_type; int vif_index; struct umac_vif *uvif; uvif = ((struct umac_vif *)&vif->drv_priv); memset(&sec_key, 0, sizeof(struct umac_key)); switch (key_conf->cipher) { case WLAN_CIPHER_SUITE_WEP40: sec_key.key = key_conf->key; cipher_type = CIPHER_TYPE_WEP40; break; case WLAN_CIPHER_SUITE_WEP104: sec_key.key = key_conf->key; cipher_type = CIPHER_TYPE_WEP104; break; case WLAN_CIPHER_SUITE_TKIP: key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; /* We get the key in the following form: * KEY (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) */ sec_key.key = key_conf->key; sec_key.tx_mic = key_conf->key + 16; sec_key.rx_mic = key_conf->key + 24; cipher_type = CIPHER_TYPE_TKIP; break; case WLAN_CIPHER_SUITE_CCMP: sec_key.key = key_conf->key; cipher_type = CIPHER_TYPE_CCMP; break; case WLAN_CIPHER_SUITE_CCMP_256: sec_key.key = key_conf->key; cipher_type = CIPHER_TYPE_CCMP_256; break; case WLAN_CIPHER_SUITE_GCMP: sec_key.key = key_conf->key; cipher_type = CIPHER_TYPE_GCMP; break; case WLAN_CIPHER_SUITE_GCMP_256: sec_key.key = key_conf->key; cipher_type = CIPHER_TYPE_GCMP_256; break; /* this one needs to be done in software */ case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256: case WLAN_CIPHER_SUITE_BIP_CMAC_256: /* return the value 1 to permit this specific key/algorithm to be done in software */ return 1; #ifdef RPU_WAPI_SUPPORT case WLAN_CIPHER_SUITE_SMS4: key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; sec_key.key = key_conf->key; sec_key.tx_mic = key_conf->key + 16; sec_key.rx_mic = key_conf->key + 24; cipher_type = CIPHER_TYPE_WAPI; wapi_pn_init(uvif); break; #endif /* RPU_WAPI_SUPPORT */ default: result = -EOPNOTSUPP; goto out; } vif_index = ((struct umac_vif *)&vif->drv_priv)->vif_index; mutex_lock(&priv->mutex); if (cmd == SET_KEY) { key_conf->hw_key_idx = 0; /* Don't really use this */ /* This flag indicate that it requires IV generation */ #ifdef RPU_WAPI_SUPPORT if (key_conf->cipher != WLAN_CIPHER_SUITE_SMS4) #endif /* RPU_WAPI_SUPPORT */ key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; if (cipher_type == CIPHER_TYPE_WEP40 || cipher_type == CIPHER_TYPE_WEP104) { RPU_DEBUG_CRYPTO("%s-80211IF: ADD IF KEY (WEP).", priv->name); RPU_DEBUG_CRYPTO(" vif_index = %d,", vif_index); RPU_DEBUG_CRYPTO(" keyidx = %d, cipher_type = %d\n", key_conf->keyidx, cipher_type); rpu_prog_if_key(vif_index, vif->addr, KEY_CTRL_ADD, key_conf->keyidx, cipher_type, &sec_key); } else if (sta) { sec_key.peer_mac = sta->addr; if (key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE) key_type = KEY_TYPE_UCAST; else key_type = KEY_TYPE_BCAST; RPU_DEBUG_CRYPTO("%s-80211IF: ADD PEER KEY (WPA/WPA2)", priv->name); RPU_DEBUG_CRYPTO(" vif_index = %d,", vif_index); RPU_DEBUG_CRYPTO(" keyidx = %d, keytype = %d,", key_conf->keyidx, key_type); RPU_DEBUG_CRYPTO(" cipher_type = %d\n", cipher_type); rpu_prog_peer_key(vif_index, vif->addr, KEY_CTRL_ADD, key_conf->keyidx, key_type, cipher_type, &sec_key); } else { key_type = KEY_TYPE_BCAST; if (vif->type == NL80211_IFTYPE_STATION) { sec_key.peer_mac = (unsigned char *)vif->bss_conf.bssid; memcpy(uvif->bssid, (vif->bss_conf.bssid), ETH_ALEN); RPU_DEBUG_CRYPTO("%s-80211IF: ADD PEER KEY ", priv->name); RPU_DEBUG_CRYPTO("(BCAST-STA). vif_index = %d", vif_index); RPU_DEBUG_CRYPTO(", keyidx = %d, keytype = %d", key_conf->keyidx, key_type); RPU_DEBUG_CRYPTO(", cipher_type = %d\n", cipher_type); rpu_prog_peer_key(vif_index, vif->addr, KEY_CTRL_ADD, key_conf->keyidx, key_type, cipher_type, &sec_key); } else if (vif->type == NL80211_IFTYPE_AP) { RPU_DEBUG_CRYPTO("%s-80211IF: ADD IF KEY ", priv->name); RPU_DEBUG_CRYPTO("(BCAST-AP). vif_index = %d", vif_index); RPU_DEBUG_CRYPTO(", keyidx = %d", key_conf->keyidx); RPU_DEBUG_CRYPTO(", cipher_type = %d\n", cipher_type); rpu_prog_if_key(vif_index, vif->addr, KEY_CTRL_ADD, key_conf->keyidx, cipher_type, &sec_key); } else { /* ADHOC */ /* TODO: Check this works for IBSS RSN */ RPU_DEBUG_CRYPTO("%s-80211IF: ADD IF KEY ", priv->name); RPU_DEBUG_CRYPTO("(BCAST-IBSS).vif_index = %d", vif_index); RPU_DEBUG_CRYPTO(", keyidx = %d", key_conf->keyidx); RPU_DEBUG_CRYPTO(", cipher_type = %d\n", cipher_type); rpu_prog_if_key(vif_index, vif->addr, KEY_CTRL_ADD, key_conf->keyidx, cipher_type, &sec_key); } } } else if (cmd == DISABLE_KEY) { if ((cipher_type == CIPHER_TYPE_WEP40) || (cipher_type == CIPHER_TYPE_WEP104)) { rpu_prog_if_key(vif_index, vif->addr, KEY_CTRL_DEL, key_conf->keyidx, cipher_type, &sec_key); RPU_DEBUG_CRYPTO("%s-80211IF: DEL IF KEY (WEP).", priv->name); RPU_DEBUG_CRYPTO(" vif_index = %d, keyidx = %d", vif_index, key_conf->keyidx); RPU_DEBUG_CRYPTO(", cipher_type = %d\n", cipher_type); } else if (sta) { sec_key.peer_mac = sta->addr; if (key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE) key_type = KEY_TYPE_UCAST; else key_type = KEY_TYPE_BCAST; RPU_DEBUG_CRYPTO("%s-80211IF: DEL IF KEY (WPA/WPA2).", priv->name); RPU_DEBUG_CRYPTO(" vif_index = %d, keyidx = %d", vif_index, key_conf->keyidx); RPU_DEBUG_CRYPTO(", cipher_type = %d\n", cipher_type); rpu_prog_peer_key(vif_index, vif->addr, KEY_CTRL_DEL, key_conf->keyidx, key_type, cipher_type, &sec_key); } else { if (vif->type == NL80211_IFTYPE_STATION) { sec_key.peer_mac = uvif->bssid; RPU_DEBUG_CRYPTO("%s-80211IF: DEL IF KEY ", priv->name); RPU_DEBUG_CRYPTO("(BCAST-STA). vif_index = %d", vif_index); RPU_DEBUG_CRYPTO(", keyidx = %d", key_conf->keyidx); RPU_DEBUG_CRYPTO(", cipher_type = %d\n", cipher_type); rpu_prog_peer_key(vif_index, vif->addr, KEY_CTRL_DEL, key_conf->keyidx, KEY_TYPE_BCAST, cipher_type, &sec_key); } else if (vif->type == NL80211_IFTYPE_AP) { RPU_DEBUG_CRYPTO("%s-80211IF: DEL IF KEY ", priv->name); RPU_DEBUG_CRYPTO("(BCAST-AP). vif_index = %d", vif_index); RPU_DEBUG_CRYPTO(", keyidx = %d", key_conf->keyidx); RPU_DEBUG_CRYPTO(", cipher_type = %d\n", cipher_type); rpu_prog_if_key(vif_index, vif->addr, KEY_CTRL_DEL, key_conf->keyidx, cipher_type, &sec_key); } else { RPU_DEBUG_CRYPTO("%s-80211IF: DEL IF KEY ", priv->name); RPU_DEBUG_CRYPTO("(BCAST-IBSS).vif_index = %d", vif_index); RPU_DEBUG_CRYPTO(", keyidx = %d", key_conf->keyidx); RPU_DEBUG_CRYPTO(", cipher_type = %d\n", cipher_type); rpu_prog_if_key(vif_index, vif->addr, KEY_CTRL_DEL, key_conf->keyidx, cipher_type, &sec_key); } } } mutex_unlock(&priv->mutex); out: return result; } void bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, unsigned int changed) { struct img_priv *priv= hw->priv; mutex_lock(&priv->mutex); rpu_vif_bss_info_changed((struct umac_vif *)&vif->drv_priv, bss_conf, changed); mutex_unlock(&priv->mutex); } void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info) { int i; memset(ht_info, 0, sizeof(*ht_info)); ht_info->ht_supported = true; pr_info("SETUP HT CALLED\n"); ht_info->cap = 0; ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; ht_info->cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT; ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K; ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); if (wifi->params.max_tx_streams != wifi->params.max_rx_streams) { ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; ht_info->mcs.tx_params |= ((wifi->params.max_tx_streams - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); } ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; for (i = 0; i < wifi->params.max_rx_streams; i++) ht_info->mcs.rx_mask[i] = 0xff; } #define IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT 13 #define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT 16 void setup_vht_cap(struct ieee80211_sta_vht_cap *vht_info) { if (!vht_support) return; memset(vht_info, 0, sizeof(*vht_info)); vht_info->vht_supported = true; pr_info("SETUP VHT CALLED\n"); vht_info->cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | /*64KB Rx buffer size*/ (3 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT) | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT) | (1 << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT) ; /* 1x1 */ if ((wifi->params.max_tx_streams == 1) && (wifi->params.max_rx_streams == 1)) { vht_info->vht_mcs.rx_mcs_map = ((IEEE80211_VHT_MCS_SUPPORT_0_7) << (2*0)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*1)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*2)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*3)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*4)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*5)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*6)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*7)); } /*2x2 */ if ((wifi->params.max_tx_streams == 2) && (wifi->params.max_rx_streams == 2)) { vht_info->vht_mcs.rx_mcs_map = ((IEEE80211_VHT_MCS_SUPPORT_0_7) << (2*0)) | ((IEEE80211_VHT_MCS_SUPPORT_0_7) << (2*1)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*2)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*3)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*4)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*5)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*6)) | ((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*7)); } vht_info->vht_mcs.tx_mcs_map = vht_info->vht_mcs.rx_mcs_map; } void set_hw_flags(struct ieee80211_hw *hw) { ieee80211_hw_set(hw, SIGNAL_DBM); ieee80211_hw_set(hw, SUPPORTS_PS); ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); if (data_config_params_g.aggregation){ ieee80211_hw_set(hw, AMPDU_AGGREGATION); } ieee80211_hw_set(hw, MFP_CAPABLE); ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); if (wifi->params.dot11a_support) ieee80211_hw_set(hw, SPECTRUM_MGMT); ieee80211_hw_set(hw, SUPPORTS_PER_STA_GTK); ieee80211_hw_set(hw, CONNECTION_MONITOR); ieee80211_hw_set(hw, CHANCTX_STA_CSA); ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); } void init_hw(struct ieee80211_hw *hw) { struct img_priv *priv= (struct img_priv *)hw->priv; int num_if_comb = 0; /* Supported Interface Types and other Default values*/ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); hw->wiphy->iface_combinations = if_comb; num_if_comb = (sizeof(if_comb) / sizeof(struct ieee80211_iface_combination)); hw->wiphy->n_iface_combinations = num_if_comb; set_hw_flags(hw); hw->wiphy->max_scan_ssids = MAX_NUM_SSIDS; /* 4 */ /* Low priority bg scan */ hw->wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; hw->max_listen_interval = 10; hw->wiphy->max_remain_on_channel_duration = 5000; /*ROC*/ hw->offchannel_tx_hw_queue = LMAC_AC_VO; hw->max_rates = 4; hw->max_rate_tries = 5; hw->queues = 4; if (data_config_params_g.reorder_buf_size) hw->max_rx_aggregation_subframes = data_config_params_g.reorder_buf_size; /* Size */ hw->extra_tx_headroom = 0; hw->vif_data_size = sizeof(struct umac_vif); hw->sta_data_size = sizeof(struct umac_sta); if (wifi->params.dot11g_support) { hw->wiphy->bands[NL80211_BAND_2GHZ] = &band_2ghz; if (ht_support){ setup_ht_cap(&hw->wiphy->bands[NL80211_BAND_2GHZ]->ht_cap); } #ifdef RPU_HE_SUPPORT setup_he_cap(&band_2ghz); #endif } if (wifi->params.dot11a_support) { hw->wiphy->bands[NL80211_BAND_5GHZ] = &band_5ghz; #ifdef RPU_HE_SUPPORT setup_he_cap(&band_5ghz); #endif if (vht_support){ setup_vht_cap(&band_5ghz.vht_cap); } if (ht_support){ setup_ht_cap(&band_5ghz.ht_cap); } } memset(hw->wiphy->addr_mask, 0, sizeof(hw->wiphy->addr_mask)); if (wifi->params.num_vifs == 1) { hw->wiphy->addresses = NULL; SET_IEEE80211_PERM_ADDR(hw, priv->if_mac_addresses[0].addr); } else { hw->wiphy->n_addresses = wifi->params.num_vifs; hw->wiphy->addresses = priv->if_mac_addresses; } hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; #ifdef RPU_WAPI_SUPPORT wapi_init(hw); #endif /* RPU_WAPI_SUPPORT */ hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; if (!wifi->params.disable_power_save && !wifi->params.disable_sm_power_save) { /* SMPS Support both Static and Dynamic */ hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS; hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS; } #ifdef WOWLAN #ifdef CONFIG_PM hw->wiphy->wowlan = &uccp_wowlan_support; #endif /* CONFIG_PM */ #endif /* WOWLAN */ } int ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params) { int ret = 0; unsigned int val = 0; struct img_priv *priv = (struct img_priv *)hw->priv; struct ieee80211_sta *sta = params->sta; enum ieee80211_ampdu_mlme_action action = params->action; u16 tid = params->tid; u16 ssn = params->ssn; switch (action) { case IEEE80211_AMPDU_RX_START: { val = tid | TID_INITIATOR_AP; priv->tid_info[val].tid_state = TID_STATE_AGGR_START; priv->tid_info[val].ssn = ssn; rpu_prog_ba_session_data(1, tid, &priv->tid_info[val].ssn, 1, vif->addr, (unsigned char *)(vif->bss_conf.bssid)); } break; case IEEE80211_AMPDU_RX_STOP: { val = tid | TID_INITIATOR_AP; priv->tid_info[val].tid_state = TID_STATE_AGGR_STOP; rpu_prog_ba_session_data(0, tid, &priv->tid_info[val].ssn, 1, vif->addr, (unsigned char *)(vif->bss_conf.bssid)); } break; case IEEE80211_AMPDU_TX_START: { val = tid | TID_INITIATOR_STA; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); priv->tid_info[val].tid_state = TID_STATE_AGGR_START; priv->tid_info[val].ssn = ssn; } break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_STOP_CONT: { val = tid | TID_INITIATOR_STA; priv->tid_info[val].tid_state = TID_STATE_AGGR_STOP; ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); } break; case IEEE80211_AMPDU_TX_OPERATIONAL: { val = tid | TID_INITIATOR_STA; priv->tid_info[val].tid_state = TID_STATE_AGGR_OPERATIONAL; } break; default: pr_err("%s: Invalid command, ignoring\n", __func__); } return ret; } int set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) { struct img_priv *priv = (struct img_priv *)hw->priv; /* Maximum no of antenna supported =2 */ if (!tx_ant || (tx_ant & ~3) || !rx_ant || (rx_ant & ~3)) return -EINVAL; priv->tx_antenna = (tx_ant & 3); return 0; } /* Needed in case of IBSS to send out probe responses when we are beaconing */ int tx_last_beacon(struct ieee80211_hw *hw) { struct img_priv *priv = (struct img_priv *)hw->priv; return priv->tx_last_beacon; } int scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *ireq) { (void)hw; struct umac_vif *uvif = (struct umac_vif *)vif->drv_priv; struct scan_req scan_req = {0}; int i = 0; struct cfg80211_scan_request *req; req = &ireq->req; scan_req.n_ssids = req->n_ssids; scan_req.n_channels = req->n_channels; scan_req.ie_len = req->ie_len; if (wifi->params.hw_scan_status != HW_SCAN_STATUS_NONE) return -EBUSY; /* Already in HW SCAN State */ /* Keep track of HW Scan requests and compeltes */ wifi->params.hw_scan_status = HW_SCAN_STATUS_PROGRESS; if (req->ie_len) memcpy(scan_req.ie, req->ie, req->ie_len); for (i = 0; i < req->n_channels; i++) { scan_req.center_freq[i] = req->channels[i]->center_freq; scan_req.freq_max_power[i] = req->channels[i]->max_power; scan_req.chan_flags[i] = req->channels[i]->flags; /* The type of scan comes from mac80211 so its taken care of */ } scan_req.p2p_probe = req->no_cck; /* For hostapd scan (40MHz) and scan_type=passive, n_ssids=0 * and req->ssids is NULL */ if (req->n_ssids > 0) { for (i = 0; i < req->n_ssids; i++) { scan_req.ssids[i].ssid_len = req->ssids[i].ssid_len; if (req->ssids[i].ssid_len > 0) memcpy(scan_req.ssids[i].ssid, req->ssids[i].ssid, req->ssids[i].ssid_len); } } return rpu_scan(uvif->vif_index, &scan_req); } void rpu_scan_complete(void *context, struct lmac_event_scanres *scan_res, unsigned char *skb, unsigned int len) { (void)skb; (void)len; struct img_priv *priv = (struct img_priv *)context; struct cfg80211_scan_info info; RPU_DEBUG_SCAN("Event Scan Complete from RPU:"); RPU_DEBUG_SCAN(" More_results: 0, Scan is Completed\n"); /* There can be a race where we receive remove_interface and * abort the scan(1) * But we get scan_complete from the FW(2), this check will make * sure we are not calling scan_complete when we have already * aborted the scan. Eg: Killing wpa_supplicant in middle of * scanning */ if (wifi->params.hw_scan_status != HW_SCAN_STATUS_NONE) { IMG_CMD_EVENT_DBG_PARAM_INCR(umac_scan_complete,1); info.aborted = false; ieee80211_scan_completed(priv->hw, &info); /* Keep track of HW Scan requests and compeltes */ wifi->params.hw_scan_status = HW_SCAN_STATUS_NONE; } } void cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct umac_vif *uvif = (struct umac_vif *)vif->drv_priv; struct img_priv *priv = NULL; struct cfg80211_scan_info info; priv= (struct img_priv *)hw->priv; if (wifi->params.hw_scan_status == HW_SCAN_STATUS_PROGRESS) { pr_info("Aborting pending scan request...\n"); priv->scan_abort_done = 0; if (rpu_scan_abort(uvif->vif_index)) return; if (!wait_for_scan_abort(priv)) { info.aborted = true; ieee80211_scan_completed(hw, &info); wifi->params.hw_scan_status = HW_SCAN_STATUS_NONE; IMG_CMD_EVENT_DBG_PARAM_INCR(umac_scan_complete,1); return; } } } int set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct img_priv *priv = NULL; priv = (struct img_priv *)hw->priv; /*if thres>=2347 (default case) hostapd sends down (u32) -1*/ if (value > 65536) priv->rts_threshold = 65536; else priv->rts_threshold = value; return 0; } void channel_switch_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_chan_def *chandef) { pr_err("RECEIVED CHANNEL SWITCH BEACON\n"); } #ifdef RPU_SNAPSHOT #ifdef CONFIG_NL80211_TESTMODE const struct nla_policy rpu_testmode_policy[RPU_TM_ATTR_MAX + 1] = { [RPU_TM_ATTR_CMD] = { .type = NLA_U32 }, [RPU_TM_ATTR_DUMP] = { .type = NLA_UNSPEC }, }; #define CB_ARG_OFFSET_ID 3 #define CB_ARG_OFFSET_DUMP_START 4 #define CB_ARG_OFFSET_DUMP_LEN 5 /*Assuming minium dump of MAX_NL_DUMP_LEN*/ /* Control Buffer is used as below * cb[3] ==> To identify First Command * cb[4] ==> Storing DUMP Start * cb[5] ==> Dump Len */ int rpu_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len) { int idx = 0; int err; int cmd = 0; long dump_start = 0; char *curr_dump; unsigned long dump_len = cb->args[CB_ARG_OFFSET_DUMP_LEN], curr_msg_len = MAX_NL_DUMP_LEN; unsigned long no_of_msgs = dump_len/MAX_NL_DUMP_LEN; struct nlattr *tb[RPU_TM_ATTR_MAX + 1]; struct img_priv *priv = (struct img_priv *)hw->priv; mutex_lock(&priv->mutex); idx = cb->args[CB_ARG_OFFSET_ID]; if (cb->args[CB_ARG_OFFSET_DUMP_START]) dump_start = cb->args[CB_ARG_OFFSET_DUMP_START]; /*MAX Message: Dump Over*/ if (idx > no_of_msgs) { if (dump_start) { kfree((void *)dump_start); cb->args[CB_ARG_OFFSET_DUMP_START] = 0; dump_start = 0; } goto dump_fail; } /*Get Dump only once per command*/ if (!idx) { err = nla_parse(tb, RPU_TM_ATTR_MAX, data, len, rpu_testmode_policy); if (err) goto dump_fail; if (!tb[RPU_TM_ATTR_CMD]) { pr_err("%s: CMD Attribute not found\n", __func__); goto dump_fail; } cmd = nla_get_u32(tb[RPU_TM_ATTR_CMD]); switch (cmd) { case RPU_TM_CMD_GRAM: if (hal_ops.get_dump_gram(&dump_start)) goto dump_fail; break; case RPU_TM_CMD_COREA: if (hal_ops.get_dump_core(&dump_start, 0)) goto dump_fail; break; case RPU_TM_CMD_COREB: if (hal_ops.get_dump_core(&dump_start, 1)) goto dump_fail; break; case RPU_TM_CMD_PERIP: if (hal_ops.get_dump_perip(&dump_start)) goto dump_fail; break; case RPU_TM_CMD_SYSBUS: if (hal_ops.get_dump_sysbus(&dump_start)) goto dump_fail; break; default: pr_err("%s: no match\n", __func__); } dump_len = hal_ops.get_dump_len(cmd); cb->args[CB_ARG_OFFSET_DUMP_START] = dump_start; cb->args[CB_ARG_OFFSET_DUMP_LEN] = dump_len; no_of_msgs = dump_len/MAX_NL_DUMP_LEN; } /*Last Message of the Dump*/ if (idx == no_of_msgs) curr_msg_len = (dump_len % MAX_NL_DUMP_LEN); curr_dump = ((char *)dump_start) + (MAX_NL_DUMP_LEN * idx); if (!curr_dump || (curr_dump < (char *)dump_start) || (curr_dump > ((char *)dump_start + dump_len))) goto dump_fail; if (curr_msg_len > skb_tailroom(skb)) goto dump_fail; err = nla_put(skb, RPU_TM_ATTR_DUMP, curr_msg_len, curr_dump); if (err) goto dump_fail; cb->args[CB_ARG_OFFSET_ID] = ++idx; mutex_unlock(&priv->mutex); return 0; dump_fail: mutex_unlock(&priv->mutex); return -ENOBUFS; } #endif /* CONFIG_NL80211_TESTMODE */ #endif /* RPU_SNAPSHOT */ struct ieee80211_ops ops = { .tx = tx, .start = start, .stop = stop, .add_interface = add_interface, .remove_interface = remove_interface, .config = config, .prepare_multicast = prepare_multicast, .configure_filter = configure_filter, .sw_scan_start = NULL, .sw_scan_complete = NULL, .get_stats = NULL, .sta_notify = NULL, .conf_tx = conf_vif_tx, .bss_info_changed = bss_info_changed, .set_tim = NULL, .set_key = set_key, .tx_last_beacon = tx_last_beacon, .ampdu_action = ampdu_action, .set_antenna = set_antenna, .remain_on_channel = remain_on_channel, .cancel_remain_on_channel = cancel_remain_on_channel, #ifdef WOWLAN #ifdef CONFIG_PM .suspend = img_suspend, .resume = img_resume, #endif /* CONFIG_PM */ #endif /* WOWLAN */ .hw_scan = scan, .cancel_hw_scan = cancel_hw_scan, .set_rekey_data = NULL, .set_rts_threshold = set_rts_threshold, .channel_switch_beacon = channel_switch_beacon, #ifdef RPU_SNAPSHOT CFG80211_TESTMODE_DUMP(rpu_testmode_dump) #endif /* RPU_SNAPSHOT */ }; void rpu_exit(void) { if (wifi->hw) { ieee80211_unregister_hw(wifi->hw); ieee80211_free_hw(wifi->hw); wifi->hw = NULL; } } RETENTION_MEM_SECTION_UNINITIALIZED unsigned char rdev_retention_mem[sizeof(struct img_priv) + (ALIGN(sizeof(struct ieee80211_local), NETDEV_ALIGN)) + sizeof(struct cfg80211_registered_device)]; int rpu_init(void) { struct ieee80211_hw *hw; int error = 0; struct img_priv *priv = NULL; int i; setup_channel_rate_capabilities(); /* Allocate new hardware device */ hw = ieee80211_alloc_hw(sizeof(struct img_priv), &ops); if (hw == NULL) { pr_err("Failed to allocate memory for ieee80211_hw\n"); error = -ENOMEM; goto out; } priv = (struct img_priv *)hw->priv; memset(priv, 0, sizeof(struct img_priv)); mutex_init(&priv->mutex); mutex_init(&priv->pkt_gen_lock); spin_lock_init(&priv->bcast_lock); spin_lock_init(&priv->roc_lock); priv->state = STOPPED; priv->active_vifs = 0; priv->txpower = DEFAULT_TX_POWER; priv->tx_antenna = DEFAULT_TX_ANT_SELECT; priv->rts_threshold = DEFAULT_RTS_THRESHOLD; strncpy(priv->name, "RPUWIFI" , 7); priv->name[7] = '\0'; if (mac_addr != NULL) { conv_str_to_byte(vif_macs[0], mac_addr, ETH_ALEN); img_ether_addr_copy(vif_macs[1], vif_macs[0]); /* Set the Locally Administered bit*/ vif_macs[1][0] |= 0x02; /* Increment the MSB by 1 (excluding 2 special bits)*/ vif_macs[1][0] += (1 << 2); } for (i = 0; i < wifi->params.num_vifs; i++) img_ether_addr_copy(priv->if_mac_addresses[i].addr, vif_macs[i]); /* Initialize HW parameters */ init_hw(hw); priv->hw = hw; priv->params = &wifi->params; priv->stats = &wifi->stats; priv->current_vif_count = 0; mutex_lock_timedout_init(&priv->reset_complete_sem, 0); mutex_lock_timedout_init(&priv->chan_prog_done_sem, 0); mutex_lock_timedout_init(&priv->scan_abort_done_sem, 0); mutex_lock_timedout_init(&priv->cancel_hw_roc_done_sem,0); /*Register hardware*/ error = ieee80211_register_hw(hw); if (!error) { wifi->hw = hw; rpu_if_init(priv, priv->name); goto out; } else { rpu_exit(); goto out; } out: return error; }