1385 lines
42 KiB
C
1385 lines
42 KiB
C
/**
|
|
****************************************************************************************
|
|
*
|
|
* @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
|
|
}
|
|
|