Files
aic8800d80/drivers/aic8800/aic8800_fdrv/rwnx_msg_rx.c
T
MastaG 2c4ab4b421 fix: support Linux 7.3 cfg80211 API
7.3 lands the last part of a two-commit series that converts several
cfg80211_ops cookie parameters from driver-invented output values to
values cfg80211 pre-assigns and passes in as input, and renames
probe_client to probe_peer. Building against 7.3 fails with 5 errors:

  - cfg80211_probe_status() gained an MLO link_id parameter between cookie
    and acked (kernel commit 010e955c203e). This driver has no MLO
    support; -1 is the documented value for non-MLO.
  - cfg80211_ops::probe_client was renamed to probe_peer (4ab9b637b94a).
  - cfg80211_ops::mgmt_tx, ::remain_on_channel and ::probe_peer all
    changed their cookie parameter from u64 * to u64 (914781c72813):
    cfg80211 now pre-assigns the cookie before calling the driver instead
    of the driver inventing one and returning it through the pointer.

The cookie change is more than a type fix. This driver invents its
mgmt_tx/remain_on_channel/probe_peer cookies from a skb pointer, an
internal roc_cookie_cnt counter, and an internal probe_id counter
respectively, and separately re-derives "the same" value later when
reporting completion. Once cfg80211 assigns the cookie itself, the
completion report must echo back the value cfg80211 gave the driver.

Two of the three ops are thin wrappers around a lower-level helper that
is also called from a purely-internal path with no cfg80211-assigned
cookie to honor: rwnx_start_mgmt_xmit() is reused by TDLS, and
rwnx_cfg80211_remain_on_channel_() starts the internal RoC from inside
mgmt_tx. Each of those helpers gets a bool (use_given_cookie / the
existing mgmt_roc_flag) so the internal callers keep inventing a cookie
exactly as before on every kernel, while the cfg80211 op wrappers honor
the pre-assigned value on 7.3+ and thread it through to the completion
report. probe_peer has no helper and no internal caller; the op stores
the cookie itself.

  - rwnx_start_mgmt_xmit() gains use_given_cookie; the resulting cookie is
    stored in a new rwnx_sw_txhdr::cookie and echoed back via
    cfg80211_mgmt_tx_status(). Because sw_txhdr comes from a kmem_cache
    and is never zeroed, every one of the five allocation sites
    initialises the field -- to the skb pointer, which is exactly what the
    completion path reported before the field existed -- so the
    TXU_CNTRL_MGMT filter on the reporting side is not what keeps it safe.
  - rwnx_cfg80211_remain_on_channel_()'s mgmt_roc_flag also selects the
    cookie source, stored in a new rwnx_roc_elem::cookie that the
    rwnx_msg_rx.c completion handlers read.
  - rwnx_cfg80211_probe_client() stores the given cookie in a new
    apm_probe_sta::cookie field for the async probe-status work.

Every gate this adds uses AICWF_CFG80211_VERSION_CODE, so an OpenWrt
backports build selects these paths from the wireless stack's version
rather than the kernel's, matching the existing 7.2 gates.

On kernels before 7.3 the compiled behaviour is identical to main with
two exceptions, both requested in review and both changing only the
order of a store relative to a queue/send: *cookie for probe_client is
taken from probe_id before queue_work() rather than after it, and the
RoC cookie is read from roc_cookie_cnt once, before rwnx_send_roc(),
rather than after it returns. In each case the value differs from
main's only if the worker/RX path has already completed and bumped the
counter in between -- a window in which main returns N+1 while having
already reported N to cfg80211. The reported and returned values now
agree by construction.

v2, addressing review:
  - RoC: the cookie is decided and stored in roc_elem before the element
    is published and rwnx_send_roc() is called, so an early channel-switch
    indication cannot report an uninitialised cookie.
  - probe_peer: the cookie is stored before queue_work(), so a worker
    running immediately on another CPU reports the right one.
  - Monitor injection: intended to initialise sw_txhdr->cookie in
    rwnx_start_monitor_if_xmit(), but the store landed in
    rwnx_start_xmit() instead, where it is never read. Fixed in v5.
  - Pre-3.14: use_given_cookie is part of that signature too and both
    old-signature callers pass false.
  - Rebased onto the OpenWrt backport-version change.

v3:
  - rwnx_main.h gated the probe_client prototype on LINUX_VERSION_CODE
    while rwnx_main.c gated the definition on AICWF_CFG80211_VERSION_CODE.
    Identical on a native build, but a backports build with the two
    decoupled would see a u64 * prototype against a u64 definition.
  - rwnx_cfg80211_remain_on_channel() no longer duplicates the whole call
    to the shared helper just to change one argument; only the cookie
    pointer is selected by version.

v4:
  - rwnx_cfg80211_remain_on_channel_() no longer writes *cookie before
    rwnx_send_roc(). The cookie is snapshotted into a local, roc_elem
    keeps its early assignment, and *cookie is written where it was
    before this patch: inside the error == 0 branch.

v5:
  - The monitor-injection initialisation is now where it was always
    meant to be: rwnx_start_monitor_if_xmit(), between the skb
    assignment and raw_frame = 1, ungated, since that path runs on every
    kernel with CONFIG_RWNX_MON_XMIT. The value is (unsigned long)skb_mgmt,
    which is what rwnx_txdatacfm() reported for these frames before.
  - The remaining allocation sites that did not set the field --
    intf_tx() and rwnx_probersp_work() -- now do, so all five do.
  - The internal RoC's block-local cookie in rwnx_cfg80211_mgmt_tx() is
    renamed roc_cookie: on 7.3 it had come to shadow a same-typed
    parameter with a different meaning.
  - Comment fixes: the rwnx_start_mgmt_xmit() kerneldoc documents
    use_given_cookie; the probe_peer comment states the 7.3 contract; the
    two #endif trailers that close chains now opening on the 7.3 gate say
    so; the unused rx_addr is explained on the 7.3 arm as well.
  - Rebased onto main (set_monitor_channel on 6.12 stable).

Verified against real linux-cachyos-bc250 (7.2.6) and linux-cachyos-rc-bc250
(7.3-rc4) headers with LLVM=1, the way DKMS builds them, asserting on exit
code and module count:

  upstream main @ 7.2.6   rc=0, 3 modules, 13 warnings
  upstream main @ 7.3-rc4 rc=2 - the errors this patch fixes
  this branch   @ 7.2.6   rc=0, 3 modules, 13 warnings
  this branch   @ 7.3-rc4 rc=0, 3 modules, 13 warnings
  warning sets on 7.2.6: identical, and none names a function this
  patch touches.

The backports configuration is not something I can build here, so the
header/definition agreement in v3 rests on both sites using the same
macro rather than on a test.
2026-09-24 12:34:45 +02:00

1789 lines
60 KiB
C

/**
****************************************************************************************
*
* @file rwnx_msg_rx.c
*
* @brief RX function definitions
*
* Copyright (C) RivieraWaves 2012-2019
*
****************************************************************************************
*/
#include <linux/vmalloc.h>
#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"
#ifdef CONFIG_USE_WIRELESS_EXT
#include "aicwf_wext_linux.h"
#endif
#include "rwnx_msg_rx.h"
void rwnx_cfg80211_unlink_bss(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif);
static int rwnx_freq_to_idx(struct rwnx_hw *rwnx_hw, int freq)
{
struct ieee80211_supported_band *sband;
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;
RWNX_DBG(RWNX_FN_ENTRY_STR);
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) {
AICWFDBG(LOGDEBUG, "rwnx_txq_vif_stop\r\n");
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;
RWNX_DBG(RWNX_FN_ENTRY_STR);
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;
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 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
cfg80211_ready_on_channel(roc_elem->wdev->netdev, roc_elem->cookie,
roc_elem->chan, NL80211_CHAN_HT20, roc_elem->duration, GFP_ATOMIC);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
cfg80211_ready_on_channel(roc_elem->wdev, roc_elem->cookie,
roc_elem->chan, NL80211_CHAN_HT20, roc_elem->duration, GFP_ATOMIC);
#else
cfg80211_ready_on_channel(roc_elem->wdev, roc_elem->cookie,
roc_elem->chan, roc_elem->duration, GFP_ATOMIC);
#endif
}
/* 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 = NULL;// = container_of(roc_elem->wdev, struct rwnx_vif, wdev);
RWNX_DBG(RWNX_FN_ENTRY_STR);
if(roc_elem == NULL){
AICWFDBG(LOGERROR,"%s roc_elem == NULL \r\n", __func__);
return 0;
}
rwnx_vif = container_of(roc_elem->wdev, struct rwnx_vif, wdev);
#ifdef CREATE_TRACE_POINTS
/* For debug purpose (use ftrace kernel option) */
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 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
cfg80211_remain_on_channel_expired(roc_elem->wdev->netdev, roc_elem->cookie,
roc_elem->chan, NL80211_CHAN_HT20, GFP_ATOMIC);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
cfg80211_remain_on_channel_expired(roc_elem->wdev, roc_elem->cookie,
roc_elem->chan, NL80211_CHAN_HT20, GFP_ATOMIC);
#else
cfg80211_remain_on_channel_expired(roc_elem->wdev, roc_elem->cookie,
roc_elem->chan, GFP_ATOMIC);
#endif
}
/* 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;
AICWFDBG(LOGTRACE, "%s exit\r\n", __func__);
#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);
tasklet_schedule(&rwnx_hw->task);
}
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;
RWNX_DBG(RWNX_FN_ENTRY_STR);
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_radar_detect_ind(struct rwnx_hw *rwnx_hw,
struct rwnx_cmd *cmd,
struct ipc_e2a_msg *msg)
{
struct radar_pulse_array_desc *pulses = (struct radar_pulse_array_desc *)msg->param;
int i;
RWNX_DBG(RWNX_FN_ENTRY_STR);
//printk("%s\n", __func__);
if(pulses->cnt == 0) {
printk("cnt error\n");
return -1;
}
if(rwnx_radar_detection_is_enable(&rwnx_hw->radar, pulses->idx)) {
for(i=0; i<pulses->cnt; i++) {
struct rwnx_radar_pulses *p = &rwnx_hw->radar.pulses[pulses->idx];
p->buffer[p->index] = pulses->pulse[i];
p->index = (p->index + 1)%RWNX_RADAR_PULSE_MAX;
if(p->count < RWNX_RADAR_PULSE_MAX)
p->count++;
//printk("pulse=%x\n", pulses->pulse[i]);
}
if(!work_pending(&rwnx_hw->radar.detection_work))
schedule_work(&rwnx_hw->radar.detection_work);
} else
printk("not enable\n");
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];
RWNX_DBG(RWNX_FN_ENTRY_STR);
#if 1//2022-01-15 add for rwnx_hw->vif_table[sta->vif_idx] if null when rwnx_close
if (!rwnx_hw->vif_table[sta->vif_idx]){
wiphy_err(rwnx_hw->wiphy, "rwnx_hw->vif_table[sta->vif_idx] is null\n");
return 0;
}
#endif
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 {
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
static inline int rwnx_rx_scanu_start_cfm(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);
if (rwnx_hw->scan_request
#ifdef CONFIG_SCHED_SCAN
&& !rwnx_hw->is_sched_scan
#endif//CONFIG_SCHED_SCAN
#ifdef CONFIG_USE_WIRELESS_EXT
&& !rwnx_hw->wext_scan) {
#else
){
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
cfg80211_scan_done(rwnx_hw->scan_request, &info);
#else
cfg80211_scan_done(rwnx_hw->scan_request, false);
#endif
}
#ifdef CONFIG_USE_WIRELESS_EXT
else if(rwnx_hw->wext_scan){
rwnx_hw->wext_scan = 0;
AICWFDBG(LOGDEBUG, "%s rwnx_hw->wext_scan done!!\r\n", __func__);
if(rwnx_hw->scan_request){
vfree(rwnx_hw->scan_request);
}
complete(&rwnx_hw->wext_scan_com);
}
#endif
else {
AICWFDBG(LOGERROR, "%s rwnx_hw->scan_request is NULL!!\r\n", __func__);
}
#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;
rwnx_hw->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;
#if 0
const u8 *ie = mgmt->u.beacon.variable;
char *ssid = NULL;
int ssid_len = 0;
int freq = 0;
#endif
#ifdef CONFIG_USE_WIRELESS_EXT
struct scanu_result_wext *scan_re_wext;
#endif
RWNX_DBG(RWNX_FN_ENTRY_STR);
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 0
//print scan result info start
if(ie != NULL && bss != NULL){
ssid_len = ie[1];
ssid = (char *)vmalloc(sizeof(char)* (ssid_len + 1));
if(ssid != NULL){
memset(ssid, 0, ssid_len + 1);
memcpy(ssid, &ie[2], ssid_len);
freq = ind->center_freq;
AICWFDBG(LOGDEBUG, "%s %02x:%02x:%02x:%02x:%02x:%02x ssid:%s freq:%d timestamp:%ld\r\n", __func__,
bss->bssid[0],bss->bssid[1],bss->bssid[2],
bss->bssid[3],bss->bssid[4],bss->bssid[5],
ssid, freq, (long)mgmt->u.probe_resp.timestamp);
vfree(ssid);
ssid = NULL;
}else{
AICWFDBG(LOGERROR, "%s ssid vmalloc fail skip printk ssid info \r\n", __func__);
}
}
//print scan result info end
#endif
#ifdef CONFIG_USE_WIRELESS_EXT
if(rwnx_hw->wext_scan){
if (!bss ) {
AICWFDBG(LOGERROR, "%s: Invalid BSS structure\n", __func__);
goto putbss;
}
list_for_each_entry(scan_re_wext, &rwnx_hw->wext_scanre_list, scanu_re_list) {
if (!scan_re_wext || !scan_re_wext->bss ) {
AICWFDBG(LOGDEBUG, "%s: Corrupted list entry detected\n", __func__);
continue;
}
if (!memcmp(scan_re_wext->bss->bssid, bss->bssid, ETH_ALEN)) {
AICWFDBG(LOGDEBUG, "%s: BSSID already exists, no need to add again\r\n", __func__);
goto putbss;
}
}
scan_re_wext = (struct scanu_result_wext *)vmalloc(sizeof(struct scanu_result_wext));
scan_re_wext->ind = (struct scanu_result_ind *)vmalloc(sizeof(struct scanu_result_ind));
scan_re_wext->payload = (u32_l *)vmalloc(sizeof(u32_l) * ind->length);
memset(scan_re_wext->ind, 0, sizeof(struct scanu_result_ind));
memset(scan_re_wext->payload, 0, ind->length);
memcpy(scan_re_wext->ind, ind, sizeof(struct scanu_result_ind));
memcpy(scan_re_wext->payload, ind->payload, ind->length);
scan_re_wext->bss = bss;
INIT_LIST_HEAD(&scan_re_wext->scanu_re_list);
list_add_tail(&scan_re_wext->scanu_re_list, &rwnx_hw->wext_scanre_list);
return 0;
}
#endif
}
#ifdef CONFIG_USE_WIRELESS_EXT
putbss:
#endif
if (bss != NULL)
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
cfg80211_put_bss(bss);
#else
cfg80211_put_bss(rwnx_hw->wiphy, bss);
#endif
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
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
static inline void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
struct ieee80211_channel *chan,
enum nl80211_channel_type chan_type)
{
if (WARN_ON(!chan))
return;
chandef->chan = chan;
chandef->center_freq2 = 0;
switch (chan_type) {
case NL80211_CHAN_NO_HT:
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
chandef->center_freq1 = chan->center_freq;
break;
case NL80211_CHAN_HT20:
chandef->width = NL80211_CHAN_WIDTH_20;
chandef->center_freq1 = chan->center_freq;
break;
case NL80211_CHAN_HT40PLUS:
chandef->width = NL80211_CHAN_WIDTH_40;
chandef->center_freq1 = chan->center_freq + 10;
break;
case NL80211_CHAN_HT40MINUS:
chandef->width = NL80211_CHAN_WIDTH_40;
chandef->center_freq1 = chan->center_freq - 10;
break;
default:
WARN_ON(1);
}
}
#endif
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 = NULL;
const u8 *req_ie, *rsp_ie;
const u8 *extcap_ie;
const struct ieee_types_extcap *extcap;
struct ieee80211_channel *chan;
//struct cfg80211_bss *bss = NULL;
struct wireless_dev *wdev = NULL;
//int retry_counter = 10;
RWNX_DBG(RWNX_FN_ENTRY_STR);
if(!rwnx_vif){
AICWFDBG(LOGERROR, "%s rwnx_vif is null \r\n", __func__);
return 0;
}
dev = rwnx_vif->ndev;
wdev = dev->ieee80211_ptr;
/* Retrieve IE addresses and lengths */
req_ie = (const u8 *)ind->assoc_ie_buf;
rsp_ie = req_ie + ind->assoc_req_ie_len;
if (rwnx_vif->sta.external_auth)
rwnx_vif->sta.external_auth = false;
// 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;
sta->width = ind->width;
sta->center_freq = ind->center_freq;
sta->center_freq1 = ind->center_freq1;
sta->center_freq2 = ind->center_freq2;
rwnx_vif->sta.ap = sta;
chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_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];
chandef.center_freq1 = ind->center_freq1;
chandef.center_freq2 = ind->center_freq2;
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(atomic_read(&rwnx_hw->sta_flowctrl[sta->sta_idx].tx_pending_cnt) > 0) {
AICWFDBG(LOGDEBUG, "sta idx %d fc error %d\n", sta->sta_idx, atomic_read(&rwnx_hw->sta_flowctrl[sta->sta_idx].tx_pending_cnt));
}
#ifdef CONFIG_RADAR_OR_IR_DETECT
if (chan->flags & IEEE80211_CHAN_RADAR)
rwnx_radar_detection_enable(&rwnx_hw->radar,
RWNX_RADAR_DETECT_REPORT,
RWNX_RADAR_RIU);
#endif
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;
AICWFDBG(LOGINFO, "con ind wep_auth_err %d\n", rwnx_vif->wep_auth_err);
}
rwnx_set_conn_state(rwnx_vif, &rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
}else{
rwnx_set_conn_state(rwnx_vif, &rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
}
AICWFDBG(LOGINFO, "%s ind->roamed:%d ind->status_code:%d rwnx_vif->drv_conn_state:%d\r\n",
__func__,
ind->roamed,
ind->status_code,
(int)atomic_read(&rwnx_vif->drv_conn_state));
if(ind->status_code == 0 && (int)atomic_read(&rwnx_vif->drv_conn_state) == RWNX_DRV_STATUS_DISCONNECTING){
AICWFDBG(LOGINFO, "%s the disconnection has been requested, return it\r\n", __func__);
goto exit;
}
if (!ind->roamed) {//not roaming
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);
if (ind->status_code == 0) {
rwnx_set_conn_state(rwnx_vif, &rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_CONNECTED);
} else {
if(atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_CONNECTED) {
AICWFDBG(LOGINFO, "%s roaming fail no roamed ind \r\n", __func__);
cfg80211_disconnected(dev, 0, NULL, 0, 1, GFP_ATOMIC);
}
rwnx_set_conn_state(rwnx_vif, &rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
rwnx_external_auth_disable(rwnx_vif);
}
AICWFDBG(LOGINFO, "%s cfg80211_connect_result pass, rwnx_vif->drv_conn_state:%d\r\n", __func__, (int)atomic_read(&rwnx_vif->drv_conn_state));
}
else {//roaming
if(ind->status_code != 0){
AICWFDBG(LOGINFO, "%s roaming fail to notify disconnect \r\n", __func__);
cfg80211_disconnected(dev, 0, NULL, 0,1, GFP_ATOMIC);
rwnx_set_conn_state(rwnx_vif, &rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
rwnx_external_auth_disable(rwnx_vif);
}else{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
struct cfg80211_roam_info info;
memset(&info, 0, sizeof(info));
#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
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;
#else
if (rwnx_vif->ch_index < NX_CHAN_CTXT_CNT)
info.links[0].channel = rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def.chan;
info.links[0].bssid = (const u8 *)ind->bssid.array;;
#endif//LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
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;
AICWFDBG(LOGINFO, "%s roaming success to notify roam \r\n", __func__);
cfg80211_roamed(dev, &info, GFP_ATOMIC);
#else
chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);
AICWFDBG(LOGINFO, "%s roaming success to notify roam \r\n", __func__);
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)*/
rwnx_set_conn_state(rwnx_vif, &rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_CONNECTED);
}
rwnx_vif->sta.is_roam = false;
}
if (ind->status_code == 0) {
netif_tx_start_all_queues(dev);
netif_carrier_on(dev);
}
exit:
rwnx_vif->sta.is_roam = false;
#ifdef CONFIG_DYNAMIC_PWR
rwnx_hw->sta_rssi_idx = ind->ap_idx;
#endif
return 0;
}
void rwnx_cfg80211_unlink_bss(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif){
struct wiphy *wiphy = rwnx_hw->wiphy;
struct cfg80211_bss *bss = NULL;
RWNX_DBG(RWNX_FN_ENTRY_STR);
bss = cfg80211_get_bss(wiphy, NULL/*notify_channel*/,
rwnx_vif->sta.bssid, rwnx_vif->sta.ssid,
rwnx_vif->sta.ssid_len,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
IEEE80211_BSS_TYPE_ESS,
IEEE80211_PRIVACY(true));//temp set true
#else
WLAN_CAPABILITY_ESS,
WLAN_CAPABILITY_ESS);
#endif
if (bss) {
cfg80211_unlink_bss(wiphy, bss);
AICWFDBG(LOGINFO, "%s(): cfg80211_unlink %s!!\n", __func__, rwnx_vif->sta.ssid);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
cfg80211_put_bss(wiphy, bss);
#else
cfg80211_put_bss(bss);
#endif
}else{
AICWFDBG(LOGINFO, "%s(): cfg80211_unlink error %s!!\n", __func__, rwnx_vif->sta.ssid);
}
}
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);
AICWFDBG(LOGINFO, "%s reason code:%d \r\n", __func__, ind->reason_code);
if((int)atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_DISCONNECTED){
AICWFDBG(LOGINFO, "%s, is already disconnected, drop disconnect ind", __func__);
return 0;
}
dhcped = 0;
if(!rwnx_vif){
AICWFDBG(LOGERROR, "%s rwnx_vif is null \r\n", __func__);
//atomic_set(&rwnx_vif->drv_conn_state, RWNX_DRV_STATUS_DISCONNECTED);
return 0;
}
dev = rwnx_vif->ndev;
if (rwnx_vif->sta.is_roam == false) {
rwnx_cfg80211_unlink_bss(rwnx_hw, rwnx_vif);
} else {
AICWFDBG(LOGINFO, "%s roaming no rwnx_cfg80211_unlink_bss \r\n", __func__);
}
#ifdef CONFIG_DEBUG_FS
rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_vif->sta.ap);
#endif
#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 = (u8*)rwnx_vif->ndev->dev_addr;
AICWFDBG(LOGINFO, "deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0],macaddr[1],macaddr[2], \
macaddr[3],macaddr[4],macaddr[5]);
list_for_each_entry_safe(reord_info, tmp, &rx_priv->stas_reord_list, list) {
macaddr = (u8*)rwnx_vif->ndev->dev_addr;
AICWFDBG(LOGINFO, "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;
}
}
}
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);
if (rwnx_vif->sta.is_roam == false) {
rwnx_set_conn_state(rwnx_vif, &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;
int ret = 0;
struct wireless_dev *wdev = dev->ieee80211_ptr;
int retry_counter = 10;
RWNX_DBG(RWNX_FN_ENTRY_STR);
memset((void*)&params, 0, sizeof(struct cfg80211_external_auth_params));
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;
while (wdev->conn_owner_nlportid == 0) {
AICWFDBG(LOGINFO, "%s WARNING conn_owner_nlportid = 0, msleep 100ms.\r\n", __func__);
msleep(100);
retry_counter--;
if (retry_counter == 0) {
break;
}
}
AICWFDBG(LOGINFO, "%s wdev->conn_owner_nlportid:%d \r\n", __func__, (int)wdev->conn_owner_nlportid);
if ((ind->vif_idx > NX_VIRT_DEV_MAX) || !rwnx_vif->up ||
(RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_STATION) ||
(ret = cfg80211_external_auth_request(dev, &params, GFP_ATOMIC))) {
wiphy_err(rwnx_hw->wiphy, "Failed to start external auth on vif %d, rwnx_vif->up %d, iftype:%d, ret %d",
ind->vif_idx, rwnx_vif->up, RWNX_VIF_TYPE(rwnx_vif), ret);
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;
}
struct fault_ctxt {
uint32_t reg_i[13];
uint32_t SP;
uint32_t LR;
uint32_t PC;
uint32_t xPSR;
uint32_t PSP;
uint32_t MSP;
uint32_t EXC_RETURN;
uint32_t CONTROL;
};
static inline int rwnx_fw_panic_ind(struct rwnx_hw *rwnx_hw,
struct rwnx_cmd *cmd,
struct ipc_e2a_msg *msg)
{
struct fw_panic_info_ind *ind = (struct fw_panic_info_ind *)msg->param;
uint8_t version[36];
struct fault_ctxt fault;
uint32_t msp[64];
uint8_t i;
memcpy(version, ind->info, 36);
version[35] = '\0';
memcpy(&fault, &ind->info[36], sizeof(struct fault_ctxt));
memcpy(msp, &ind->info[36+sizeof(struct fault_ctxt)], 64*4);
printk("fw_panic: len=%d\n", ind->len);
printk("firmware: %s\n", version);
for(i=0; i<13; i++)
printk("REG %d = [%x]\n", i, fault.reg_i[i]);
printk("SP = [%x]\n", fault.SP);
printk("LR = [%x]\n", fault.LR);
printk("PC = [%x]\n", fault.PC);
printk("PSP = [%x]\n", fault.PSP);
printk("xPSR = [%x]\n", fault.xPSR);
printk("EXC_RETURN = [%x]\n", fault.EXC_RETURN);
printk("CONTROL = [%x]\n", fault.CONTROL);
printk("STACK:\n");
for(i=0; i<64; i+=4) {
printk("%08x, %08x, %08x, %08x\n", msp[i], msp[i+1], msp[i+2], msp[i+3]);
}
return 0;
}
static inline int rwnx_fw_assert_ind(struct rwnx_hw *rwnx_hw,
struct rwnx_cmd *cmd,
struct ipc_e2a_msg *msg)
{
struct fw_assert_info_ind *ind = (struct fw_assert_info_ind *)msg->param;
uint8_t buffer[256];
memcpy(buffer, ind->info, ind->len);
buffer[ind->len] = '\0';
printk("%s: %s\n", __func__, buffer);
return 0;
}
#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,
[MSG_I(MM_RADAR_DETECT_IND)] = rwnx_radar_detect_ind,
[MSG_I(MM_FW_PANIC_IND)] = rwnx_fw_panic_ind,
[MSG_I(MM_FW_ASSERT_IND)] = rwnx_fw_assert_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,
};
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(%d) MSG_T(msg->id):%d MSG_I(msg->id):%d cmd:%s\r\n", __func__,
// msg->id,
// MSG_T(msg->id),
// MSG_I(msg->id),
// rwnx_id2str[MSG_T(msg->id)][MSG_I(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;
msg[len-1] = '\0';
if (!rwnx_hw || !rwnx_hw->fwlog_en) {
pr_err("FWLOG-OVFL: %s", msg);
return;
}
AICWFDBG(LOGFW, "%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
}