// SPDX-License-Identifier: GPL-2.0 #include #include "softmac_core.h" #include "utils.h" #include "rpu_if.h" #include "img_module_iface.h" #include "img_host_txrx_buffs.h" #include "img_host_txrx_buffs_iface.h" #include "host_rpu_data_if.h" #include "host_rpu_sys_if.h" #include "ieee80211_i.h" #include "img_mac80211_types.h" #include "img_rx_multi_pkt_proc.h" #include "img_host_rpu_umac_iface.h" #include "img_utils.h" #include "umac_debugs.h" #ifdef WOWLAN #ifdef CONFIG_PM unsigned char rx_interrupt_status; #endif #endif int rpu_prog_submit_rx_buffer(unsigned char buff_cnt, struct rx_mpdu_ctrl_info *mpdu_ctrl); #ifdef RF_TEST int img_sys_send_rftest_data(struct lmac_event_rftest *rftest_event); #endif int img_sys_send_coex_config_data(struct lmac_event_coexMgr *coex_event); void nl80211_process_twt_teardown_event(struct wiphy *wiphy,u8 flow_id,unsigned char reason); void nl80211_process_twt_sleep_event(struct wiphy *wiphy,u8 type); void ieee80211_s1g_send_twt_teardown(struct ieee80211_sub_if_data *sdata, u8 flowid); #ifdef ENABLE_OTP RETENTION_MEM_SECTION_UNINITIALIZED extern struct rpu_otp_params otp_params; #endif RETENTION_MEM_SECTION_UNINITIALIZED extern struct img_he_gi_ltf_config_params he_gi_ltf_config_params_g; int img_sys_send_stats(struct lmac_event_stats *prod_stats); RETENTION_MEM_SECTION_UNINITIALIZED struct cmd_send_recv_cnt cmd_info; extern int global_channel_width_g; int img_data_send_deinit_done(); RETENTION_MEM_SECTION_UNINITIALIZED extern struct mapped_scan_channels_info *mapped_scan_ch_gp; RETENTION_MEM_SECTION_UNINITIALIZED extern unsigned char duration_probe_cnt; RETENTION_MEM_SECTION_UNINITIALIZED extern struct rx_buf_pool_params rx_buf_pools_g[MAX_NUM_OF_RX_QUEUES]; extern unsigned char host_deinit_cmd_rcvd_flag_g; RETENTION_MEM_SECTION_UNINITIALIZED extern struct temp_vbat_config temp_vbat_config_params_g; RETENTION_MEM_SECTION_UNINITIALIZED struct rpu_if_data __rcu *rpu_if; extern unsigned char events_pending_list_len; #ifdef DEBUG_STORE_LMAC_TX_CMDS struct lmac_cmd_tx sent_tx_cmd[NUM_TX_DESCS]; #endif /* DEBUG_STORE_LMAC_TX_CMDS */ void update_mcs_packet_stat(int mcs_rate_num, int rate_flags, struct img_priv *priv) { if (rate_flags & ENABLE_11N_FORMAT) { switch (mcs_rate_num) { case 0: priv->stats->ht_tx_mcs0_packet_count++; break; case 1: priv->stats->ht_tx_mcs1_packet_count++; break; case 2: priv->stats->ht_tx_mcs2_packet_count++; break; case 3: priv->stats->ht_tx_mcs3_packet_count++; break; case 4: priv->stats->ht_tx_mcs4_packet_count++; break; case 5: priv->stats->ht_tx_mcs5_packet_count++; break; case 6: priv->stats->ht_tx_mcs6_packet_count++; break; case 7: priv->stats->ht_tx_mcs7_packet_count++; break; case 8: priv->stats->ht_tx_mcs8_packet_count++; break; case 9: priv->stats->ht_tx_mcs9_packet_count++; break; case 10: priv->stats->ht_tx_mcs10_packet_count++; break; case 11: priv->stats->ht_tx_mcs11_packet_count++; break; case 12: priv->stats->ht_tx_mcs12_packet_count++; break; case 13: priv->stats->ht_tx_mcs13_packet_count++; break; case 14: priv->stats->ht_tx_mcs14_packet_count++; break; case 15: priv->stats->ht_tx_mcs15_packet_count++; break; default: break; } #ifdef RPU_VHT_SUPPORT } else if (rate_flags & ENABLE_VHT_FORMAT) { switch (mcs_rate_num) { case 0: priv->stats->vht_tx_mcs0_packet_count++; break; case 1: priv->stats->vht_tx_mcs1_packet_count++; break; case 2: priv->stats->vht_tx_mcs2_packet_count++; break; case 3: priv->stats->vht_tx_mcs3_packet_count++; break; case 4: priv->stats->vht_tx_mcs4_packet_count++; break; case 5: priv->stats->vht_tx_mcs5_packet_count++; break; case 6: priv->stats->vht_tx_mcs6_packet_count++; break; case 7: priv->stats->vht_tx_mcs7_packet_count++; break; case 8: priv->stats->vht_tx_mcs8_packet_count++; break; case 9: priv->stats->vht_tx_mcs9_packet_count++; break; default: break; } #endif } } void umac_fill_min_supported_rate(struct lmac_cmd_tx *txcmd, unsigned int min_rate) { txcmd->num_rates = 1; txcmd->rate[0] = min_rate; txcmd->rate_retries[0] = 4; txcmd->rate_protection_type[0] = USE_PROTECTION_NONE; txcmd->rate_preamble_type[0] = DONT_USE_SHORT_PREAMBLE; txcmd->num_spatial_streams[0] = 1; txcmd->bcc_or_ldpc = 0; txcmd->stbc_enabled = 0; txcmd->rate_flags[0] = 0; } void umac_fill_rate_protection_type_bw_gi(struct sk_buff *skb, struct lmac_cmd_tx *txcmd, struct img_priv *priv, unsigned int index, unsigned char nss) { int prot_type; struct ieee80211_tx_info *c = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_rate *txrate; /* STBC Enabled/Disabled: valid if n_antennas > Nss */ if (priv->params->uccp_num_spatial_streams > nss && (c->flags & IEEE80211_TX_CTL_STBC)) txcmd->stbc_enabled = 1; txcmd->rate_retries[index] = c->control.rates[index].count; if (c->control.rates[index].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) txcmd->rate_preamble_type[index] = USE_SHORT_PREAMBLE; else txcmd->rate_preamble_type[index] = DONT_USE_SHORT_PREAMBLE; prot_type = USE_PROTECTION_NONE; if (priv->params->rate_protection_type == 1) { /* Protection*/ if (c->control.rates[index].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) prot_type = USE_PROTECTION_CTS2SELF; else if (c->control.rates[index].flags & IEEE80211_TX_RC_USE_RTS_CTS) prot_type = USE_PROTECTION_RTS; else prot_type = USE_PROTECTION_NONE; /*RTS threshold: Check for PSDU length * Need to add all HW added lenghts to skb, * sw added lengths are already part of skb->len * IV ==> Always SW * MIC for CCMP ==> HW (MMIC for TKIP ==> SW) * ICV ==> HW * FCS ==> HW */ if (ieee80211_is_data(hdr->frame_control) && !is_multicast_ether_addr(hdr->addr1) && ieee80211_has_protected(hdr->frame_control)) { if (skb->len + c->control.hw_key->icv_len + priv->rts_threshold < FCS_LEN) prot_type = USE_PROTECTION_RTS; } if (ieee80211_is_data(hdr->frame_control) && !is_multicast_ether_addr(hdr->addr1) && !ieee80211_has_protected(hdr->frame_control) && (skb->len + FCS_LEN > priv->rts_threshold)) prot_type = USE_PROTECTION_RTS; } txcmd->rate_protection_type[index] = prot_type; if (c->control.rates[index].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) txcmd->rate_flags[index] |= ENABLE_CHNL_WIDTH_40MHZ; if (c->control.rates[index].flags & IEEE80211_TX_RC_80_MHZ_WIDTH) txcmd->rate_flags[index] |= ENABLE_CHNL_WIDTH_80MHZ; if (c->control.rates[index].flags & IEEE80211_TX_RC_SHORT_GI) txcmd->rate_flags[index] |= ENABLE_SGI; #ifdef RPU_HE_SUPPORT txrate = (&c->control.rates[index]); if (txrate->flags & IEEE80211_TX_RC_HE_MCS) { if (c->control.rates[index].flags & IEEE80211_TX_RC_SHORT_GI) { txcmd->he_ltf[index] = 1; txcmd->he_gi_type[index] = HE_GI_1600NS; } else if (c->control.rates[index].flags & IEEE80211_TX_RC_SHORT_GI_2) { txcmd->he_ltf[index] = 2; txcmd->he_gi_type[index] = HE_GI_3200NS; } else { txcmd->he_ltf[index] = 1; txcmd->he_gi_type[index] = HE_GI_800NS; } } #endif /* RPU_HE_SUPPORT */ } void umac_get_rate_num_nss(struct lmac_cmd_tx *txcmd, struct ieee80211_tx_rate *txrate, unsigned int index, bool *is_mcs, unsigned char *mcs_rate_num, unsigned char *nss) { /* It is an VHT MCS rate */ if (txrate->flags & IEEE80211_TX_RC_VHT_MCS) { /*idx field is split * into a higher 4 bits (Nss), starts * with 0 and lower 4 bits (MCS number) */ *is_mcs = true; *mcs_rate_num = ieee80211_rate_get_vht_mcs(txrate); *nss = ieee80211_rate_get_vht_nss(txrate); txcmd->rate_flags[index] |= ENABLE_VHT_FORMAT; #ifdef RPU_HE_SUPPORT } else if (txrate->flags & IEEE80211_TX_RC_HE_MCS) { /* It is an HE MCS rate */ /* idx field is split * into a higher 4 bits (Nss), starts * with 0 and lower 4 bits (MCS number) */ *is_mcs = true; /* TODO get below mcs_rate and nss values from mac80211.h * from functions equivalant to ieee80211_rate_get_vht_mcs * and ieee80211_rate_get_vht_nss functions or write * our own functions for he and add them to mac80211.h */ *mcs_rate_num = txrate->idx & 0xF; *nss = (txrate->idx >> 4) + 1; txcmd->rate_flags[index] |= ENABLE_HE_SU; } else if (txrate->flags & IEEE80211_TX_RC_HE_ER_MCS) { /* It is an HE MCS rate */ /* idx field is split * into a higher 4 bits (Nss), starts * with 0 and lower 4 bits (MCS number) */ *is_mcs = true; /* TODO get below mcs_rate and nss values from mac80211.h * from functions equivalant to ieee80211_rate_get_vht_mcs * and ieee80211_rate_get_vht_nss functions or write * our own functions for he and add them to mac80211.h */ *mcs_rate_num = txrate->idx & 0xF; *nss = (txrate->idx >> 4) + 1; txcmd->rate_flags[index] |= ENABLE_HE_ER_SU; #endif /* RPU_HE_SUPPORT */ } else { if (txrate->flags & IEEE80211_TX_RC_MCS) { *is_mcs = true; *mcs_rate_num = txrate->idx; *nss = *mcs_rate_num/8 + 1; txcmd->rate_flags[index] |= ENABLE_11N_FORMAT; } } } void get_rate(struct sk_buff *skb, struct lmac_cmd_tx *txcmd, struct tx_pkt_info *pkt_info, bool retry, struct img_priv *priv) { (void)retry; struct ieee80211_rate *rate; struct ieee80211_tx_info *c = IEEE80211_SKB_CB(skb); unsigned int index, min_rate; bool is_mcs = false, is_mgd = false; struct ieee80211_tx_rate *txrate; unsigned char mcs_rate_num = 0; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; int mcs_indx; int mgd_rate; int mgd_mcast_rate; unsigned char nss = 1; bool all_rates_invalid = true; /* Normal Mode*/ rate = ieee80211_get_tx_rate(priv->hw, c); min_rate = priv->hw->wiphy->bands[c->band]->bitrates[0].hw_value; if (rate == NULL) { RPU_DEBUG_IF("%s:%d rate is null taking defaults: min: %d\n", __func__, __LINE__, c->control.rates[0].idx); umac_fill_min_supported_rate(txcmd, min_rate); return; } /* Some defaults*/ txcmd->num_rates = 0; txcmd->stbc_enabled = 0; /* BCC (or) LDPC */ if (c->flags & IEEE80211_TX_CTL_LDPC) txcmd->bcc_or_ldpc = 1; else txcmd->bcc_or_ldpc = 0; if (ieee80211_is_data(hdr->frame_control) && c->flags & IEEE80211_TX_CTL_AMPDU) { txcmd->aggregate_mpdu = AMPDU_AGGR_ENABLED; } for (index = 0; index < 4; index++) { bool skip_rate = false; txrate = (&c->control.rates[index]); txcmd->rate_flags[index] = 0; if (txrate->idx < 0) continue; txcmd->num_rates++; txcmd->num_spatial_streams[index] = 1; /* No input from production_test proc, continue and use * info from mac80211 RC */ umac_get_rate_num_nss(txcmd, txrate, index,&is_mcs, &mcs_rate_num, &nss); mcs_indx = priv->params->mgd_mode_tx_fixed_mcs_indx; mgd_rate = priv->params->mgd_mode_tx_fixed_rate; mgd_mcast_rate = priv->params->mgd_mode_mcast_fixed_data_rate; /* Rate Index: * From proc: * ** Multicast data packets * ** Unicast data packets * From RC in mac80211 * Can be MCS(HT/VHT) or Rate (11abg) */ if (ieee80211_is_data(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1) && (mgd_mcast_rate != -1)) { /* proc: Fixed MCS/Legacy rate for Multicast packets */ is_mgd = true; is_mcs = (mgd_mcast_rate & 0x80) == 0x80 ? true : false; if (!is_mcs) { if (mgd_mcast_rate == 55) mgd_mcast_rate = 11; else mgd_mcast_rate *= 2; } txcmd->rate[index] = mgd_mcast_rate; txcmd->rate_flags[index] = priv->params->mgd_mode_mcast_fixed_rate_flags; txcmd->bcc_or_ldpc = priv->params->mgd_mode_mcast_fixed_bcc_or_ldpc; if (txcmd->rate_flags[index] & ENABLE_11N_FORMAT) nss = (mgd_mcast_rate & 0x7F)/8 + 1; else nss = priv->params->mgd_mode_mcast_fixed_nss; txcmd->stbc_enabled = priv->params->mgd_mode_mcast_fixed_stbc_enabled; txcmd->rate_preamble_type[index] = priv->params->mgd_mode_mcast_fixed_preamble; if (is_mcs) update_mcs_packet_stat(mgd_mcast_rate & 0x7F, txcmd->rate_flags[index], priv); } else if (ieee80211_is_data(hdr->frame_control) && mcs_indx != -1) { /* proc: Fixed MCS for unicast */ is_mgd = true; txcmd->rate[index] = 0x80; txcmd->rate[index] |= (mcs_indx); txcmd->rate_flags[index] = priv->params->prod_mode_rate_flag; if (txcmd->rate_flags[index] & ENABLE_11N_FORMAT) nss = (mcs_indx)/8 + 1; else nss = priv->params->num_spatial_streams; txcmd->bcc_or_ldpc = priv->params->prod_mode_bcc_or_ldpc; txcmd->stbc_enabled = priv->params->prod_mode_stbc_enabled; update_mcs_packet_stat(mcs_indx, txcmd->rate_flags[index], priv); } else if (ieee80211_is_data(hdr->frame_control) && mgd_rate != -1) { /* proc: Fixed Legacy Rate for unicast */ is_mgd = true; txcmd->rate[index] = 0x80; txcmd->rate[index] = 0x00; if (mgd_rate == 55) txcmd->rate[index] |= ((mgd_rate) / 5); else txcmd->rate[index] |= ((mgd_rate * 10) / 5); txcmd->rate_flags[index] = 0; nss = 1; txcmd->bcc_or_ldpc = 0; txcmd->stbc_enabled = 0; txcmd->rate_preamble_type[index] = priv->params->prod_mode_rate_preamble_type; } else if (is_mcs) { txcmd->rate[index] = MARK_RATE_AS_MCS_INDEX; txcmd->rate[index] |= mcs_rate_num; update_mcs_packet_stat(mcs_rate_num, txcmd->rate_flags[index], priv); } else if (!is_mcs) { rate = &priv->hw->wiphy->bands[ c->band]->bitrates[ c->control.rates[index].idx]; txcmd->rate[index] = MARK_RATE_AS_RATE; txcmd->rate[index] |= rate->hw_value; nss = 1; } txcmd->num_spatial_streams[index] = nss; if (is_mgd) { if (priv->params->rate_protection_type) txcmd->rate_protection_type[index] = USE_PROTECTION_RTS; else txcmd->rate_protection_type[index] = USE_PROTECTION_NONE; txcmd->rate_retries[index] = 1; all_rates_invalid = false; break; } umac_fill_rate_protection_type_bw_gi(skb, txcmd, priv, index, nss); /*Some Sanity Checks*/ if (nss <= max(MAX_TX_STREAMS, MAX_RX_STREAMS)) /*Got at-least one valid rate*/ all_rates_invalid = false; else { if (net_ratelimit()) RPU_DEBUG_IF("RPU_WIFI:Skip Nss: %d\n", nss); skip_rate = true; } /* VHT 20MHz MCS9 is not valid*/ if (txrate->flags & IEEE80211_TX_RC_VHT_MCS && ((txcmd->rate[index] & 0x7F) == 9) && !(txcmd->rate_flags[index] & ENABLE_CHNL_WIDTH_40MHZ) && !(txcmd->rate_flags[index] & ENABLE_CHNL_WIDTH_80MHZ)) skip_rate = true; /*First Time*/ if (!index) pkt_info->max_retries = 0; pkt_info->max_retries += txcmd->rate_retries[index]; if (skip_rate) txcmd->rate_retries[index] = 0; } #ifdef RPU_HE_SUPPORT if (he_gi_ltf_config_params_g.configured){ for (index = 0; index < 4; index++) { txcmd->he_gi_type[index] = he_gi_ltf_config_params_g.he_gi_type; txcmd->he_ltf[index] = he_gi_ltf_config_params_g.he_ltf; } } #endif if (all_rates_invalid) { /*use min supported rate*/ if (net_ratelimit()) RPU_DEBUG_TX("RPU_WIFI:invalid rates\n"); umac_fill_min_supported_rate(txcmd, min_rate); } } int rpu_send_cmd(unsigned char *buf, unsigned int len, unsigned char id) { struct lst_node *cmd_buff_node; char *cmd_buff; struct rpu_if_data *p; struct img_priv *priv; struct host_rpu_msg_hdr *hal_hdr = (struct host_rpu_msg_hdr *)buf; struct lmac_msg_hdr *hdr = (struct lmac_msg_hdr *)(buf + sizeof(struct host_rpu_msg_hdr)); hdr->id = id; hal_hdr->len = len; rcu_read_lock(); p = (struct rpu_if_data *)(rcu_dereference(rpu_if)); if (!p) { pr_err("%s: Unable to retrieve rpu_if\n", __func__); WARN_ON(1); rcu_read_unlock(); return -1; } priv= p->context; cmd_buff_node = malloc(sizeof(struct lst_node) + len); if (!cmd_buff_node) { rcu_read_unlock(); WARN_ON(1); pr_err("%s:%d alloc_skb failed len %d\n", __FUNCTION__, __LINE__, len); return -ENOMEM; } cmd_buff = (char *)cmd_buff_node + sizeof(struct lst_node); cmd_buff_node->buff = cmd_buff; cmd_buff_node->size = len; memcpy(cmd_buff, buf, len); priv->stats->outstanding_cmd_cnt = cmd_info.outstanding_ctrl_req; /* Take lock to make the control commands sequential in case of SMP*/ spin_lock_bh(&cmd_info.control_path_lock); if (cmd_info.outstanding_ctrl_req < MAX_OUTSTANDING_CTRL_REQ) { RPU_DEBUG_IF("Sending the CMD, got Access\n"); hal_ops.send((void *)cmd_buff_node, NULL, 0); priv->stats->gen_cmd_send_count++; } else { RPU_DEBUG_IF("Sending the CMD, Waiting in Queue: %d\n", cmd_info.outstanding_ctrl_req); LST_add(&cmd_info.outstanding_cmd, cmd_buff_node); } /* sent but still no proc_done / unsent due to pending requests */ cmd_info.outstanding_ctrl_req++; spin_unlock_bh(&cmd_info.control_path_lock); rcu_read_unlock(); return 0; } RETENTION_MEM_SECTION_UNINITIALIZED extern unsigned int sleepEnable; RETENTION_MEM_SECTION_UNINITIALIZED extern unsigned int hw_bringup_time; RETENTION_MEM_SECTION_UNINITIALIZED extern unsigned int sw_bringup_time; RETENTION_MEM_SECTION_UNINITIALIZED extern unsigned int bcn_time_out; RETENTION_MEM_SECTION_UNINITIALIZED extern unsigned int calibSleepClk; int rpu_prog_deinit(void) { struct lmac_cmd_tx_deinit lmac_cmd_tx_deinit; memset(&lmac_cmd_tx_deinit, 0, (sizeof(struct lmac_cmd_tx_deinit))); return rpu_send_cmd((unsigned char *) &lmac_cmd_tx_deinit, sizeof(struct lmac_cmd_tx_deinit), LMAC_CMD_TX_DEINIT); } int rpu_prog_reset(unsigned int reset_type, unsigned int rpu_mode) { struct lmac_cmd_reset reset; struct img_priv *priv; struct rpu_if_data *p; unsigned int i; rcu_read_lock(); p = (struct rpu_if_data *)(rcu_dereference(rpu_if)); if (!p) { WARN_ON(1); rcu_read_unlock(); pr_err("%s:%d ERR: rpu_if not initialized\n", __FUNCTION__, __LINE__); return -1; } rcu_read_unlock(); priv= p->context; memset(&reset, 0, sizeof(struct lmac_cmd_reset)); reset.type = reset_type; if (reset_type == LMAC_ENABLE) { reset.lmac_mode = rpu_mode; reset.calib_bitmap = priv->params->phy_calib; memcpy(reset.rf_params, priv->params->rf_params, RF_PARAMS_SIZE); } reset.hw_bringup_time = hw_bringup_time; reset.sw_bringup_time = sw_bringup_time; reset.bcn_time_out = bcn_time_out; reset.calibSleepClk = calibSleepClk; reset.sleepEnable = sleepEnable; for (i = 0; i < MAX_NUM_OF_RX_QUEUES; i++ ){ memcpy(&reset.rx_skb_params[i], &rx_buf_pools_g[i], sizeof(struct rx_buf_pool_params)); } memcpy(&reset.temp_vbat_params, &temp_vbat_config_params_g, sizeof(struct temp_vbat_config)); #ifdef ENABLE_OTP memcpy(reset.prodtest_trim, otp_params.prodtest_trim, 15*4); reset.prodctrl_disable5ghz = otp_params.prodctrl_disable5ghz; #endif return rpu_send_cmd((unsigned char *) &reset, sizeof(struct lmac_cmd_reset), LMAC_CMD_RESET); } int rpu_prog_txpower(unsigned int txpower) { struct lmac_cmd_tx_pwr power; memset(&power, 0, sizeof(struct lmac_cmd_tx_pwr)); power.tx_pwr = txpower; power.if_index = 0; return rpu_send_cmd((unsigned char *) &power, sizeof(struct lmac_cmd_tx_pwr), LMAC_CMD_TX_POWER); } int rpu_prog_vif_ctrl(int index, unsigned char *mac_addr, unsigned int vif_type, unsigned int op) { struct lmac_cmd_vifctrl vif_ctrl; memset(&vif_ctrl, 0, sizeof(struct lmac_cmd_vifctrl)); vif_ctrl.mode = vif_type; memcpy(vif_ctrl.mac_addr, mac_addr, 6); vif_ctrl.if_index = index; vif_ctrl.if_ctrl = op; return rpu_send_cmd((unsigned char *) &vif_ctrl, sizeof(struct lmac_cmd_vifctrl), LMAC_CMD_VIF_CTRL); } int rpu_prog_mcast_addr_cfg(unsigned char *mcast_addr, unsigned int op) { struct lmac_cmd_mcst_addr_cfg mcast_config; memset(&mcast_config, 0, sizeof(struct lmac_cmd_mcst_addr_cfg)); mcast_config.op = op; memcpy(mcast_config.mac_addr, mcast_addr, 6); return rpu_send_cmd((unsigned char *) &mcast_config, sizeof(struct lmac_cmd_mcst_addr_cfg), LMAC_CMD_MCST_ADDR_CFG); } int rpu_prog_mcast_filter_control(unsigned int mcast_filter_enable) { struct lmac_cmd_mcst_filter_ctrl mcast_ctrl; memset(&mcast_ctrl, 0, sizeof(struct lmac_cmd_mcst_filter_ctrl)); mcast_ctrl.ctrl = mcast_filter_enable; return rpu_send_cmd((unsigned char *) &mcast_ctrl, sizeof(struct lmac_cmd_mcst_filter_ctrl), LMAC_CMD_MCST_FLTR_CTRL); } int rpu_prog_roc(unsigned int roc_ctrl, unsigned int roc_channel, unsigned int roc_duration, unsigned int roc_type) { struct lmac_cmd_roc cmd_roc; memset(&cmd_roc, 0, sizeof(struct lmac_cmd_roc)); cmd_roc.roc_ctrl = roc_ctrl; cmd_roc.roc_channel = roc_channel; cmd_roc.roc_duration = roc_duration; cmd_roc.roc_type = roc_type; return rpu_send_cmd((unsigned char *) &cmd_roc, sizeof(struct lmac_cmd_roc), LMAC_CMD_ROC_CTRL); } int rpu_prog_peer_key(int vif_index, unsigned char *vif_addr, unsigned int op, unsigned int key_id, unsigned int key_type, unsigned int cipher_type, struct umac_key *key) { (void)vif_addr; struct lmac_cmd_setkey peer_key; memset(&peer_key, 0, sizeof(struct lmac_cmd_setkey)); peer_key.if_index = vif_index; /* memcpy(peer_key.vif_addr, vif_addr, ETH_ALEN); */ peer_key.ctrl = op; peer_key.key_id = key_id; img_ether_addr_copy(peer_key.mac_addr, key->peer_mac); peer_key.key_type = key_type; peer_key.cipher_type = cipher_type; switch (cipher_type) { case CIPHER_TYPE_WEP40: memcpy(peer_key.enc_key, key->key, 5); peer_key.enc_key_len = 5; break; case CIPHER_TYPE_WEP104: memcpy(peer_key.enc_key, key->key, 13); peer_key.enc_key_len = 13; break; case CIPHER_TYPE_TKIP: case CIPHER_TYPE_CCMP: case CIPHER_TYPE_GCMP: case CIPHER_TYPE_WAPI: memcpy(peer_key.enc_key, key->key, 16); peer_key.enc_key_len = 16; if (key->tx_mic) { memcpy(peer_key.mic_key, key->tx_mic, 8); peer_key.mic_key_len += 8; } if (key->rx_mic) { memcpy(peer_key.mic_key + 8, key->rx_mic, 8); peer_key.mic_key_len += 8; } break; case CIPHER_TYPE_CCMP_256: case CIPHER_TYPE_GCMP_256: memcpy(peer_key.enc_key, key->key, 32); peer_key.enc_key_len = 32; break; } return rpu_send_cmd((unsigned char *) &peer_key, sizeof(struct lmac_cmd_setkey), LMAC_CMD_SETKEY); } int rpu_prog_if_key(int vif_index, unsigned char *vif_addr, unsigned int op, unsigned int key_id, unsigned int cipher_type, struct umac_key *key) { (void)vif_addr; struct lmac_cmd_setkey if_key; memset(&if_key, 0, sizeof(struct lmac_cmd_setkey)); if_key.if_index = vif_index; /* memcpy(if_key.vif_addr, vif_addr, 6); */ if_key.key_id = key_id; if_key.ctrl = op; if (op == KEY_CTRL_ADD) { if_key.cipher_type = cipher_type; if (cipher_type == CIPHER_TYPE_TKIP || cipher_type == CIPHER_TYPE_CCMP) { memcpy(if_key.enc_key, key->key, 16); if_key.enc_key_len = 16; if (key->tx_mic) { memcpy(if_key.mic_key, key->tx_mic, 8); if_key.mic_key_len += 8; } } else { if_key.enc_key_len = (cipher_type == CIPHER_TYPE_WEP40) ? 5 : 13; memcpy(if_key.enc_key, key->key, if_key.enc_key_len); } } if_key.key_type = KEY_TYPE_BCAST; memset(if_key.mac_addr, 0xff, 6); return rpu_send_cmd((unsigned char *) &if_key, sizeof(struct lmac_cmd_setkey), LMAC_CMD_SETKEY); } int rpu_prog_ba_session_data(unsigned int op, unsigned short tid, unsigned short *ssn, unsigned short ba_policy, unsigned char *vif_addr, unsigned char *peer_addr) { struct lmac_cmd_ht_ba ba_cmd; int index; struct img_priv *priv; struct rpu_if_data *p; struct ieee80211_vif *vif = NULL; rcu_read_lock(); p = (struct rpu_if_data *)(rcu_dereference(rpu_if)); if (!p) { WARN_ON(1); rcu_read_unlock(); pr_err("%s:%d ERR: rpu_if not initialized\n", __FUNCTION__, __LINE__); return -1; } priv= p->context; memset(&ba_cmd, 0, sizeof(struct lmac_cmd_ht_ba)); for (index = 0; index < priv->params->num_vifs; index++) { if (!(priv->active_vifs & (1 << index))) continue; vif = rcu_dereference(priv->vifs[index]); if (ether_addr_equal(vif->addr, vif_addr)) break; } if (index == priv->params->num_vifs) { RPU_DEBUG_IF("no VIF found\n"); return -1; } ba_cmd.if_index = index; ba_cmd.op = op; ba_cmd.policy = ba_policy; ba_cmd.tid = tid; ba_cmd.ssn = *ssn; img_ether_addr_copy(ba_cmd.vif_addr, vif_addr); img_ether_addr_copy(ba_cmd.peer_addr, peer_addr); rcu_read_unlock(); return rpu_send_cmd((unsigned char *) &ba_cmd, sizeof(struct lmac_cmd_ht_ba), LMAC_CMD_BA_SESSION_INFO); } int rpu_scan(int index, struct scan_req *req) { struct lmac_cmd_scan_ie *iePtr; struct lmac_cmd_scan *scan; unsigned char i; struct img_priv *priv; struct rpu_if_data *p; rcu_read_lock(); p = (struct rpu_if_data *)(rcu_dereference(rpu_if)); if (!p) { WARN_ON(1); rcu_read_unlock(); return -1; } rcu_read_unlock(); priv = p->context; scan = kmalloc(sizeof(struct lmac_cmd_scan) , GFP_KERNEL); if (scan == NULL) { RPU_DEBUG_IF("%s: Failed to allocate memory\n", __func__); return -ENOMEM; } iePtr = kmalloc(sizeof(struct lmac_cmd_scan_ie) + req->ie_len, GFP_KERNEL); if (iePtr == NULL) { RPU_DEBUG_IF("%s: Failed to allocate memory\n", __func__); kfree(scan); return -ENOMEM; } memset(scan, 0, sizeof(struct lmac_cmd_scan)); scan->if_index = index; /* We support 4 SSIDs */ scan->n_ssids = req->n_ssids; scan->n_channel = req->n_channels; scan->type = priv->params->scan_type; for (i = 0; i < scan->n_channel; i++) { scan->scanParams[i].channel = (ieee80211_frequency_to_channel(req->center_freq[i])); scan->scanParams[i].max_tx_power = req->freq_max_power[i]; /* scan->chan_max_antenna_gain[i] = * req->freq_max_antenna_gain[i]; */ /* In mac80211 the flags are u32 but for scanning we need * only first PASSIVE_SCAN flag, remaining flags may be used * in future. */ if ((!req->n_ssids) || (req->chan_flags[i] & IEEE80211_CHAN_NO_IR) || (req->chan_flags[i] & IEEE80211_CHAN_RADAR) ) { scan->scanParams[i].scan_type = PASSIVE; } else { scan->scanParams[i].scan_type = ACTIVE; } if (mapped_scan_ch_gp){ if (mapped_scan_ch_gp->scan_duration[duration_probe_cnt]){ scan->scanParams[i].scan_duration = mapped_scan_ch_gp->scan_duration[duration_probe_cnt]; }else{ if (scan->scanParams[i].scan_type == PASSIVE) scan->scanParams[i].scan_duration = PASSIVE_SCAN_DURATION; else scan->scanParams[i].scan_duration = ACTIVE_SCAN_DURATION; } if (mapped_scan_ch_gp->probe_cnt[duration_probe_cnt]) scan->scanParams[i].probe_cnt = mapped_scan_ch_gp->probe_cnt[duration_probe_cnt]; else scan->scanParams[i].probe_cnt = 2; duration_probe_cnt++; } } if (mapped_scan_ch_gp) { scan->operating_ChDuration = mapped_scan_ch_gp->oper_ch_duration; scan->scan_mode = CHANNEL_MAPPING; } scan->p2p_probe = req->p2p_probe; iePtr->extra_ies_len = req->ie_len; if (req->ie_len) memcpy(iePtr->extra_ies, req->ie, req->ie_len); if (req->n_ssids > 0) { for (i = 0; i < scan->n_ssids; i++) { scan->ssids[i].len = req->ssids[i].ssid_len; if (scan->ssids[i].len > 0) memcpy(scan->ssids[i].ssid, req->ssids[i].ssid, req->ssids[i].ssid_len); } } RPU_DEBUG_SCAN("Scan request ie len = %d n_channel = %d,", req->ie_len, scan->n_channel); RPU_DEBUG_SCAN(" n_ssids = %d, if_index = %d type = %d p2p = %d\n", scan->n_ssids, scan->if_index, scan->type, scan->p2p_probe); for (i = 0; i < scan->n_ssids; i++) { if (scan->ssids[i].len != 0) RPU_DEBUG_SCAN("SSID: %s\n", scan->ssids[i].ssid); else RPU_DEBUG_SCAN("SSID: EMPTY\n"); } RPU_DEBUG_SCAN("CHANNEL_LIST: Channel ==> Channel Flags\n"); for (i = 0; i < scan->n_channel; i++) { RPU_DEBUG_SCAN("Index %d: %d ==> %d\n", i, scan->channel_list[i], scan->chan_flags[i]); } IMG_CMD_EVENT_DBG_PARAM_INCR(umac_scan_req,1); rpu_send_cmd((unsigned char *)iePtr, sizeof(struct lmac_cmd_scan_ie) + req->ie_len, LMAC_CMD_SCAN_IE); rpu_send_cmd((unsigned char *)scan, sizeof(struct lmac_cmd_scan), LMAC_CMD_SCAN); kfree(scan); kfree(iePtr); return 0; } int rpu_scan_abort(int index) { struct lmac_cmd_scan_abort *scan_abort = NULL; scan_abort = (struct lmac_cmd_scan_abort *) kmalloc(sizeof(struct lmac_cmd_scan_abort), GFP_KERNEL); if (scan_abort == NULL) { RPU_DEBUG_IF("%s: Failed to allocate memory\n", __func__); return -ENOMEM; } memset(scan_abort, 0, sizeof(struct lmac_cmd_scan_abort)); scan_abort->if_index = index; rpu_send_cmd((unsigned char *)scan_abort, sizeof(struct lmac_cmd_scan_abort), LMAC_CMD_SCAN_ABORT); kfree(scan_abort); scan_abort = NULL; return 0; } unsigned int img_get_primary_index_info(unsigned int primary_channel, unsigned int channel_num1, unsigned int channel_bw) { unsigned int primary_ch_offset = 0; if (channel_bw == LMAC_CHAN_WIDTH_20_NOHT) { primary_ch_offset = 0; } else if (channel_bw == LMAC_CHAN_WIDTH_40) { if (primary_channel < channel_num1) primary_ch_offset = 0; else primary_ch_offset = 1; } else if (channel_bw == LMAC_CHAN_WIDTH_80) { if (primary_channel < channel_num1) { if ((channel_num1 - primary_channel) > 4) primary_ch_offset = 0; else primary_ch_offset = 1; } else { if ((primary_channel - channel_num1) > 4) primary_ch_offset = 3; else primary_ch_offset = 2; } } return primary_ch_offset; } RETENTION_MEM_SECTION_UNINITIALIZED unsigned char prog_ch_cnt; int rpu_prog_channel(unsigned int prim_ch, unsigned int center_freq1, unsigned int center_freq2, unsigned int ch_width, unsigned int freq_band) { (void)center_freq2; struct lmac_cmd_channel channel; struct rpu_if_data *p; struct img_priv *priv; int is_vht_bw80_sec_40minus; int is_vht_bw80_sec_40plus; int is_vht_bw80; int ch_no1, ch_no2; int err = 0; unsigned int cf_offset = center_freq1; if (prog_ch_cnt==0) { prog_ch_cnt++; return 0; } memset(&channel, 0, sizeof(struct lmac_cmd_channel)); rcu_read_lock(); p = (struct rpu_if_data *)(rcu_dereference(rpu_if)); if (!p) { WARN_ON(1); rcu_read_unlock(); pr_err("%s:%d ERR: rpu_if not initialized\n", __FUNCTION__, __LINE__); return -1; } priv = p->context; if (priv->params->production_test == 1) { if ((priv->params->prod_mode_chnl_bw_40_mhz == 1) && (priv->params->sec_ch_offset_40_minus == 1)) { /* NL80211_CHAN_HT40MINUS */ ch_width = 2; cf_offset -= 10; } else if (priv->params->prod_mode_chnl_bw_40_mhz == 1) { /* NL80211_CHAN_HT40PLUS */ ch_width = 2; cf_offset += 10; } } ch_no1 = ieee80211_frequency_to_channel(cf_offset); ch_no2 = 0; switch(global_channel_width_g) { case 0: case 20: break; case 40: UMAC_OUTMSG("case 40hhz\n"); ch_width = 2; /*hard coded for 40MHZ*/ ch_no1 = ieee80211_frequency_to_channel(cf_offset + 10); break; case 80: UMAC_OUTMSG("case 80hhz\n"); ch_width = 3; /*hard coded for 40MHZ*/ ch_no1 = ieee80211_frequency_to_channel(cf_offset + 30); break; default: break; } channel.phy_threshold = priv->params->phy_threshold; channel.primary_ch_number = prim_ch; channel.channel_number1 = ch_no1; channel.channel_number2 = ch_no2; switch (ch_width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: channel.channel_bw = LMAC_CHAN_WIDTH_20_NOHT; break; case NL80211_CHAN_WIDTH_40: channel.channel_bw = LMAC_CHAN_WIDTH_40; break; case NL80211_CHAN_WIDTH_80: channel.channel_bw = LMAC_CHAN_WIDTH_80; break; default: break; } channel.freq_band = freq_band; priv->cur_chan.center_freq1 = cf_offset; priv->cur_chan.center_freq2 = ch_no2; priv->cur_chan.pri_chnl_num = prim_ch; priv->cur_chan.ch_width = ch_width; priv->cur_chan.freq_band = freq_band; rcu_read_unlock(); priv->chan_prog_done = 0; channel.pri_ch_offset = img_get_primary_index_info(channel.primary_ch_number, channel.channel_number1,channel.channel_bw); if (channel.channel_bw != LMAC_CHAN_WIDTH_20_NOHT) channel.channel_bw = LMAC_CHAN_WIDTH_20_NOHT; if (channel.primary_ch_number != channel.channel_number1) channel.channel_number1 = channel.primary_ch_number; err = rpu_send_cmd((unsigned char *) &channel, sizeof(struct lmac_cmd_channel), LMAC_CMD_CHANNEL); if (err) return err; if (wait_for_channel_prog_complete(priv)) return -1; return 0; } int rpu_prog_ps_state(int index, unsigned char *vif_addr, unsigned int powersave_state) { (void)vif_addr; struct lmac_cmd_ps ps_cfg; memset(&ps_cfg, 0, sizeof(struct lmac_cmd_ps)); ps_cfg.mode = powersave_state; ps_cfg.if_index = index; IMG_CMD_EVENT_DBG_PARAM_INCR(LMAC_CMD_PS, 1); img_cmd_event_dbg_params.CURR_STATE = ps_cfg.mode; return rpu_send_cmd((unsigned char *)&ps_cfg, sizeof(struct lmac_cmd_ps), LMAC_CMD_PS); } void img_handle_ru_tx(struct img_priv *priv, struct lmac_cmd_tx *tx_cmd, struct ieee80211_hdr *mac_hdr) { __u16 fc; fc=mac_hdr->frame_control; if (priv->ru_tx == 1){ if (ieee80211_is_data_qos(fc) && !is_broadcast_ether_addr(mac_hdr->addr3)) { tx_cmd->he_tb_frame = 1; tx_cmd->aggregate_mpdu = AMPDU_AGGR_ENABLED; } } } void update_tx_stat(struct ieee80211_hdr *mac_hdr2) { if (ieee80211_is_data(mac_hdr2->frame_control)) { IMG_TX_DBG_PARAM_INCR(tx_packet_data_count, 1); } else if (ieee80211_is_mgmt(mac_hdr2->frame_control)) { IMG_TX_DBG_PARAM_INCR(tx_packet_mgmt_count, 1); switch (mac_hdr2->frame_control & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_BEACON: IMG_TX_DBG_PARAM_INCR(tx_packet_beacon_count, 1); break; case IEEE80211_STYPE_PROBE_REQ: IMG_TX_DBG_PARAM_INCR(tx_packet_probe_req_count, 1); break; case IEEE80211_STYPE_AUTH: IMG_TX_DBG_PARAM_INCR(tx_packet_auth_count, 1); break; case IEEE80211_STYPE_DEAUTH: IMG_TX_DBG_PARAM_INCR(tx_packet_deauth_count, 1); break; case IEEE80211_STYPE_DISASSOC: IMG_TX_DBG_PARAM_INCR(tx_packet_disassoc_count, 1); break; case IEEE80211_STYPE_ASSOC_REQ: case IEEE80211_STYPE_REASSOC_REQ: IMG_TX_DBG_PARAM_INCR(tx_packet_assoc_req_count, 1); break; case IEEE80211_STYPE_ACTION: IMG_TX_DBG_PARAM_INCR(tx_packet_action_count, 1); break; default: IMG_TX_DBG_PARAM_INCR(tx_packet_other_mgmt_count, 1); break; } } else { IMG_TX_DBG_PARAM_INCR(tx_packet_non_mgmt_data_count, 1); } } int program_tx_queue(struct sk_buff *skb, struct sk_buff *skb_first, struct img_priv *priv, struct lmac_cmd_tx *tx_cmd, unsigned int descriptor_id, struct tx_pkt_info *pkt_info, struct lst_node *tx_cmd_node, struct ieee80211_hdr *mac_hdr, struct umac_vif *uvif,bool retry) { DEFINE_SPIN_LOCK_IRQ(oldIPL); struct ieee80211_tx_info *tx_info; unsigned int len = 0; unsigned int ddr_addr = 0; struct nrf_wifi_tx_buff *tx_buff = img_data_get_tx_buff(skb); int tx_buff_pkt_index = 0; unsigned int pkt=0; while (pkt < MAX_TX_CMDS) { unsigned short tmp_seq_ctrl = 0; if (tx_buff == NULL) { struct pkt_info *pkt_info_t; IMG_TX_DBG_PARAM_INCR(internal_pkts, 1); /* internally generated packet */ if (img_data_skb_replace_mgmt_buff(skb)) { pkt_info_t = (struct pkt_info*)skb->pkt_info; ddr_addr = pkt_info_t->ddr_ptr; len = skb->len; } else { spin_unlock_bh(&priv->tx.lock); kfree(tx_cmd_node); rcu_read_unlock(); return -30; } } else { struct img_data_tx_buff_desc *tx_buff_desc; tx_buff_desc = (struct img_data_tx_buff_desc *)skb->rpu_ref; tx_buff_pkt_index = img_data_get_tx_buff_pkt_index(skb, descriptor_id, pkt_info->tx_buff_pkt_count, &(pkt_info->tx_buff_pkt_ref[pkt])); if (tx_buff_pkt_index < 0) { break; } pkt_info->tx_buff_pkt_count ++; ddr_addr = tx_buff->tx_buff_info[tx_buff_pkt_index].ddr_ptr; len = tx_buff->tx_buff_info[tx_buff_pkt_index].pkt_length; tmp_seq_ctrl = tx_buff_desc->tx_cmd_seq_num[pkt]; IMG_TX_DBG_PARAM_INCR(external_pkts, 1); } tx_cmd->num_frames_per_desc ++; tx_info = IEEE80211_SKB_CB(skb_first); /* Complete packet length */ tx_cmd->pkt_length[pkt] = len; tx_cmd->frame_ddr_pointer[pkt] = ddr_addr; spin_lock_irqsave(&oldIPL, 0); /* Only for Non-Qos and MGMT frames, for Qos-Data * mac80211 handles the sequence no generation */ if (!retry && tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { mac_hdr = (struct ieee80211_hdr *)img_umac_ext_ram_access_addr(ddr_addr); if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) { uvif->seq_no += 0x10; } tmp_seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); tmp_seq_ctrl |= cpu_to_le16(uvif->seq_no); memcpy_unaligned(&mac_hdr->seq_ctrl, &tmp_seq_ctrl, sizeof(unsigned short)); } memcpy(&tx_cmd->mpdu_seq_number[pkt], &tmp_seq_ctrl, sizeof(unsigned short)); update_tx_stat(mac_hdr); spin_unlock_irqrestore(&oldIPL, 0); pkt++; if (tx_buff == NULL) { break; } } return pkt; } int rpu_prog_tx(unsigned int queue, unsigned int more_frms, unsigned int descriptor_id, bool retry) { //DEFINE_SPIN_LOCK_IRQ(oldIPL); struct lmac_cmd_tx *tx_cmd; struct rpu_if_data *p; struct img_priv *priv; struct umac_vif *uvif; struct sk_buff *skb, *skb_first, *tmp; struct lst_node *tx_cmd_node; char *tx_cmd_buff; struct sk_buff_head *txq = NULL; struct ieee80211_hdr *mac_hdr, *mac_hdr2; struct ieee80211_tx_info *tx_info_first; unsigned int hdrlen, pkt = 0, extra_copy_gram = 0; int vif_index,ret=0; __u16 fc; struct tx_pkt_info *pkt_info = NULL; unsigned int cmd_tx_size; unsigned char tid; p = (struct rpu_if_data *)(rcu_dereference(rpu_if)); if (!p) { pr_err("%s:%d ERR: rpu_if not initialized\n", __FUNCTION__, __LINE__); return -1; } rcu_read_lock(); priv = p->context; spin_lock_bh(&priv->tx.lock); txq = &priv->tx.pkt_info[descriptor_id].pkt; pkt_info = &priv->tx.pkt_info[descriptor_id]; skb_first = skb_peek(txq); if (!skb_first) { spin_unlock_bh(&priv->tx.lock); rcu_read_unlock(); return -10; } cmd_tx_size = sizeof(struct lmac_cmd_tx); tx_cmd_node = kzalloc(sizeof(struct lst_node) + cmd_tx_size, GFP_KERNEL); if (!tx_cmd_node) { spin_unlock_bh(&priv->tx.lock); rcu_read_unlock(); return -ENOMEM; } tx_cmd_buff = (char *)tx_cmd_node + sizeof(struct lst_node); tx_cmd_node->buff = tx_cmd_buff; tx_cmd_node->size = cmd_tx_size; tx_cmd = (struct lmac_cmd_tx *)tx_cmd_buff; tx_info_first = IEEE80211_SKB_CB(skb_first); mac_hdr = (struct ieee80211_hdr *)skb_first->data; mac_hdr2 = mac_hdr; fc = mac_hdr->frame_control; hdrlen = ieee80211_hdrlen(fc); vif_index = vif_addr_to_index(mac_hdr->addr2, priv); /* GET The security Header Length only for data/qos-data/unicast PMF * for 11W case. */ if ((ieee80211_is_data(fc) || ieee80211_is_data_qos(fc)) && ieee80211_has_protected(fc)) { /* hw_key == NULL: Encrypted in SW (injected frames) * iv_len = 0: treat as SW encryption. */ if (tx_info_first->control.hw_key == NULL || !tx_info_first->control.hw_key->iv_len) { RPU_DEBUG_IF("%s: hw_key is %s and iv_len: 0\n", __func__, tx_info_first->control.hw_key?"valid":"NULL"); tx_cmd->encrypt = ENCRYPT_DISABLE; } else { extra_copy_gram += tx_info_first->control.hw_key->iv_len; tx_cmd->encrypt = ENCRYPT_ENABLE; } } /* For injected frames (wlantest) hw_key is not set,as PMF uses * CCMP always so hardcode this to CCMP IV LEN 8. * For Auth3: It is completely handled in SW (mac80211). */ if (ieee80211_is_unicast_robust_mgmt_frame(skb_first) && ieee80211_has_protected(fc)) { extra_copy_gram += 8; tx_cmd->encrypt = ENCRYPT_ENABLE; } else if (!ieee80211_is_unicast_robust_mgmt_frame(skb_first)) { if (ieee80211_is_mgmt(fc) && ieee80211_has_protected(fc)) { if (tx_info_first->control.hw_key != 0) hdrlen += tx_info_first->control.hw_key->iv_len; tx_cmd->encrypt = ENCRYPT_ENABLE; } } /* separate in to up to TSF and From TSF*/ if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) extra_copy_gram += 8; /* Timestamp*/ /* HAL Host-RPU HDR*/ tx_cmd->lmac_hdr.id = LMAC_CMD_TX; tx_cmd->hal_hdr.len = sizeof(struct lmac_cmd_tx); /* LMAC_CMD_TX*/ tx_cmd->if_index = vif_index; tx_cmd->queue_num = queue; tx_cmd->more_frms = more_frms; tx_cmd->descriptor_id = descriptor_id; tx_cmd->num_frames_per_desc = 0; tx_cmd->aggregate_mpdu = AMPDU_AGGR_DISABLED; /* These extra fields will be copied to GRAM along with * tx_cmd and 802.11 header */ hdrlen += extra_copy_gram; memcpy(tx_cmd->config_mac_header, skb_first->data, hdrlen); tx_cmd->config_mac_hdr_len = hdrlen; priv->tx.pkt_info[descriptor_id].vif_index = vif_index; priv->tx.pkt_info[descriptor_id].queue = queue; uvif = (struct umac_vif *) (tx_info_first->control.vif->drv_priv); /* Get the rate for first packet as all packets have same rate */ get_rate(skb_first, tx_cmd, pkt_info, retry, priv); img_handle_ru_tx(priv,tx_cmd,mac_hdr); if (ieee80211_is_data_qos(fc)) { tid = *ieee80211_get_qos_ctl(mac_hdr) & IEEE80211_QOS_CTL_TID_MASK; } else { tid = 0; } pkt_info->tx_buff_pkt_count = 0; if ((priv->tid_info[tid | TID_INITIATOR_STA].tid_state == TID_STATE_AGGR_OPERATIONAL) && ieee80211_is_data_qos(mac_hdr->frame_control)) { tx_cmd->aggregate_mpdu = AMPDU_AGGR_ENABLED; } skb_queue_walk_safe(txq, skb, tmp) { ret=program_tx_queue(skb, skb_first, priv, tx_cmd, descriptor_id, pkt_info, tx_cmd_node, mac_hdr, uvif, retry); if (ret < 0) return ret; pkt=ret; } /* SDK: Check if we can use the same txq initialized before in * the function here */ txq = &priv->tx.pkt_info[descriptor_id].pkt; spin_lock_bh(&cmd_info.control_path_lock); IMG_TX_DBG_PARAM_INCR(tx_cmds_to_lmac, 1); hal_ops.send((void *)tx_cmd_node, NULL, 0); cmd_info.tx_cmd_send_count ++; spin_unlock_bh(&cmd_info.control_path_lock); /* increment tx_cmd_send_count to keep track of number of * tx_cmd send */ if (queue != LMAC_AC_BCN) { if (pkt == 0) { printk("ERR: how can num_frames_per_desc is zero\n"); } else if (pkt == 1) { priv->stats->tx_cmd_send_count_single++; } else { priv->stats->tx_cmd_send_count_multi++; } } else { priv->stats->tx_cmd_send_count_beaconq++; } spin_unlock_bh(&priv->tx.lock); rcu_read_unlock(); return 0; } int rpu_prog_vif_short_slot(int index, unsigned char *vif_addr, unsigned int use_short_slot) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = SHORTSLOT_CHANGED; vif_cfg.use_short_slot = use_short_slot; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_vif_atim_window(int index, unsigned char *vif_addr, unsigned int atim_window) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = ATIMWINDOW_CHANGED; vif_cfg.atim_window = atim_window; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_long_retry(int index, unsigned char *vif_addr, unsigned int long_retry) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = LONGRETRY_CHANGED; vif_cfg.long_retry = long_retry; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_short_retry(int index, unsigned char *vif_addr, unsigned int short_retry) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = SHORTRETRY_CHANGED; vif_cfg.short_retry = short_retry; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_vif_basic_rates(int index, unsigned char *vif_addr, unsigned int basic_rate_set) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = BASICRATES_CHANGED; vif_cfg.basic_rate_set = basic_rate_set; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_vif_aid(int index, unsigned char *vif_addr, unsigned int aid) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = AID_CHANGED; vif_cfg.aid = aid; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_vif_op_channel(int index, unsigned char *vif_addr, unsigned char op_channel) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = OP_CHAN_CHANGED; vif_cfg.op_channel = op_channel; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_vif_conn_state(int index, unsigned char *vif_addr, unsigned int connect_state) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = CONNECT_STATE_CHANGED; vif_cfg.connect_state = connect_state; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_vif_assoc_cap(int index, unsigned char *vif_addr, unsigned int caps) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = CAPABILITY_CHANGED; vif_cfg.capability = caps; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int calculate_pd_level(int min_value, int max_value, int tx_power, unsigned char ch_bw) { int pd_level,bw_value=0; switch(ch_bw) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: bw_value = 0; break; case NL80211_CHAN_WIDTH_40: bw_value = 3; break; case NL80211_CHAN_WIDTH_80: bw_value = 6; break; case NL80211_CHAN_WIDTH_160: bw_value = 9; break; default: return 0; } pd_level = max(min_value,min(max_value,min_value +(21-tx_power)))+bw_value; return pd_level; } int calculate_non_srg_pd_level(struct ieee80211_he_obss_pd obss_pd, int tx_power, unsigned char ch_bw) { int max_val = 0,min_val = 0; int pd_level; if (!(obss_pd.sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) &&!(obss_pd.sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT)){ max_val = -62; min_val = -82; } else if (!(obss_pd.sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) && (obss_pd.sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT)){ max_val = -82+obss_pd.non_srg_max_offset; min_val = -82; } else{ max_val = -82; min_val = -82; } pd_level = calculate_pd_level(min_val,max_val,tx_power,ch_bw); return pd_level; } int calculate_srg_pd_level(struct ieee80211_he_obss_pd obss_pd, int tx_power, unsigned char ch_bw) { int max_val = 0,min_val = 0,pd_level; if (obss_pd.sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT){ max_val = -82 + obss_pd.max_offset; min_val = -82 + obss_pd.min_offset; }else{ return 0; } pd_level = calculate_pd_level(min_val,max_val,tx_power,ch_bw); return pd_level; } int rpu_prog_srg(struct umac_vif *uvif, struct ieee80211_he_obss_pd obss_pd, struct ieee80211_he_operation he_op) { struct lmac_cmd_phy_spatial_reuse_params srg_params; int tx_power; unsigned char ch_bw; if (obss_pd.enable != true) return 0; memset(&srg_params, 0, sizeof(struct lmac_cmd_phy_spatial_reuse_params)); srg_params.if_index = uvif->vif_index; if (!(obss_pd.sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED)) srg_params.nonSrgObssPdSrEnable = 1; if (!(obss_pd.sr_ctrl & IEEE80211_HE_SPR_PSR_DISALLOWED)) srg_params.srgObssPdSrEnable = 1; tx_power = uvif->priv->hw->conf.chandef.chan->max_power; ch_bw = uvif->priv->hw->conf.chandef.width; srg_params.nonSrgObssPdLevel = calculate_non_srg_pd_level(obss_pd,tx_power,ch_bw); srg_params.srgObssPdLevel = calculate_srg_pd_level(obss_pd,tx_power,ch_bw); srg_params.vhtBssColorEnable = ((he_op.he_oper_params >> 30) & 0x1); memcpy(srg_params.srgPartialIdBitMap,obss_pd.partial_bssid_bitmap,8); memcpy(srg_params.srgBssColorBitMap,obss_pd.bss_color_bitmap,8); return rpu_send_cmd((unsigned char *)&srg_params, sizeof(struct lmac_cmd_phy_spatial_reuse_params), LMAC_CMD_PHY_SPATIAL_REUSE_PARAMS); } int rpu_prog_mac_params(int index,struct ieee80211_bss_conf *bss_conf) { struct lmac_cmd_config_mac_params mac_params; if (bss_conf->he_support != 1) return 0; memset(&mac_params, 0, sizeof(struct lmac_cmd_config_mac_params)); mac_params.if_index = index; mac_params.partialAid = ((bss_conf->bssid[4]>>7 & 0x1)|(bss_conf->bssid[5]<<1)); mac_params.bssColor = ((bss_conf->he_operation.he_oper_params >> 23) & 0x3f); mac_params.staId = (bss_conf->aid & 0x7ff); mac_params.vhtBssIdPortion = ((bss_conf->bssid[4]>>7 & 0x1)|(bss_conf->bssid[5]<<1)); return rpu_send_cmd((unsigned char *)&mac_params, sizeof(struct lmac_cmd_config_mac_params), LMAC_CMD_CONFIG_MAC_PARAMS); } int rpu_prog_vif_beacon_int(int index, unsigned char *vif_addr, unsigned int bcn_int) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = BCN_INT_CHANGED; vif_cfg.beacon_interval = bcn_int; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_vif_dtim_period(int index, unsigned char *vif_addr, unsigned int dtim_period) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = DTIM_PERIOD_CHANGED; vif_cfg.dtim_period = dtim_period; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_vif_bssid(int index, unsigned char *vif_addr, unsigned char *bssid, unsigned char *mbssid) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = BSSID_CHANGED; img_ether_addr_copy(vif_cfg.bssid, bssid); if ((mbssid[0] != 0x00) && (mbssid[1] != 0x00)) { vif_cfg.mbssid_flag = 1; img_ether_addr_copy(vif_cfg.mbssid, mbssid); } img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); vif_cfg.if_index = index; return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_vif_smps(int index, unsigned char *vif_addr, unsigned char smps_mode) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = SMPS_CHANGED; vif_cfg.if_index = index; img_ether_addr_copy(vif_cfg.vif_addr, vif_addr); switch (smps_mode) { case IEEE80211_SMPS_STATIC: vif_cfg.smps_info |= SMPS_ENABLED; break; case IEEE80211_SMPS_DYNAMIC: vif_cfg.smps_info |= SMPS_ENABLED; vif_cfg.smps_info |= SMPS_MODE; break; case IEEE80211_SMPS_AUTOMATIC:/* will be one of the above*/ case IEEE80211_SMPS_OFF: break; default: WARN(1, "Invalid SMPS Mode: %d\n", smps_mode); } return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_txq_params(int index, unsigned char *addr, unsigned int queue, unsigned int aifs, unsigned int txop, unsigned int cwmin, unsigned int cwmax, unsigned int uapsd) { struct lmac_cmd_txq_params params; memset(¶ms, 0, (sizeof(struct lmac_cmd_txq_params))); params.if_index = index; img_ether_addr_copy(params.vif_addr, addr); params.queue_num = queue; params.aifsn = aifs; params.txop = txop; params.cwmin = cwmin; params.cwmax = cwmax; params.uapsd = uapsd; return rpu_send_cmd((unsigned char *) ¶ms, sizeof(struct lmac_cmd_txq_params), LMAC_CMD_TXQ_PARAMS); } int rpu_prog_rcv_bcn_mode(unsigned int bcn_rcv_mode) { struct lmac_cmd_vif_cfg vif_cfg; memset(&vif_cfg, 0, sizeof(struct lmac_cmd_vif_cfg)); vif_cfg.changed_bitmap = RCV_BCN_MODE_CHANGED; vif_cfg.bcn_mode = bcn_rcv_mode; return rpu_send_cmd((unsigned char *)&vif_cfg, sizeof(struct lmac_cmd_vif_cfg), LMAC_CMD_VIF_CFG); } int rpu_prog_stats(int op_mode) { struct lmac_cmd_get_stats get_stats; struct img_priv *priv = ((struct img_priv *)(wifi->hw->priv)); RPU_DEBUG_IF("cmd program stats\n"); memset(&get_stats, 0, sizeof(struct lmac_cmd_get_stats)); if (wifi->params.stats_type == RPU_STATS_TYPE_LMAC) { get_stats.type = STATS_TYPE_LMAC; } else if ((wifi->params.stats_type == RPU_STATS_TYPE_PHY) || (wifi->params.stats_type == RPU_STATS_TYPE_ALL)) { get_stats.type = STATS_TYPE_PHY; } return rpu_send_cmd((unsigned char *)&get_stats, sizeof(struct lmac_cmd_get_stats), LMAC_CMD_STATS); } int rpu_prog_pwr_mon(int ctrl) { struct lmac_cmd_pwr_mon pwr_mon; memset(&pwr_mon, 0, sizeof(struct lmac_cmd_pwr_mon)); pwr_mon.ctrl = ctrl; return rpu_send_cmd((unsigned char *)&pwr_mon, sizeof(struct lmac_cmd_pwr_mon), LMAC_CMD_PWR_MON); } int rpu_prog_clear_stats(void) { int ret = 0; unsigned char *clear_stats_cmd = kzalloc((sizeof(struct host_rpu_msg_hdr) + sizeof(struct lmac_msg_hdr)), GFP_KERNEL); ret = rpu_send_cmd(clear_stats_cmd, sizeof(struct host_rpu_msg_hdr) + sizeof(struct lmac_msg_hdr), LMAC_CMD_CLEAR_STATS); kfree(clear_stats_cmd); return ret; } #ifdef WOWLAN #ifdef CONFIG_PM int rpu_prog_econ_ps_state(int if_index, unsigned int ps_state) { struct lmac_cmd_ps ps_cfg; memset(&ps_cfg, 0, sizeof(struct lmac_cmd_ps)); ps_cfg.mode = ps_state; ps_cfg.if_index = if_index; return rpu_send_cmd((unsigned char *)&ps_cfg, sizeof(struct lmac_cmd_ps), LMAC_CMD_PS_ECON_CFG); } #endif /* CONFIG_PM */ #endif /* WOWLAN */ extern MAILBOX_T rxMbox; void rx_task_post(enum img_module_iface_port_id port_id, void *msg, int msg_len) { struct mbox_message *mbox_msg; mbox_msg = kzalloc(sizeof(struct mbox_message), GFP_KERNEL); mbox_msg->port_id = port_id; mbox_msg->msg = msg; mbox_msg->msg_len = msg_len; IMG_RX_DBG_PARAM_INCR(rx_mbox_post, 1); KRN_putMbox(&rxMbox, mbox_msg); } int rpu_msg_handler(char *nbuff); void umac_rx_task(void) { struct mbox_message *mbox_msg; enum img_module_iface_port_id port_id; void * softmac_rx_msg = NULL; for (;;) { mbox_msg = KRN_getMbox(&rxMbox,INFWAIT); if (mbox_msg == NULL) continue; update_jiffie(); softmac_rx_msg = mbox_msg->msg; port_id = mbox_msg->port_id; if (softmac_rx_msg != NULL) { IMG_RX_DBG_PARAM_INCR(rx_mbox_receive, 1); if (port_id == IMG_MODULE_IFACE_RX_DATA_PORT){ rpu_msg_handler(softmac_rx_msg); } else if (port_id == IMG_MODULE_IFACE_TIMER_RX_PORT){ IMG_RX_DBG_PARAM_INCR(timer_mbox_rcv, 1); timer_list_t *timer = (timer_list_t *)softmac_rx_msg; if (!timer->function) { return; } timer->timer_pending = 0; timer->function((struct timer_list *)timer->data); } else if (port_id == IMG_MODULE_IFACE_PENDING_EVENTS_TIMER_PORT){ send_rx_buffs_to_host(NULL, 0); } } free(mbox_msg); mbox_msg = NULL; } return; } void umac_update_twt_info(unsigned char twt_var,u8 flow_id,u8 trigger) { struct rpu_if_data *p; struct img_priv *priv; p = (struct rpu_if_data *)(rcu_dereference(rpu_if)); if (!p) { //pr_err("%s: Unable to retrieve rpu_if\n", __func__); WARN_ON(1); rcu_read_unlock(); return; } priv= p->context; if (twt_var == 1){ priv->hw->twt_enable = 0; priv->ru_tx = 0; priv->hw->twt_trigger_enable = 0; priv->hw->awake_window_cnt = 0; }else{ priv->hw->twt_flow_id = flow_id; priv->hw->twt_trigger_enable = trigger; priv->hw->twt_enable = 1; if (trigger == 1) priv->ru_tx = 1; } } void umac_send_twt_del_info(void) { struct lmac_cmd_config_twt twt_del; memset(&twt_del, 0, (sizeof(struct lmac_cmd_config_twt))); twt_del.config_ctrl = 1; umac_update_twt_info(twt_del.config_ctrl,0,0); rpu_send_cmd((unsigned char *)&twt_del, sizeof(struct lmac_cmd_config_twt), LMAC_CMD_CONFIG_TWT); } 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) { struct lmac_cmd_config_twt twt_info; memset(&twt_info, 0, (sizeof(struct lmac_cmd_config_twt))); twt_info.config_ctrl = 0; twt_info.negotiation_type = neg_type; twt_info.flow_type = flow_type; twt_info.flow_id = flow_id; twt_info.trigger = trigger_enable; twt_info.twt_target_wake_interval_exponent = wkup_exponent; twt_info.twt_target_wake_interval_mantissa = mantissa; twt_info.target_wake_time = twt; twt_info.nominal_min_twt_wake_duration = min_twt_dur; umac_update_twt_info(twt_info.config_ctrl,flow_id,twt_info.trigger); rpu_send_cmd((unsigned char *)&twt_info, sizeof(struct lmac_cmd_config_twt), LMAC_CMD_CONFIG_TWT); } int umac_send_sleep_status(int status) { struct lmac_cmd_sleep_status *sleep_status; if (status == SLEEP_REQ_FAIL) { IMG_SLEEP_DBG_PARAM_INCR(sleep_req_fail, 1); } sleep_status = kmalloc(sizeof(struct lmac_cmd_sleep_status), GFP_KERNEL); if (sleep_status == NULL) { RPU_DEBUG_IF("%s: Failed to allocate memory\n", __func__); return -ENOMEM; } memset(sleep_status, 0, sizeof(struct lmac_cmd_sleep_status)); sleep_status->status = status; rpu_send_cmd((unsigned char *)sleep_status, sizeof(struct lmac_cmd_sleep_status), LMAC_CMD_SLEEP_STATUS); free(sleep_status); return 0; } int check_umac_activity(void); int umac_softmac_check_sleep_activity() { IMG_SLEEP_DBG_PARAM_INCR(sleep_req, 1); if (cmd_info.outstanding_ctrl_req != 0) { IMG_SLEEP_DBG_PARAM_INCR(outstanding_cmds, 1); return 1; } if (IMG_TX_DBG_PARAM_VAL(tx_cmds_currently_in_use)) { IMG_SLEEP_DBG_PARAM_INCR(tx_cmds_currently_in_use, 1); return 1; } if (check_umac_activity()) { return 1; } return 0; } void process_tx_block_twt(struct img_priv *priv, struct lmac_twt_sleep_event *sleep_event) { struct ieee80211_sub_if_data *sdata; int i; if ((priv->hw->awake_window_cnt > sleep_dbg.max_twt_awake_cnt) && (priv->hw->twt_trigger_enable == 1)){ for (i=0;ivifs[i]->addr,sleep_event->vif_addr,ETH_ALEN) == 0){ sdata =vif_to_sdata(priv->vifs[i]); priv->ru_tx = 0; priv->hw->twt_enable = 0; priv->hw->twt_trigger_enable = 0; /* twt del info to lmac */ umac_send_twt_del_info(); /* twt teardown pkt to lmac */ ieee80211_s1g_send_twt_teardown(sdata,priv->hw->twt_flow_id); /* twt teardown event to host */ nl80211_process_twt_teardown_event(priv->hw->wiphy, priv->hw->twt_flow_id, TRIGGER_NOT_RECEIVED); break; } } }else { if ((priv->trig_recv_cnt > 0) && (priv->hw->twt_trigger_enable == 1)) priv->hw->awake_window_cnt = 0; /* twt block event to host */ nl80211_process_twt_sleep_event(priv->hw->wiphy,sleep_event->type); priv->block_tx_done = 1; } } int img_sys_send_pwr_mon_data(struct lmac_event_pwr_mon_data *pwr_mon_data); int handle_lmacevent_in_patch(unsigned int event, char *event_buff, struct lmac_msg_hdr *hdr, struct img_priv *priv) { return 0; } int rpu_msg_handler(char *event_buff) { unsigned int event; struct rpu_if_data *p; char *pending_cmd; struct img_priv *priv; struct lmac_msg_hdr *hdr = (struct lmac_msg_hdr *)(event_buff + sizeof(struct host_rpu_msg_hdr)); unsigned char *buff = (unsigned char *)event_buff; event = hdr->id & 0xffff; rcu_read_lock(); p = (struct rpu_if_data *)(rcu_dereference(rpu_if)); if (!p) { WARN_ON(1); // kfree(event_buff); rcu_read_unlock(); return 0; } priv = (struct img_priv *)p->context; if (handle_lmacevent_in_patch(event, event_buff, hdr, priv)) return 0; /* RPU_DEBUG_IF("%s-RPUIF: event %d received\n", p->name, event); */ switch(event) { case LMAC_EVENT_SLEEP_REQ: { int ret; ret = umac_softmac_check_sleep_activity(); if (ret) { umac_send_sleep_status(SLEEP_REQ_FAIL); } else { IMG_SLEEP_DBG_PARAM_INCR(sleep_req_succ, 1); umac_send_sleep_status(SLEEP_REQ_SUCCESS); cmd_info.outstanding_ctrl_req--; } } break; case LMAC_EVENT_RESET_COMPLETE: { struct lmac_event_reset_complete *r = (struct lmac_event_reset_complete *)buff; if (host_deinit_cmd_rcvd_flag_g == 1){ host_deinit_cmd_rcvd_flag_g = 0; img_data_send_deinit_done(); } rpu_reset_complete(r->version, p->context); spin_lock_bh(&cmd_info.control_path_lock); if (cmd_info.outstanding_ctrl_req == 0) { pr_err("%s-RPUIF: Unexpected: " "Spurious reset_complete" " received." " Ignoring and continuing.\n", p->name); } else { cmd_info.outstanding_ctrl_req--; RPU_DEBUG_IF("After DEC: outstanding cmd: %d\n", cmd_info.outstanding_ctrl_req); pending_cmd = LST_removeHead(&cmd_info.outstanding_cmd); if (pending_cmd != NULL) { RPU_DEBUG_IF("Send 1 outstanding cmd\n"); hal_ops.send((void *)pending_cmd, NULL, 0); priv->stats->gen_cmd_send_count++; } } spin_unlock_bh(&cmd_info.control_path_lock); } break; case LMAC_EVENT_SCAN_ABORT_COMPLETE : priv->scan_abort_done = 1; break; #ifdef WOWLAN #ifdef CONFIG_PM case LMAC_EVENT_PS_ECON_CFG_DONE : { struct lmac_event_ps_econ_cfg_complete *econ_cfg_complete_data = (struct lmac_event_ps_econ_cfg_complete *)buff; priv->econ_ps_cfg_stats.completed = 1; priv->econ_ps_cfg_stats.result = econ_cfg_complete_data->status; rx_interrupt_status = 0; } break; case LMAC_EVENT_PS_ECON_WAKE : { struct lmac_event_ps_econ_wake *econ_wake_data = (struct lmac_event_ps_econ_wake *)buff; priv->econ_ps_cfg_stats.wake_trig = econ_wake_data->trigger; } break; #endif /* CONFIG_PM */ #endif /* WOWLAN */ case LMAC_EVENT_SCAN_COMPLETE : rpu_scan_complete(p->context, (struct lmac_event_scanres *) buff, NULL, 0); break; case LMAC_EVENT_RX : if (events_pending_list_len > 10) { struct lmac_event_rx *rx_event = (struct lmac_event_rx *)buff; rpu_prog_submit_rx_buffer(rx_event->rx_pkt_cnt, rx_event->mpdu_ctrl); } else { rpu_rx_frame(buff, p->context); rcu_read_unlock(); return 0; } break; case LMAC_EVENT_TX_DONE : { /* Increment tx_done_recv_count to keep track of number * of tx_done received do not count tx dones from host. */ priv->stats->tx_done_recv_count++; rpu_tx_complete((void *)buff, p->context); } cmd_info.tx_done_recv_count++; rcu_read_unlock(); return 0; case LMAC_EVENT_DISCONNECTED : { struct lmac_event_disconnect *dis = (struct lmac_event_disconnect *)buff; struct img_priv *priv = (struct img_priv *)p->context; struct ieee80211_vif *vif = NULL; int i = 0; if (dis->reason_code == REASON_NW_LOST) { for (i = 0; i < MAX_VIFS; i++) { if (!(priv->active_vifs & (1 << i))) continue; vif = rcu_dereference(priv->vifs[i]); if (ether_addr_equal(vif->addr, dis->mac_addr)) { ieee80211_connection_loss(vif); break; } } } } break; case LMAC_EVENT_STATS : { struct lmac_event_stats *prod_stats = (struct lmac_event_stats *) buff; if (prod_stats->type == STATS_TYPE_PHY) { rpu_store_stats(prod_stats); if (wifi->params.stats_type == RPU_STATS_TYPE_ALL) { struct lmac_cmd_get_stats get_stats; memset(&get_stats, 0, sizeof(struct lmac_cmd_get_stats)); get_stats.type = STATS_TYPE_LMAC; rpu_send_cmd((unsigned char *)&get_stats, sizeof(struct lmac_cmd_get_stats), LMAC_CMD_STATS); break; } } else if (prod_stats->type == STATS_TYPE_LMAC) { RPU_DEBUG_IF("LMAC STATS Updating while sending to host\n"); } else if (prod_stats->type == STATS_TYPE_MAX) { RPU_DEBUG_IF("MAX stats needs to be updated\n"); } img_sys_send_stats(prod_stats); } break; case LMAC_EVENT_PWR_MON : { struct lmac_event_pwr_mon_data *pwr_mon_data = (struct lmac_event_pwr_mon_data *) buff; static unsigned int dbg_pwr_mon_event=0; dbg_pwr_mon_event++; LOG_string("UMAC : LMAC_EVENT_PWR_MON"); img_sys_send_pwr_mon_data(pwr_mon_data); } break; case LMAC_EVENT_NOA: rpu_noa_event(FROM_EVENT_NOA, (void *)buff, p->context, NULL); break; case LMAC_EVENT_COMMAND_PROC_DONE: { /*struct lmac_event_command_complete *cmd = * (struct lmac_event_command_complete*)buff; */ RPU_DEBUG_IF("Received PROC_DONE\n"); spin_lock_bh(&cmd_info.control_path_lock); if (cmd_info.outstanding_ctrl_req == 0) { if (event == LMAC_EVENT_COMMAND_PROC_DONE) pr_err("%s-RPUIF: Unexpected: proc_done received. Ignoring and continuing\n", p->name); } else { if (event == LMAC_EVENT_COMMAND_PROC_DONE) cmd_info.outstanding_ctrl_req--; RPU_DEBUG_IF("After DEC: outstanding cmd: %d\n", cmd_info.outstanding_ctrl_req); pending_cmd = LST_removeHead(&cmd_info.outstanding_cmd); if (pending_cmd != NULL) { RPU_DEBUG_IF("Send 1 outstanding cmd\n"); hal_ops.send((void *)pending_cmd, NULL, 0); priv->stats->gen_cmd_send_count++; } } spin_unlock_bh(&cmd_info.control_path_lock); } break; case LMAC_EVENT_CH_PROG_DONE: rpu_ch_prog_complete(event, (struct lmac_event_ch_prog_complete *)buff, p->context); break; case LMAC_EVENT_ROC_STATUS: { struct lmac_event_roc_status *roc_status = (void *)buff; struct delayed_work *work = NULL; RPU_DEBUG_ROC("%s:%d ROC status is %d\n", __func__, __LINE__, roc_status->roc_status); switch (roc_status->roc_status) { case LMAC_ROC_STAT_STARTED: if (priv->roc_params.roc_in_progress == 0) { priv->roc_params.roc_in_progress = 1; ieee80211_ready_on_channel(priv->hw); RPU_DEBUG_ROC("%s-RPUIF: ROC READY..\n", priv->name); } break; case LMAC_ROC_STAT_DONE: case LMAC_ROC_STAT_STOPPED: if (priv->roc_params.roc_in_progress == 1) { work = &priv->roc_complete_work; ieee80211_queue_delayed_work(priv->hw, work, 0); } break; } } break; case LMAC_EVENT_COEX_MANAGER: { struct lmac_event_coexMgr *coex_event = (struct lmac_event_coexMgr *) buff; img_sys_send_coex_config_data(coex_event); } break; case LMAC_EVENT_TWT_BLOCK: { struct lmac_twt_sleep_event *sleep_event = (struct lmac_twt_sleep_event *)buff; int ret; unsigned int value; if ((sleep_event->type == TWT_BLOCK_TX)) { if (priv->block_tx_done == 0) { process_tx_block_twt(priv,sleep_event); } }else if (sleep_event->type == TWT_UNBLOCK_TX){ priv->block_tx_done = 0; if (priv->hw->twt_trigger_enable == 1){ priv->trig_recv_cnt = 0; priv->hw->awake_window_cnt++; } /* twt unblock event to host */ nl80211_process_twt_sleep_event(priv->hw->wiphy,sleep_event->type); } } break; case LMAC_EVENT_FW_ERROR: pr_err("%s: FW is in Error State, please reload.\n", __func__); break; default: pr_warn("%s: Unknown event received %d\n", __func__, event); } free(event_buff); rcu_read_unlock(); return 0; } int rpu_if_init(void *context, const char *name) { struct rpu_if_data *p; RPU_DEBUG_IF("%s-RPUIF: rpu_if init called\n", name); p = kzalloc(sizeof(struct rpu_if_data), 0xFFFF); if (!p) { pr_err("%s:%d kzalloc failed length %lu\n", __FUNCTION__, __LINE__, sizeof(struct rpu_if_data)); return -ENOMEM; } p->name = (char *)name; p->context = context; rcu_assign_pointer(rpu_if, p); LST_init(&cmd_info.outstanding_cmd); spin_lock_bh_init(&cmd_info.control_path_lock); cmd_info.outstanding_ctrl_req = 0; return 0; } void rpu_if_deinit(void) { struct rpu_if_data *p; RPU_DEBUG_IF("%s-RPUIF: Deinit called\n", rpu_if->name); p = rcu_dereference(rpu_if); rcu_assign_pointer(rpu_if, NULL); synchronize_rcu(); kfree(p); } void rpu_if_free_outstnding(void) { /* First free the outstanding commands, we are not sending * anymore commands to the FW except RESET. */ while (!LST_empty(&cmd_info.outstanding_cmd)) { kfree(LST_removeHead(&cmd_info.outstanding_cmd)); } cmd_info.outstanding_ctrl_req = 0; } int rpu_prog_submit_rx_buffer(unsigned char buff_cnt, struct rx_mpdu_ctrl_info *mpdu_ctrl) { struct lmacCmdSubmitRxBuffer sub_rx_buff; unsigned char i=0; unsigned int len=0; IMG_RX_DBG_PARAM_INCR(umac_consumed_pkts, buff_cnt); IMG_RX_DBG_PARAM_DECR(current_refill_gap, buff_cnt); if (buff_cnt == 0) return 0; memset(&sub_rx_buff, 0, sizeof(struct lmacCmdSubmitRxBuffer)); sub_rx_buff.rxBufCnt = buff_cnt; for (i=0; i