/** **************************************************************************************** * * @file rwnx_msg_rx.c * * @brief RX function definitions * * Copyright (C) RivieraWaves 2012-2021 * **************************************************************************************** */ #include "rwnx_defs.h" #include "rwnx_prof.h" #include "rwnx_tx.h" #ifdef CONFIG_RWNX_BFMER #include "rwnx_bfmer.h" #endif //(CONFIG_RWNX_BFMER) #ifdef CONFIG_RWNX_FULLMAC #include "rwnx_debugfs.h" #include "rwnx_msg_tx.h" #include "rwnx_tdls.h" #endif /* CONFIG_RWNX_FULLMAC */ #include "rwnx_events.h" #include "rwnx_compat.h" #include "aicwf_txrxif.h" #include "rwnx_msg_rx.h" static int rwnx_freq_to_idx(struct rwnx_hw *rwnx_hw, int freq) { struct ieee80211_supported_band *sband = NULL; int band, ch, idx = 0; for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { #ifdef CONFIG_RWNX_FULLMAC sband = rwnx_hw->wiphy->bands[band]; #endif /* CONFIG_RWNX_FULLMAC */ if (!sband) { continue; } for (ch = 0; ch < sband->n_channels; ch++, idx++) { if (sband->channels[ch].center_freq == freq) { goto exit; } } } BUG_ON(1); exit: // Channel has been found, return the index return idx; } /*************************************************************************** * Messages from MM task **************************************************************************/ static inline int rwnx_rx_chan_pre_switch_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct rwnx_vif *rwnx_vif; int chan_idx = ((struct mm_channel_pre_switch_ind *)msg->param)->chan_index; REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT); #ifdef CONFIG_RWNX_FULLMAC list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx) { rwnx_txq_vif_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); } } #endif /* CONFIG_RWNX_FULLMAC */ REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT); return 0; } static inline int rwnx_rx_chan_switch_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct rwnx_vif *rwnx_vif; int chan_idx = ((struct mm_channel_switch_ind *)msg->param)->chan_index; bool roc = ((struct mm_channel_switch_ind *)msg->param)->roc; bool roc_tdls = ((struct mm_channel_switch_ind *)msg->param)->roc_tdls; REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT); #ifdef CONFIG_RWNX_FULLMAC if (roc_tdls) { u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index; list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { if (rwnx_vif->vif_index == vif_index) { rwnx_vif->roc_tdls = true; rwnx_txq_tdls_sta_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); } } } else if (!roc) { list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx) { rwnx_txq_vif_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); } } } else { /* Retrieve the allocated RoC element */ struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem; /* Get VIF on which RoC has been started */ //rwnx_vif = netdev_priv(roc_elem->wdev->netdev); /* For debug purpose (use ftrace kernel option) */ //trace_switch_roc(rwnx_vif->vif_index); if(roc_elem) { /* If mgmt_roc is true, remain on channel has been started by ourself */ if (!roc_elem->mgmt_roc) { /* Inform the host that we have switch on the indicated off-channel */ cfg80211_ready_on_channel(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt), roc_elem->chan, roc_elem->duration, GFP_ATOMIC); } /* Keep in mind that we have switched on the channel */ roc_elem->on_chan = true; } else { printk("roc_elem == null\n"); } // Enable traffic on OFF channel queue rwnx_txq_offchan_start(rwnx_hw); } tasklet_schedule(&rwnx_hw->task); rwnx_hw->cur_chanctx = chan_idx; rwnx_radar_detection_enable_on_cur_channel(rwnx_hw); #endif /* CONFIG_RWNX_FULLMAC */ REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT); return 0; } static inline int rwnx_rx_tdls_chan_switch_cfm(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { return 0; } static inline int rwnx_rx_tdls_chan_switch_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { #ifdef CONFIG_RWNX_FULLMAC // Enable traffic on OFF channel queue rwnx_txq_offchan_start(rwnx_hw); return 0; #endif } static inline int rwnx_rx_tdls_chan_switch_base_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct rwnx_vif *rwnx_vif; u8 vif_index = ((struct tdls_chan_switch_base_ind *)msg->param)->vif_index; RWNX_DBG(RWNX_FN_ENTRY_STR); #ifdef CONFIG_RWNX_FULLMAC list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { if (rwnx_vif->vif_index == vif_index) { rwnx_vif->roc_tdls = false; rwnx_txq_tdls_sta_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); } } return 0; #endif } static inline int rwnx_rx_tdls_peer_ps_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct rwnx_vif *rwnx_vif; u8 vif_index = ((struct tdls_peer_ps_ind *)msg->param)->vif_index; bool ps_on = ((struct tdls_peer_ps_ind *)msg->param)->ps_on; #ifdef CONFIG_RWNX_FULLMAC list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { if (rwnx_vif->vif_index == vif_index) { rwnx_vif->sta.tdls_sta->tdls.ps_on = ps_on; // Update PS status for the TDLS station rwnx_ps_bh_enable(rwnx_hw, rwnx_vif->sta.tdls_sta, ps_on); } } return 0; #endif } static inline int rwnx_rx_remain_on_channel_exp_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { #ifdef CONFIG_RWNX_FULLMAC /* Retrieve the allocated RoC element */ struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem; /* Get VIF on which RoC has been started */ struct rwnx_vif *rwnx_vif; RWNX_DBG(RWNX_FN_ENTRY_STR); if (!roc_elem) return 0; rwnx_vif = container_of(roc_elem->wdev, struct rwnx_vif, wdev); /* For debug purpose (use ftrace kernel option) */ #ifdef CREATE_TRACE_POINTS trace_roc_exp(rwnx_vif->vif_index); #endif /* If mgmt_roc is true, remain on channel has been started by ourself */ /* If RoC has been cancelled before we switched on channel, do not call cfg80211 */ if (!roc_elem->mgmt_roc && roc_elem->on_chan) { /* Inform the host that off-channel period has expired */ cfg80211_remain_on_channel_expired(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt), roc_elem->chan, GFP_ATOMIC); } /* De-init offchannel TX queue */ rwnx_txq_offchan_deinit(rwnx_vif); /* Increase the cookie counter cannot be zero */ rwnx_hw->roc_cookie_cnt++; if (rwnx_hw->roc_cookie_cnt == 0) { rwnx_hw->roc_cookie_cnt = 1; } /* Free the allocated RoC element */ kfree(roc_elem); rwnx_hw->roc_elem = NULL; #endif /* CONFIG_RWNX_FULLMAC */ return 0; } static inline int rwnx_rx_p2p_vif_ps_change_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { int vif_idx = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->vif_index; int ps_state = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->ps_state; struct rwnx_vif *vif_entry; RWNX_DBG(RWNX_FN_ENTRY_STR); #ifdef CONFIG_RWNX_FULLMAC vif_entry = rwnx_hw->vif_table[vif_idx]; if (vif_entry) { goto found_vif; } #endif /* CONFIG_RWNX_FULLMAC */ goto exit; found_vif: #ifdef CONFIG_RWNX_FULLMAC if (ps_state == MM_PS_MODE_OFF) { // Start TX queues for provided VIF rwnx_txq_vif_start(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw); } else { // Stop TX queues for provided VIF rwnx_txq_vif_stop(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw); } #endif /* CONFIG_RWNX_FULLMAC */ exit: return 0; } static inline int rwnx_rx_channel_survey_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mm_channel_survey_ind *ind = (struct mm_channel_survey_ind *)msg->param; // Get the channel index int idx = rwnx_freq_to_idx(rwnx_hw, ind->freq); // Get the survey struct rwnx_survey_info *rwnx_survey; if (idx > ARRAY_SIZE(rwnx_hw->survey)) return 0; rwnx_survey = &rwnx_hw->survey[idx]; // Store the received parameters rwnx_survey->chan_time_ms = ind->chan_time_ms; rwnx_survey->chan_time_busy_ms = ind->chan_time_busy_ms; rwnx_survey->noise_dbm = ind->noise_dbm; rwnx_survey->filled = (SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY); if (ind->noise_dbm != 0) { rwnx_survey->filled |= SURVEY_INFO_NOISE_DBM; } return 0; } static inline int rwnx_rx_p2p_noa_upd_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { return 0; } static inline int rwnx_rx_rssi_status_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mm_rssi_status_ind *ind = (struct mm_rssi_status_ind *)msg->param; int vif_idx = ind->vif_index; bool rssi_status = ind->rssi_status; struct rwnx_vif *vif_entry; RWNX_DBG(RWNX_FN_ENTRY_STR); #ifdef CONFIG_RWNX_FULLMAC vif_entry = rwnx_hw->vif_table[vif_idx]; if (vif_entry) { cfg80211_cqm_rssi_notify(vif_entry->ndev, rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, ind->rssi, GFP_ATOMIC); } #endif /* CONFIG_RWNX_FULLMAC */ return 0; } static inline int rwnx_rx_pktloss_notify_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { #ifdef CONFIG_RWNX_FULLMAC struct mm_pktloss_ind *ind = (struct mm_pktloss_ind *)msg->param; struct rwnx_vif *vif_entry; int vif_idx = ind->vif_index; RWNX_DBG(RWNX_FN_ENTRY_STR); vif_entry = rwnx_hw->vif_table[vif_idx]; if (vif_entry) { cfg80211_cqm_pktloss_notify(vif_entry->ndev, (const u8 *)ind->mac_addr.array, ind->num_packets, GFP_ATOMIC); } #endif /* CONFIG_RWNX_FULLMAC */ return 0; } static inline int rwnx_apm_staloss_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mm_apm_staloss_ind *ind = (struct mm_apm_staloss_ind *)msg->param; RWNX_DBG(RWNX_FN_ENTRY_STR); memcpy(rwnx_hw->sta_mac_addr, ind->mac_addr, 6); rwnx_hw->apm_vif_idx = ind->vif_idx; queue_work(rwnx_hw->apmStaloss_wq, &rwnx_hw->apmStalossWork); return 0; } static inline int rwnx_rx_csa_counter_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mm_csa_counter_ind *ind = (struct mm_csa_counter_ind *)msg->param; struct rwnx_vif *vif; bool found = false; RWNX_DBG(RWNX_FN_ENTRY_STR); // Look for VIF entry list_for_each_entry(vif, &rwnx_hw->vifs, list) { if (vif->vif_index == ind->vif_index) { found = true; break; } } if (found) { #ifdef CONFIG_RWNX_FULLMAC if (vif->ap.csa) vif->ap.csa->count = ind->csa_count; else netdev_err(vif->ndev, "CSA counter update but no active CSA"); #endif } return 0; } #ifdef CONFIG_RWNX_FULLMAC static inline int rwnx_rx_csa_finish_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mm_csa_finish_ind *ind = (struct mm_csa_finish_ind *)msg->param; struct rwnx_vif *vif; bool found = false; RWNX_DBG(RWNX_FN_ENTRY_STR); // Look for VIF entry list_for_each_entry(vif, &rwnx_hw->vifs, list) { if (vif->vif_index == ind->vif_index) { found = true; break; } } if (found) { if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP || RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO) { if (vif->ap.csa) { vif->ap.csa->status = ind->status; vif->ap.csa->ch_idx = ind->chan_idx; schedule_work(&vif->ap.csa->work); } else netdev_err(vif->ndev, "CSA finish indication but no active CSA"); } else { if (ind->status == 0) { rwnx_chanctx_unlink(vif); rwnx_chanctx_link(vif, ind->chan_idx, NULL); if (rwnx_hw->cur_chanctx == ind->chan_idx) { rwnx_radar_detection_enable_on_cur_channel(rwnx_hw); rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); } else rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); } } } return 0; } static inline int rwnx_rx_csa_traffic_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mm_csa_traffic_ind *ind = (struct mm_csa_traffic_ind *)msg->param; struct rwnx_vif *vif; bool found = false; RWNX_DBG(RWNX_FN_ENTRY_STR); // Look for VIF entry list_for_each_entry(vif, &rwnx_hw->vifs, list) { if (vif->vif_index == ind->vif_index) { found = true; break; } } if (found) { if (ind->enable) rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CSA, rwnx_hw); else rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CSA, rwnx_hw); } return 0; } static inline int rwnx_rx_ps_change_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mm_ps_change_ind *ind = (struct mm_ps_change_ind *)msg->param; struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx]; if (ind->sta_idx >= (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) { wiphy_err(rwnx_hw->wiphy, "Invalid sta index reported by fw %d\n", ind->sta_idx); return 1; } netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev, "Sta %d, change PS mode to %s", sta->sta_idx, ind->ps_state ? "ON" : "OFF"); if (sta->valid) { rwnx_ps_bh_enable(rwnx_hw, sta, ind->ps_state); } else if (rwnx_hw->adding_sta) { sta->ps.active = ind->ps_state ? true : false; } else { if (rwnx_hw->vif_table[sta->vif_idx] && rwnx_hw->vif_table[sta->vif_idx]->ndev) netdev_err(rwnx_hw->vif_table[sta->vif_idx]->ndev, "Ignore PS mode change on invalid sta\n"); } return 0; } static inline int rwnx_rx_traffic_req_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mm_traffic_req_ind *ind = (struct mm_traffic_req_ind *)msg->param; struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx]; RWNX_DBG(RWNX_FN_ENTRY_STR); netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev, "Sta %d, asked for %d pkt", sta->sta_idx, ind->pkt_cnt); rwnx_ps_bh_traffic_req(rwnx_hw, sta, ind->pkt_cnt, ind->uapsd ? UAPSD_ID : LEGACY_PS_ID); return 0; } #endif /* CONFIG_RWNX_FULLMAC */ /*************************************************************************** * Messages from SCAN task **************************************************************************/ #if 0 static inline int rwnx_rx_scan_done_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) struct cfg80211_scan_info info = { .aborted = false, }; #endif RWNX_DBG(RWNX_FN_ENTRY_STR); rwnx_ipc_elem_var_deallocs(rwnx_hw, &rwnx_hw->scan_ie); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) ieee80211_scan_completed(rwnx_hw->hw, &info); #else ieee80211_scan_completed(rwnx_hw->hw, false); #endif return 0; } #endif /*************************************************************************** * Messages from SCANU task **************************************************************************/ #ifdef CONFIG_RWNX_FULLMAC extern uint8_t scanning; static inline int rwnx_rx_scanu_start_cfm(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { RWNX_DBG(RWNX_FN_ENTRY_STR); if (rwnx_hw->scan_request #ifdef CONFIG_SCHED_SCAN && !rwnx_hw->is_sched_scan #endif//CONFIG_SCHED_SCAN ) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) struct cfg80211_scan_info info = { .aborted = false, }; cfg80211_scan_done(rwnx_hw->scan_request, &info); #else cfg80211_scan_done(rwnx_hw->scan_request, false); #endif } #ifdef CONFIG_SCHED_SCAN if(rwnx_hw->is_sched_scan){ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) AICWFDBG(LOGINFO, "%s cfg80211_sched_scan_results \r\n", __func__); cfg80211_sched_scan_results(rwnx_hw->scan_request->wiphy, rwnx_hw->sched_scan_req->reqid); #else cfg80211_sched_scan_results(rwnx_hw->sched_scan_req->wiphy); #endif kfree(rwnx_hw->scan_request); rwnx_hw->is_sched_scan = false; } #endif//CONFIG_SCHED_SCAN rwnx_hw->scan_request = NULL; scanning = 0; return 0; } static inline int rwnx_rx_scanu_result_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct cfg80211_bss *bss = NULL; struct ieee80211_channel *chan; struct scanu_result_ind *ind = (struct scanu_result_ind *)msg->param; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)ind->payload; u64 tsf; u8 *ie; size_t ielen; u16 capability, beacon_interval; u16 len = ind->length; chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq); if (chan != NULL) { #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) struct timespec ts; get_monotonic_boottime(&ts); tsf = (u64)ts.tv_sec * 1000000 + div_u64(ts.tv_nsec, 1000); mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec*1000000) + ts.tv_nsec/1000; #elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) struct timespec ts; ts = ktime_to_timespec(ktime_get_boottime()); tsf = (u64)ts.tv_sec * 1000000 + div_u64(ts.tv_nsec, 1000); mgmt->u.probe_resp.timestamp = tsf; #else struct timespec64 ts; ts = ktime_to_timespec64(ktime_get_boottime()); tsf = (u64)ts.tv_sec * 1000000 + div_u64(ts.tv_nsec, 1000); mgmt->u.probe_resp.timestamp = tsf; #endif ie = mgmt->u.probe_resp.variable; ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); /* framework use system bootup time */ bss = cfg80211_inform_bss(rwnx_hw->wiphy, chan, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) CFG80211_BSS_FTYPE_UNKNOWN, #endif mgmt->bssid, tsf, capability, beacon_interval, ie, ielen, ind->rssi * 100, GFP_ATOMIC); } if (bss != NULL) cfg80211_put_bss(rwnx_hw->wiphy, bss); return 0; } #endif /* CONFIG_RWNX_FULLMAC */ /*************************************************************************** * Messages from ME task **************************************************************************/ #ifdef CONFIG_RWNX_FULLMAC static inline int rwnx_rx_me_tkip_mic_failure_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct me_tkip_mic_failure_ind *ind = (struct me_tkip_mic_failure_ind *)msg->param; struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; struct net_device *dev = rwnx_vif->ndev; RWNX_DBG(RWNX_FN_ENTRY_STR); cfg80211_michael_mic_failure(dev, (u8 *)&ind->addr, (ind->ga ? NL80211_KEYTYPE_GROUP : NL80211_KEYTYPE_PAIRWISE), ind->keyid, (u8 *)&ind->tsc, GFP_ATOMIC); return 0; } static inline int rwnx_rx_me_tx_credits_update_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct me_tx_credits_update_ind *ind = (struct me_tx_credits_update_ind *)msg->param; RWNX_DBG(RWNX_FN_ENTRY_STR); rwnx_txq_credit_update(rwnx_hw, ind->sta_idx, ind->tid, ind->credits); return 0; } #endif /* CONFIG_RWNX_FULLMAC */ /*************************************************************************** * Messages from SM task **************************************************************************/ #ifdef CONFIG_RWNX_FULLMAC static inline int rwnx_rx_sm_connect_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct sm_connect_ind *ind = (struct sm_connect_ind *)msg->param; struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; struct net_device *dev = rwnx_vif->ndev; const u8 *req_ie, *rsp_ie; const u8 *extcap_ie; const struct ieee_types_extcap *extcap; struct ieee80211_channel *chan; RWNX_DBG(RWNX_FN_ENTRY_STR); /* Retrieve IE addresses and lengths */ req_ie = (const u8 *)ind->assoc_ie_buf; rsp_ie = req_ie + ind->assoc_req_ie_len; // Fill-in the AP information AICWFDBG(LOGINFO, "%s ind->status_code:%d \r\n", __func__, ind->status_code); if (ind->status_code == 0) { struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->ap_idx]; u8 txq_status; struct cfg80211_chan_def chandef; sta->valid = true; sta->sta_idx = ind->ap_idx; sta->ch_idx = ind->ch_idx; sta->vif_idx = ind->vif_idx; sta->vlan_idx = sta->vif_idx; sta->qos = ind->qos; sta->acm = ind->acm; sta->ps.active = false; sta->aid = ind->aid; sta->band = ind->band;//ind->chan.band; sta->width = ind->width;//ind->chan.type; sta->center_freq = ind->center_freq;//ind->chan.prim20_freq; sta->center_freq1 = ind->center_freq1;//ind->chan.center1_freq; sta->center_freq2 = ind->center_freq2;//ind->chan.center2_freq; rwnx_vif->sta.ap = sta; chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);//ind->chan.prim20_freq); cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); if (!rwnx_hw->mod_params->ht_on) chandef.width = NL80211_CHAN_WIDTH_20_NOHT; else chandef.width = chnl2bw[ind->width];//[ind->chan.type]; chandef.center_freq1 = ind->center_freq1;//ind->chan.center1_freq; chandef.center_freq2 = ind->center_freq2;//ind->chan.center2_freq; rwnx_chanctx_link(rwnx_vif, ind->ch_idx, &chandef); memcpy(sta->mac_addr, ind->bssid.array, ETH_ALEN); if (ind->ch_idx == rwnx_hw->cur_chanctx) { txq_status = 0; } else { txq_status = RWNX_TXQ_STOP_CHAN; } memcpy(sta->ac_param, ind->ac_param, sizeof(sta->ac_param)); rwnx_txq_sta_init(rwnx_hw, sta, txq_status); rwnx_txq_tdls_vif_init(rwnx_vif); #ifdef CONFIG_DEBUG_FS rwnx_dbgfs_register_rc_stat(rwnx_hw, sta); #endif rwnx_mu_group_sta_init(sta, NULL); /* Look for TDLS Channel Switch Prohibited flag in the Extended Capability * Information Element*/ extcap_ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, rsp_ie, ind->assoc_rsp_ie_len); if (extcap_ie && extcap_ie[1] >= 5) { extcap = (void *)(extcap_ie); rwnx_vif->tdls_chsw_prohibited = extcap->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED; } if (rwnx_vif->wep_enabled) rwnx_vif->wep_auth_err = false; #ifdef CONFIG_RWNX_BFMER /* If Beamformer feature is activated, check if features can be used * with the new peer device */ if (rwnx_hw->mod_params->bfmer) { const u8 *vht_capa_ie; const struct ieee80211_vht_cap *vht_cap; do { /* Look for VHT Capability Information Element */ vht_capa_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, rsp_ie, ind->assoc_rsp_ie_len); /* Stop here if peer device does not support VHT */ if (!vht_capa_ie) { break; } vht_cap = (const struct ieee80211_vht_cap *)(vht_capa_ie + 2); /* Send MM_BFMER_ENABLE_REQ message if needed */ rwnx_send_bfmer_enable(rwnx_hw, sta, vht_cap); } while (0); } #endif //(CONFIG_RWNX_BFMER) #ifdef CONFIG_RWNX_MON_DATA // If there are 1 sta and 1 monitor interface active at the same time then // monitor interface channel context is always the same as the STA interface. // This doesn't work with 2 STA interfaces but we don't want to support it. if (rwnx_hw->monitor_vif != RWNX_INVALID_VIF) { struct rwnx_vif *rwnx_mon_vif = rwnx_hw->vif_table[rwnx_hw->monitor_vif]; rwnx_chanctx_unlink(rwnx_mon_vif); rwnx_chanctx_link(rwnx_mon_vif, ind->ch_idx, NULL); } #endif atomic_set(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_CONNECTED); } else if (ind->status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { if (rwnx_vif->wep_enabled) { rwnx_vif->wep_auth_err = true; printk("con ind wep_auth_err %d\n", rwnx_vif->wep_auth_err); } atomic_set(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED); }else{ atomic_set(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED); } if (!ind->roamed) cfg80211_connect_result(dev, (const u8 *)ind->bssid.array, req_ie, ind->assoc_req_ie_len, rsp_ie, ind->assoc_rsp_ie_len, ind->status_code, GFP_ATOMIC); else { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) struct cfg80211_roam_info info; memset(&info, 0, sizeof(info)); if (rwnx_vif->ch_index < NX_CHAN_CTXT_CNT) info.channel = rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def.chan; info.bssid = (const u8 *)ind->bssid.array; info.req_ie = req_ie; info.req_ie_len = ind->assoc_req_ie_len; info.resp_ie = rsp_ie; info.resp_ie_len = ind->assoc_rsp_ie_len; cfg80211_roamed(dev, &info, GFP_ATOMIC); #else chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq); cfg80211_roamed(dev #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) || defined(COMPAT_KERNEL_RELEASE) , chan #endif , (const u8 *)ind->bssid.array , req_ie , ind->assoc_req_ie_len , rsp_ie , ind->assoc_rsp_ie_len , GFP_ATOMIC); #endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)*/ } netif_tx_start_all_queues(dev); netif_carrier_on(dev); return 0; } extern u8 dhcped; static inline int rwnx_rx_sm_disconnect_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct sm_disconnect_ind *ind = (struct sm_disconnect_ind *)msg->param; struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; struct net_device *dev; #ifdef AICWF_RX_REORDER struct reord_ctrl_info *reord_info, *tmp; u8 *macaddr; struct aicwf_rx_priv *rx_priv; #endif RWNX_DBG(RWNX_FN_ENTRY_STR); dhcped = 0; if(!rwnx_vif) return 0; dev = rwnx_vif->ndev; #ifdef CONFIG_BR_SUPPORT struct rwnx_vif *vif = netdev_priv(dev); /* clear bridge database */ nat25_db_cleanup(rwnx_vif); #endif /* CONFIG_BR_SUPPORT */ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) rwnx_hw->is_p2p_connected = 0; /* if vif is not up, rwnx_close has already been called */ if (rwnx_vif->up) { if (!ind->ft_over_ds && !ind->reassoc) { cfg80211_disconnected(dev, ind->reason_code, NULL, 0, (ind->reason_code <= 1), GFP_ATOMIC); } netif_tx_stop_all_queues(dev); netif_carrier_off(dev); } #ifdef CONFIG_RWNX_BFMER /* Disable Beamformer if supported */ rwnx_bfmer_report_del(rwnx_hw, rwnx_vif->sta.ap); #endif //(CONFIG_RWNX_BFMER) #ifdef AICWF_RX_REORDER #ifdef AICWF_SDIO_SUPPORT rx_priv = rwnx_hw->sdiodev->rx_priv; #else rx_priv = rwnx_hw->usbdev->rx_priv; #endif if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) { macaddr = rwnx_vif->ndev->dev_addr; printk("deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0], macaddr[1], macaddr[2], \ macaddr[3], macaddr[4], macaddr[5]); spin_lock_bh(&rx_priv->stas_reord_lock); list_for_each_entry_safe(reord_info, tmp, &rx_priv->stas_reord_list, list) { macaddr = rwnx_vif->ndev->dev_addr; printk("reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0], reord_info->mac_addr[1], reord_info->mac_addr[2], \ reord_info->mac_addr[3], reord_info->mac_addr[4], reord_info->mac_addr[5]); if (!memcmp(reord_info->mac_addr, macaddr, 6)) { reord_deinit_sta(rx_priv, reord_info); break; } } spin_unlock_bh(&rx_priv->stas_reord_lock); } else if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) { BUG();//should be not here: del_sta function } #endif rwnx_txq_sta_deinit(rwnx_hw, rwnx_vif->sta.ap); rwnx_txq_tdls_vif_deinit(rwnx_vif); #if 0 rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_vif->sta.ap); #endif rwnx_vif->sta.ap->valid = false; rwnx_vif->sta.ap = NULL; rwnx_external_auth_disable(rwnx_vif); rwnx_chanctx_unlink(rwnx_vif); //msleep(200); atomic_set(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED); return 0; } static inline int rwnx_rx_sm_external_auth_required_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct sm_external_auth_required_ind *ind = (struct sm_external_auth_required_ind *)msg->param; struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL) struct net_device *dev = rwnx_vif->ndev; struct cfg80211_external_auth_params params; RWNX_DBG(RWNX_FN_ENTRY_STR); params.action = NL80211_EXTERNAL_AUTH_START; memcpy(params.bssid, ind->bssid.array, ETH_ALEN); params.ssid.ssid_len = ind->ssid.length; memcpy(params.ssid.ssid, ind->ssid.array, min_t(size_t, ind->ssid.length, sizeof(params.ssid.ssid))); params.key_mgmt_suite = ind->akm; if ((ind->vif_idx > NX_VIRT_DEV_MAX) || !rwnx_vif->up || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_STATION) || cfg80211_external_auth_request(dev, ¶ms, GFP_ATOMIC)) { wiphy_err(rwnx_hw->wiphy, "Failed to start external auth on vif %d", ind->vif_idx); rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif, WLAN_STATUS_UNSPECIFIED_FAILURE); return 0; } rwnx_external_auth_enable(rwnx_vif); #else rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif, WLAN_STATUS_UNSPECIFIED_FAILURE); #endif return 0; } static inline int rwnx_rx_mesh_path_create_cfm(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mesh_path_create_cfm *cfm = (struct mesh_path_create_cfm *)msg->param; struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[cfm->vif_idx]; RWNX_DBG(RWNX_FN_ENTRY_STR); /* Check we well have a Mesh Point Interface */ if (rwnx_vif && (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT)) { rwnx_vif->ap.create_path = false; } return 0; } static inline int rwnx_rx_mesh_peer_update_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mesh_peer_update_ind *ind = (struct mesh_peer_update_ind *)msg->param; struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; struct rwnx_sta *rwnx_sta = &rwnx_hw->sta_table[ind->sta_idx]; RWNX_DBG(RWNX_FN_ENTRY_STR); if ((ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)) || (rwnx_vif && (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)) || (ind->sta_idx >= NX_REMOTE_STA_MAX)) return 1; /* Check we well have a Mesh Point Interface */ if (!rwnx_vif->user_mpm) { /* Check if peer link has been established or lost */ if (ind->estab) { if (!rwnx_sta->valid) { u8 txq_status; rwnx_sta->valid = true; rwnx_sta->sta_idx = ind->sta_idx; rwnx_sta->ch_idx = rwnx_vif->ch_index; rwnx_sta->vif_idx = ind->vif_idx; rwnx_sta->vlan_idx = rwnx_sta->vif_idx; rwnx_sta->ps.active = false; rwnx_sta->qos = true; rwnx_sta->aid = ind->sta_idx + 1; //rwnx_sta->acm = ind->acm; memcpy(rwnx_sta->mac_addr, ind->peer_addr.array, ETH_ALEN); rwnx_chanctx_link(rwnx_vif, rwnx_sta->ch_idx, NULL); /* Add the station in the list of VIF's stations */ INIT_LIST_HEAD(&rwnx_sta->list); list_add_tail(&rwnx_sta->list, &rwnx_vif->ap.sta_list); /* Initialize the TX queues */ if (rwnx_sta->ch_idx == rwnx_hw->cur_chanctx) { txq_status = 0; } else { txq_status = RWNX_TXQ_STOP_CHAN; } rwnx_txq_sta_init(rwnx_hw, rwnx_sta, txq_status); #ifdef CONFIG_DEBUG_FS rwnx_dbgfs_register_rc_stat(rwnx_hw, rwnx_sta); #endif #ifdef CONFIG_RWNX_BFMER // TODO: update indication to contains vht capabilties if (rwnx_hw->mod_params->bfmer) rwnx_send_bfmer_enable(rwnx_hw, rwnx_sta, NULL); rwnx_mu_group_sta_init(rwnx_sta, NULL); #endif /* CONFIG_RWNX_BFMER */ } else { WARN_ON(0); } } else { if (rwnx_sta->valid) { rwnx_sta->ps.active = false; rwnx_sta->valid = false; /* Remove the station from the list of VIF's station */ list_del_init(&rwnx_sta->list); rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta); #ifdef CONFIG_DEBUG_FS rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_sta); #endif } else { WARN_ON(0); } } } else { if (!ind->estab && rwnx_sta->valid) { /* There is no way to inform upper layer for lost of peer, still clean everything in the driver */ rwnx_sta->ps.active = false; rwnx_sta->valid = false; /* Remove the station from the list of VIF's station */ list_del_init(&rwnx_sta->list); rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta); #ifdef CONFIG_DEBUG_FS rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_sta); #endif } else { WARN_ON(0); } } return 0; } static inline int rwnx_rx_mesh_path_update_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mesh_path_update_ind *ind = (struct mesh_path_update_ind *)msg->param; struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; struct rwnx_mesh_path *mesh_path; bool found = false; RWNX_DBG(RWNX_FN_ENTRY_STR); if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)) return 1; if (!rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)) return 0; /* Look for path with provided target address */ list_for_each_entry(mesh_path, &rwnx_vif->ap.mpath_list, list) { if (mesh_path->path_idx == ind->path_idx) { found = true; break; } } /* Check if element has been deleted */ if (ind->delete) { if (found) { #ifdef CREATE_TRACE_POINTS trace_mesh_delete_path(mesh_path); #endif /* Remove element from list */ list_del_init(&mesh_path->list); /* Free the element */ kfree(mesh_path); } } else { if (found) { // Update the Next Hop STA mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx]; #ifdef CREATE_TRACE_POINTS trace_mesh_update_path(mesh_path); #endif } else { // Allocate a Mesh Path structure mesh_path = (struct rwnx_mesh_path *)kmalloc(sizeof(struct rwnx_mesh_path), GFP_ATOMIC); if (mesh_path) { INIT_LIST_HEAD(&mesh_path->list); mesh_path->path_idx = ind->path_idx; mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx]; memcpy(&mesh_path->tgt_mac_addr, &ind->tgt_mac_addr, MAC_ADDR_LEN); // Insert the path in the list of path list_add_tail(&mesh_path->list, &rwnx_vif->ap.mpath_list); #ifdef CREATE_TRACE_POINTS trace_mesh_create_path(mesh_path); #endif } } } return 0; } static inline int rwnx_rx_mesh_proxy_update_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { struct mesh_proxy_update_ind *ind = (struct mesh_proxy_update_ind *)msg->param; struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; struct rwnx_mesh_proxy *mesh_proxy; bool found = false; RWNX_DBG(RWNX_FN_ENTRY_STR); if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)) return 1; if (!rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)) return 0; /* Look for path with provided external STA address */ list_for_each_entry(mesh_proxy, &rwnx_vif->ap.proxy_list, list) { if (!memcmp(&ind->ext_sta_addr, &mesh_proxy->ext_sta_addr, ETH_ALEN)) { found = true; break; } } if (ind->delete && found) { /* Delete mesh path */ list_del_init(&mesh_proxy->list); kfree(mesh_proxy); } else if (!ind->delete && !found) { /* Allocate a Mesh Path structure */ mesh_proxy = (struct rwnx_mesh_proxy *)kmalloc(sizeof(*mesh_proxy), GFP_ATOMIC); if (mesh_proxy) { INIT_LIST_HEAD(&mesh_proxy->list); memcpy(&mesh_proxy->ext_sta_addr, &ind->ext_sta_addr, MAC_ADDR_LEN); mesh_proxy->local = ind->local; if (!ind->local) { memcpy(&mesh_proxy->proxy_addr, &ind->proxy_mac_addr, MAC_ADDR_LEN); } /* Insert the path in the list of path */ list_add_tail(&mesh_proxy->list, &rwnx_vif->ap.proxy_list); } } return 0; } #endif /* CONFIG_RWNX_FULLMAC */ /*************************************************************************** * Messages from APM task **************************************************************************/ /*************************************************************************** * Messages from DEBUG task **************************************************************************/ static inline int rwnx_rx_dbg_error_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { RWNX_DBG(RWNX_FN_ENTRY_STR); return 0; } #ifdef CONFIG_MCU_MESSAGE static inline int rwnx_rx_dbg_custmsg_ind(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, struct ipc_e2a_msg *msg) { dbg_custom_msg_ind_t * ind; char str_msg[32 + 1]; int str_len; RWNX_DBG(RWNX_FN_ENTRY_STR); ind = (dbg_custom_msg_ind_t *)msg->param; str_len = (ind->len < 32) ? ind->len : 32; memcpy(str_msg, (char *)ind->buf, str_len); str_msg[str_len] = '\0'; printk("CustMsgInd: cmd=0x%x, len=%d, str=%s\r\n", ind->cmd, ind->len, str_msg); return 0; } #endif #ifdef CONFIG_RWNX_FULLMAC static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = { [MSG_I(MM_CHANNEL_SWITCH_IND)] = rwnx_rx_chan_switch_ind, [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = rwnx_rx_chan_pre_switch_ind, [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = rwnx_rx_remain_on_channel_exp_ind, [MSG_I(MM_PS_CHANGE_IND)] = rwnx_rx_ps_change_ind, [MSG_I(MM_TRAFFIC_REQ_IND)] = rwnx_rx_traffic_req_ind, [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = rwnx_rx_p2p_vif_ps_change_ind, [MSG_I(MM_CSA_COUNTER_IND)] = rwnx_rx_csa_counter_ind, [MSG_I(MM_CSA_FINISH_IND)] = rwnx_rx_csa_finish_ind, [MSG_I(MM_CSA_TRAFFIC_IND)] = rwnx_rx_csa_traffic_ind, [MSG_I(MM_CHANNEL_SURVEY_IND)] = rwnx_rx_channel_survey_ind, [MSG_I(MM_P2P_NOA_UPD_IND)] = rwnx_rx_p2p_noa_upd_ind, [MSG_I(MM_RSSI_STATUS_IND)] = rwnx_rx_rssi_status_ind, [MSG_I(MM_PKTLOSS_IND)] = rwnx_rx_pktloss_notify_ind, [MSG_I(MM_APM_STALOSS_IND)] = rwnx_apm_staloss_ind, }; static msg_cb_fct scan_hdlrs[MSG_I(SCANU_MAX)] = { [MSG_I(SCANU_START_CFM)] = rwnx_rx_scanu_start_cfm, [MSG_I(SCANU_RESULT_IND)] = rwnx_rx_scanu_result_ind, }; static msg_cb_fct me_hdlrs[MSG_I(ME_MAX)] = { [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = rwnx_rx_me_tkip_mic_failure_ind, [MSG_I(ME_TX_CREDITS_UPDATE_IND)] = rwnx_rx_me_tx_credits_update_ind, }; static msg_cb_fct sm_hdlrs[MSG_I(SM_MAX)] = { [MSG_I(SM_CONNECT_IND)] = rwnx_rx_sm_connect_ind, [MSG_I(SM_DISCONNECT_IND)] = rwnx_rx_sm_disconnect_ind, [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = rwnx_rx_sm_external_auth_required_ind, }; static msg_cb_fct apm_hdlrs[MSG_I(APM_MAX)] = { }; static msg_cb_fct mesh_hdlrs[MSG_I(MESH_MAX)] = { [MSG_I(MESH_PATH_CREATE_CFM)] = rwnx_rx_mesh_path_create_cfm, [MSG_I(MESH_PEER_UPDATE_IND)] = rwnx_rx_mesh_peer_update_ind, [MSG_I(MESH_PATH_UPDATE_IND)] = rwnx_rx_mesh_path_update_ind, [MSG_I(MESH_PROXY_UPDATE_IND)] = rwnx_rx_mesh_proxy_update_ind, }; #endif /* CONFIG_RWNX_FULLMAC */ static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = { [MSG_I(DBG_ERROR_IND)] = rwnx_rx_dbg_error_ind, #ifdef CONFIG_MCU_MESSAGE [MSG_I(DBG_CUSTOM_MSG_IND)] = rwnx_rx_dbg_custmsg_ind, #endif }; static msg_cb_fct tdls_hdlrs[MSG_I(TDLS_MAX)] = { [MSG_I(TDLS_CHAN_SWITCH_CFM)] = rwnx_rx_tdls_chan_switch_cfm, [MSG_I(TDLS_CHAN_SWITCH_IND)] = rwnx_rx_tdls_chan_switch_ind, [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = rwnx_rx_tdls_chan_switch_base_ind, [MSG_I(TDLS_PEER_PS_IND)] = rwnx_rx_tdls_peer_ps_ind, }; static msg_cb_fct *msg_hdlrs[] = { [TASK_MM] = mm_hdlrs, [TASK_DBG] = dbg_hdlrs, #ifdef CONFIG_RWNX_FULLMAC [TASK_TDLS] = tdls_hdlrs, [TASK_SCANU] = scan_hdlrs, [TASK_ME] = me_hdlrs, [TASK_SM] = sm_hdlrs, [TASK_APM] = apm_hdlrs, [TASK_MESH] = mesh_hdlrs, #endif /* CONFIG_RWNX_FULLMAC */ }; /** * */ void rwnx_rx_handle_msg(struct rwnx_hw *rwnx_hw, struct ipc_e2a_msg *msg) { //printk("%s msg->id:0x%x \r\n", __func__, msg->id); rwnx_hw->cmd_mgr->msgind(rwnx_hw->cmd_mgr, msg, msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]); } void rwnx_rx_handle_print(struct rwnx_hw *rwnx_hw, u8 *msg, u32 len) { u8 *data_end = NULL; (void)data_end; if (!rwnx_hw || !rwnx_hw->fwlog_en) { pr_err("FWLOG-OVFL: %s", msg); return; } printk("FWLOG: %s", msg); #ifdef CONFIG_RWNX_DEBUGFS data_end = rwnx_hw->debugfs.fw_log.buf.dataend; if (!rwnx_hw->debugfs.fw_log.buf.data) return ; //printk("end=%lx, len=%d\n", (unsigned long)rwnx_hw->debugfs.fw_log.buf.end, len); spin_lock_bh(&rwnx_hw->debugfs.fw_log.lock); if (rwnx_hw->debugfs.fw_log.buf.end + len > data_end) { int rem = data_end - rwnx_hw->debugfs.fw_log.buf.end; memcpy(rwnx_hw->debugfs.fw_log.buf.end, msg, rem); memcpy(rwnx_hw->debugfs.fw_log.buf.data, &msg[rem], len - rem); rwnx_hw->debugfs.fw_log.buf.end = rwnx_hw->debugfs.fw_log.buf.data + (len - rem); } else { memcpy(rwnx_hw->debugfs.fw_log.buf.end, msg, len); rwnx_hw->debugfs.fw_log.buf.end += len; } rwnx_hw->debugfs.fw_log.buf.size += len; if (rwnx_hw->debugfs.fw_log.buf.size > FW_LOG_SIZE) rwnx_hw->debugfs.fw_log.buf.size = FW_LOG_SIZE; spin_unlock_bh(&rwnx_hw->debugfs.fw_log.lock); #endif }