luckfox-pico-sdk/sysdrv/drv_ko/wifi/ssv6115/fmac/fmac_rx.c
luckfox-eng29 8f34c2760d project:build.sh: Added fastboot support; custom modifications to U-Boot and kernel implemented using patches.
project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between
the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door
for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more
usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp:
Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for
compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem.
sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device
tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately.
sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick
boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades.
sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for
fastboot.

Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
2024-10-14 09:47:04 +08:00

2199 lines
79 KiB
C

/*
* Copyright (c) 2021 iComm-semi Ltd.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* @file fmac_rx.c
* @brief FMAC RX functions.
*/
/*******************************************************************************
* Include Files
******************************************************************************/
#include <linux/version.h>
#include <net/ieee80211_radiotap.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/jiffies.h>
#include "dbg_ansi_color_codes.h"
#include "ssv_cfg.h"
//#include "ssvdevice/ssv_efuse.h"
#include "fmac.h"
#include "hci/drv_hci_ops.h"
#include "lmac_msg.h"
#include "host_msg.h"
#include "fmac_rx.h"
#include "ssvdevice/dev.h"
#include "fmac_tx.h"
#include "fmac_utils.h"
#include "ipc_msg.h"
#include "ssv_debug.h"
#include "utils/ssv_alloc_skb.h"
#include "fmac_msg_tx.h"
#include "compat.h"
#ifdef FMAC_BRIDGE
#include "fmac_bridge.h"
#endif
/*******************************************************************************
* Local Defines
******************************************************************************/
#define PRIO_STA_NULL 0xAA
static inline
struct ssv_vif *ssv_rx_get_vif(struct ssv_softc *sc, int vif_idx);
extern enum data_frame_types ssv_get_data_frame_type(struct sk_buff *skb);
#ifndef IEEE80211_MAX_CHAINS
#define IEEE80211_MAX_CHAINS 4
#endif
/*******************************************************************************
* Local Enumerations
******************************************************************************/
/*******************************************************************************
* Local Structures
******************************************************************************/
/*******************************************************************************
* Global Variables
******************************************************************************/
const u8 legrates_lut[] = {
0, /* 0 */
1, /* 1 */
2, /* 2 */
3, /* 3 */
-1, /* 4 */
-1, /* 5 */
-1, /* 6 */
-1, /* 7 */
10, /* 8 */
8, /* 9 */
6, /* 10 */
4, /* 11 */
11, /* 12 */
9, /* 13 */
7, /* 14 */
5 /* 15 */
};
extern struct ssv6xxx_cfg ssv_cfg;
extern int ssv_resp_reg_rw_handler(struct ssv_softc *sc, u8 *data);
/*******************************************************************************
* Local Variables
******************************************************************************/
/*******************************************************************************
* Local Functions
******************************************************************************/
/**
* ssv_rx_get_vif - Return pointer to the destination vif
*
* @sc: main driver data
* @vif_idx: vif index present in rx descriptor
*
* Select the vif that should receive this frame returns NULL if the destination
* vif is not active.
* If no vif is specified, this is probably a mgmt broadcast frame, so select
* the first active interface
*/
static inline
struct ssv_vif *ssv_rx_get_vif(struct ssv_softc *sc, int vif_idx)
{
struct ssv_vif *ssv_vif = NULL;
if (vif_idx == 0xFF) {
list_for_each_entry(ssv_vif, &sc->vifs, list) {
if (ssv_vif->up)
return ssv_vif;
}
return NULL;
} else if (vif_idx < NX_VIRT_DEV_MAX) {
ssv_vif = sc->vif_table[vif_idx];
if (!ssv_vif || !ssv_vif->up)
return NULL;
}
return ssv_vif;
}
static void _ssv_rx_mib(struct ssv_softc *sc, u8 *data, bool is_80211, u8 sta_idx, u8 data_type)
{
if(0xFF == sta_idx){
sta_idx = NX_REMOTE_STA_MAX + 1; //BROADCAST/GROUP DATA TX STA Index for virtual AP
}
sc->rx.rx_count++;
sc->rx_bysta[sta_idx].rx_count++;
if(false == is_80211) {
sc->rx.rx_data_count++; //802.3 packet is treat as data frame.
sc->rx_bysta[sta_idx].rx_data_count++;
switch(data_type){
case SSV_ARP_REPLY:
sc->rx.rx_arp_reply_count++;
sc->rx_bysta[sta_idx].rx_arp_reply_count++;
break;
case SSV_ARP_REQUEST:
sc->rx.rx_arp_req_count++;
sc->rx_bysta[sta_idx].rx_arp_req_count++;
break;
case SSV_ICMP_ECHO:
sc->rx.rx_icmp_echo++;
sc->rx_bysta[sta_idx].rx_icmp_echo++;
break;
case SSV_ICMP_ECHOREPLY:
sc->rx.rx_icmp_echoreply++;
sc->rx_bysta[sta_idx].rx_icmp_echoreply++;
break;
case SSV_DHCP_DISCOVER:
sc->rx.rx_dhcp_discv++;
sc->rx_bysta[sta_idx].rx_dhcp_discv++;
break;
case SSV_DHCP_OFFER:
sc->rx.rx_dhcp_offer++;
sc->rx_bysta[sta_idx].rx_dhcp_offer++;
SSV_LOG_DBG("SSV_DHCP_OFFER\n");
break;
case SSV_DHCP_REQUEST:
sc->rx.rx_dhcp_req++;
sc->rx_bysta[sta_idx].rx_dhcp_req++;
break;
case SSV_DHCP_ACK:
sc->rx.rx_dhcp_ack++;
sc->rx_bysta[sta_idx].rx_dhcp_ack++;
SSV_LOG_DBG("SSV_DHCP_ACK\n");
break;
case SSV_EAPOL:
sc->rx.rx_eapol++;
sc->rx_bysta[sta_idx].rx_eapol++;
SSV_LOG_DBG("SSV_EAPOL RX Packet from STA%d\n", sta_idx);
break;
default:
break;
}
}
else
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data;
if (ieee80211_is_data(hdr->frame_control)){
sc->rx.rx_data_count++;
sc->rx_bysta[sta_idx].rx_data_count++;
}else if (ieee80211_is_mgmt(hdr->frame_control)){
sc->rx.rx_mgmt_count++;
sc->rx_bysta[sta_idx].rx_mgmt_count++;
if (ieee80211_is_beacon(hdr->frame_control)){
sc->rx.rx_bcn_count++;
sc->rx_bysta[sta_idx].rx_bcn_count++;
}else if (ieee80211_is_probe_resp(hdr->frame_control)){
sc->rx.rx_proberesp_count++;
sc->rx_bysta[sta_idx].rx_proberesp_count++;
}else if (ieee80211_is_probe_req(hdr->frame_control)){
sc->rx.rx_probereq_count++;
sc->rx_bysta[sta_idx].rx_probereq_count++;
}else if (ieee80211_is_assoc_req(hdr->frame_control)){
sc->rx.rx_assoc_req_count++;
sc->rx_bysta[sta_idx].rx_assoc_req_count++;
}else if (ieee80211_is_assoc_resp(hdr->frame_control)){
sc->rx.rx_assoc_resp_count++;
sc->rx_bysta[sta_idx].rx_assoc_resp_count++;
}else if (ieee80211_is_auth(hdr->frame_control)){
sc->rx.rx_auth_count++;
sc->rx_bysta[sta_idx].rx_auth_count++;
}else if (ieee80211_is_disassoc(hdr->frame_control)){
sc->rx.rx_disassoc_count++;
sc->rx_bysta[sta_idx].rx_disassoc_count++;
}else if (ieee80211_is_deauth(hdr->frame_control)){
sc->rx.rx_deauth_count++;
sc->rx_bysta[sta_idx].rx_deauth_count++;
}
}
}
}
static void ssv_netif_receive_skb(struct sk_buff *skb)
{
struct net_device *ndev = skb->dev;
struct ssv_vif *ssv_vif = netdev_priv(ndev);
struct ssv_softc *sc = (struct ssv_softc *)ssv_vif->sc;
struct ethhdr *eth = (void *)skb->data;
int mcast = is_multicast_ether_addr(eth->h_dest);
struct sk_buff *skb_copy = NULL;
bool resend = false, forward = true;
if (!ssv_vif) {
dev_kfree_skb_any(skb);
return;
}
if ((NL80211_IFTYPE_AP == SSV_VIF_TYPE(ssv_vif) || (NL80211_IFTYPE_AP_VLAN == SSV_VIF_TYPE(ssv_vif))) &&
!(ssv_vif->ap.flags & SSV_AP_ISOLATE)) {
if (mcast) {
/* send multicast frames both to higher layers in
* local net stack and back to the wireless medium
*/
resend = true;
} else {
struct ssv_sta *sta = ssv_get_sta(sc, eth->h_dest);
if (NULL != sta) {
resend = true;
forward = false;
}
}
}
ssv_vif->rx_total_cnt++;
ssv_vif->rx_total_byte+=skb->len;
if (resend) {
/*
* Send to wireless media and increase priority by 256 to
* keep the received priority instead of reclassifying
* the frame (see cfg80211_classify8021d).
* */
skb_copy = skb_copy_expand(skb, sizeof(struct tx_bmu_desc) + /* RESERVED_TX_SIZE */ +
sizeof(struct txdesc_api) +
sizeof(struct sdio_hdr) + SSV_SWTXHDR_ALIGN_SZ, 0, GFP_ATOMIC);
if (skb_copy) {
skb_copy->protocol = htons(ETH_P_802_3);
skb_reset_network_header(skb_copy);
skb_reset_mac_header(skb_copy);
skb_copy->dev = ndev;
ssv_requeue_multicast_skb(skb_copy, ssv_vif);
}
}
if (forward) {
#ifdef FMAC_BRIDGE
if (ssv_bridge_rx_change(ssv_vif, skb) < 0) {
dev_kfree_skb_any(skb);
return;
}
#endif /* FMAC_BRIDGE */
skb->protocol = eth_type_trans(skb, ssv_vif->ndev);
memset(skb->cb, 0, sizeof(skb->cb));
local_bh_disable();
netif_receive_skb(skb);
local_bh_enable();
} else {
dev_kfree_skb_any(skb);
}
}
static int _ssv_push_private_msg_to_host(struct ssv_softc *sc, u8 *data, u32 len)
{
struct sk_buff *skb = NULL;
struct hci_rx_aggr_info *aggr_info= NULL;
u32 *rx_desc = NULL;
struct rx_info *rx_info = NULL;
u8 *rx_buf_headroom = NULL;
u32 frame_len = 0;
u32 frame_oft = 0;
u16 headroom = sizeof(u32) + sizeof(struct rx_info) + RX_BUF_HEADROOM_SIZE;
u16 tail = 0;
bool is_aggr = true;
gfp_t flags;
if(true == is_aggr) {
headroom += sizeof(struct hci_rx_aggr_info);
tail += 4;
}
frame_len = ALIGN((headroom + tail + len), 4);
if (in_softirq())
flags = GFP_ATOMIC;
else
flags = GFP_KERNEL;
skb = ssv_rx_skb_alloc(frame_len, flags);
if (NULL == skb)
{
SSV_LOG_DBG("%s(): Can't alloc skb.\n", __FUNCTION__);
return -1;
}
skb_put(skb, frame_len);
memset((void *)skb->data, 0, skb->len);
/* add tx header and payload */
if(true == is_aggr) {
//build hci_rx_aggr_info
aggr_info =(struct hci_rx_aggr_info *)(skb->data+frame_oft);
aggr_info->jmp_mpdu_len = frame_len-tail;
aggr_info->jmp_mpdu_len1 = aggr_info->jmp_mpdu_len;
aggr_info->extra_info = (frame_len<<16);
frame_oft += sizeof(struct hci_rx_aggr_info);
}
//build rx_desc
rx_desc = (u32 *)(skb->data+frame_oft);
*rx_desc = len << 8;
*rx_desc |= (u8)E_IPC_TYPE_PRIV_MSG;
frame_oft += sizeof(u32);
//build rx_info (empty)
rx_info =(struct rx_info *)(skb->data+frame_oft);
frame_oft += sizeof(struct rx_info);
//build rx buf headroom (empty)
rx_buf_headroom = (u8 *)(skb->data+frame_oft);
frame_oft += RX_BUF_HEADROOM_SIZE;
//copy msg payload
memcpy((void *)(skb->data+frame_oft), data, len);
#if 0 //Debug
_ssv_hex_dump(skb->data, skb->len);
#endif
#ifdef CONFIG_HWIF_AND_HCI
return ssv_drv_hci_rx_enqueue(sc->hci_priv, sc->hci_ops, skb, false); //enqueue to hci rxq head
#else
ssv_rx_skb_free(skb);
return 0;
#endif
}
static void _ssv_free_host_private_msg(u8 *msg_buf)
{
if(NULL != msg_buf)
{
kfree(msg_buf);
}
}
static int _ssv_create_host_private_msg(u32 msgid, u8 *data, u32 datalen, u8 **msg_buf, u32 *msg_buf_len)
{
u32 msg_total_len = (u32)(sizeof(ST_IPC_PRIV_MSG)+datalen);
ST_IPC_PRIV_MSG *msg;
gfp_t flags;
if (in_softirq())
flags = GFP_ATOMIC;
else
flags = GFP_KERNEL;
msg = kzalloc(msg_total_len+1, flags);
if(NULL == msg)
{
SSV_LOG_DBG("[%s][%d] kzalloc() for msg(%u) failed!!\n", __FUNCTION__, __LINE__, msgid);
return -ENOMEM;
}
msg->msgid = (EN_IPC_PRIV_MSG_TYPE)msgid;
msg->msglen = datalen;
if((NULL != data) && (0 != datalen))
{
memcpy((void *)&msg->data[0], (const void *)data, (size_t)datalen);
}
*msg_buf = (u8 *)msg;
*msg_buf_len = msg_total_len;
return 0;
}
/*******************************************************************************
* Global Functions
******************************************************************************/
//#define REODER_DROPPKT_TESTMODE
//#define REODER_DUPPPKT_TESTMODE
#if defined(REODER_DROPPKT_TESTMODE) || defined(REODER_DUPPPKT_TESTMODE)
unsigned int randNum = 0;
#endif
#if defined(REODER_DUPPPKT_TESTMODE)
struct sk_buff *dup_skb;
struct host_reorder_info dup_rx_reorder_info;
#endif
#if 0 //Debug
static void _ssv_hex_dump(unsigned char *data, int len)
{
int i = 0;
SSV_LOG_DBG("\n");
for (i = 1; i <= len; i++)
{
if (i % 16 == 1)
{
SSV_LOG_DBG("0x%p : ", &data[i - 1]);
}
SSV_LOG_DBG("%02x ", data[i - 1]);
if (i % 16 == 0)
SSV_LOG_DBG("\n");
}
SSV_LOG_DBG("\n");
}
#endif
void ssv_rxreord_all_dump(struct ssv_softc *sc, uint16_t sta_idx)
{
uint16_t i = 0, j = 0;
struct rxu_cntrl_reord *rx_reord = NULL;
struct rxu_cntrl_reord_elt *elt = NULL;
for(j = 0; j <= TID_7; j++) {
rx_reord = &sc->sta_table[sta_idx].reord_info.ba_agmts_rx[j];
SSV_LOG_DBG("sta idx: %d tid idx: %d ", sta_idx, j);
if(rx_reord->active == true) {
SSV_LOG_DBG("start: %d %d pos: %d pkt_cnt: %d pn: %lld\n", rx_reord->first_win_start, rx_reord->win_start, rx_reord->rx_status_pos, rx_reord->ooo_pkt_cnt, rx_reord->pn);
for (i = 0; i < RX_CNTRL_REORD_WIN_SIZE; i++) {
elt = &rx_reord->elt[i];
if(elt->skb == NULL)
SSV_LOG_DBG("0 ");
else
SSV_LOG_DBG("%p ", elt->skb);
}
}
SSV_LOG_DBG("\n");
}
}
/**
****************************************************************************************
* @brief Check if the received frame is not a replayed one
*
* @param[in] pn Packet number of the frame
* @param[in] key Key structure attached to the frame
* @param[in] tid TID of the frame
*
* @return true if the frame shall be uploaded, false if it shall be discarded
****************************************************************************************
*/
static bool ssv_reord_check_pn(uint64_t pn, uint64_t old_pn)
{
if (pn > old_pn)
{
return true;
}
return false;
}
/**
****************************************************************************************
* @brief Add an Out Of Order buffer in the reordering structure
*
* @param[in,out] rx_reord Pointer to the reordering structure
* @param[out] elt Pointer to the element to be updated
* @param[in] host_id Host identifier of the Out Of Order buffer
****************************************************************************************
*/
__INLINE void ssv_reord_ooo_add(struct rxu_cntrl_reord *rx_reord,
struct rxu_cntrl_reord_elt *elt,
struct sk_buff *skb)
{
ASSERT_ERR(skb != 0);
elt->skb= skb;
rx_reord->ooo_pkt_cnt++;
//SSV_LOG_DBG("add skb: [%d] win_start: %d skb: %p pkt_cnt: %d\n", rx_reord->rx_status_pos, rx_reord->win_start, skb,rx_reord->ooo_pkt_cnt);
}
/**
****************************************************************************************
* @brief Remove an Out Of Order buffer from the reordering structure
*
* @param[in,out] rx_reord Pointer to the reordering structure
* @param[out] elt Pointer to the element to be updated
****************************************************************************************
*/
__INLINE void ssv_reord_ooo_remove(struct rxu_cntrl_reord *rx_reord,
struct rxu_cntrl_reord_elt *elt)
{
//SSV_LOG_DBG("remove skb: [%d] win_start: %d %p\n", rx_reord->rx_status_pos, rx_reord->win_start, elt->skb);
elt->skb= 0;
rx_reord->ooo_pkt_cnt--;
}
/**
****************************************************************************************
* @brief Move the RX reordering window by one
*
* @param[in,out] rx_reord Pointer to the reordering structure
****************************************************************************************
*/
__INLINE void ssv_reord_update(struct rxu_cntrl_reord *rx_reord)
{
// Move the windows
rx_reord->win_start = (rx_reord->win_start + 1) & MAC_SEQCTRL_NUM_MAX;
rx_reord->rx_status_pos = (rx_reord->rx_status_pos + 1) % RX_CNTRL_REORD_WIN_SIZE;
//SSV_LOG_DBG("update skb: [%d] start: [%d]\n", rx_reord->rx_status_pos, rx_reord->win_start);
}
/**
****************************************************************************************
* @brief Indicates to the host all the packets that have been unblocked by the reception
* of the next waited sequence number
*
* @param[in,out] rx_reord Pointer to the reordering structure
****************************************************************************************
*/
static void ssv_reord_fwd(struct rxu_cntrl_reord *rx_reord)
{
struct rxu_cntrl_reord_elt *elt = NULL;
bool upload = true;
while (rx_reord->elt[rx_reord->rx_status_pos].skb)
{
//SSV_LOG_DBG("fwd skb: [%d] %p\n", rx_reord->rx_status_pos, rx_reord->elt[rx_reord->rx_status_pos].skb);
elt = &rx_reord->elt[rx_reord->rx_status_pos];
ASSERT_ERR(rx_reord->ooo_pkt_cnt);
// Perform PN replay check if required
//[Tim] check, pn not have key how to check
if (elt->pn_check)
{
//SSV_LOG_DBG("pn check: %lld %lld\n", elt->pn, rx_reord->pn);
if(ssv_reord_check_pn(elt->pn, rx_reord->pn) == false) {
dev_kfree_skb_any(elt->skb);
upload = false;
}
}
if(upload ==true) {
// Data has already been copied in host memory and can now be forwarded
ssv_netif_receive_skb(rx_reord->elt[rx_reord->rx_status_pos].skb);
}
// Remove the unordered element from the structure
ssv_reord_ooo_remove(rx_reord, &rx_reord->elt[rx_reord->rx_status_pos]);
// Update the reordering window
ssv_reord_update(rx_reord);
}
}
/**
****************************************************************************************
* @brief Flush a certain amount of positions of the RX reordering window
* This function indicates to the host the packets that have been flushed.
*
* @param[in,out] rx_reord Pointer to the reordering structure
* @param[in] sn_skipped Number of RX reordering window positions to be flushed
****************************************************************************************
*/
static void ssv_reord_flush(struct rxu_cntrl_reord *rx_reord, uint16_t sn_skipped)
{
uint16_t i = 0;
//uint16_t status = RX_STAT_FORWARD;
struct rxu_cntrl_reord_elt *elt = NULL;
bool upload = true;
//SSV_LOG_DBG("reord_flush begin\n");
// Forward all packets that have already been received
for (i = 0; (i < sn_skipped) && rx_reord->ooo_pkt_cnt; i++)
{
uint8_t index = (rx_reord->rx_status_pos + i) % RX_CNTRL_REORD_WIN_SIZE;
elt = &rx_reord->elt[index];
if (elt->skb)
{
//SSV_LOG_DBG("flush skb: [%d] %p\n", index, elt->skb);
//[Tim] check, pn not have key how to check
if (elt->pn_check)
{
//SSV_LOG_DBG("pn check: %lld %lld\n", elt->pn, rx_reord->pn);
if(ssv_reord_check_pn(elt->pn, rx_reord->pn) == false) {
dev_kfree_skb_any(elt->skb);
upload =false;
}
}
if(upload == true) {
// Data has already been copied in host memory and can now be forwarded
ssv_netif_receive_skb(elt->skb);
}
// Remove the unordered element from the structure
ssv_reord_ooo_remove(rx_reord, elt);
}
}
//SSV_LOG_DBG("reord_flush end\n");
rx_reord->win_start = (rx_reord->win_start + sn_skipped) & MAC_SEQCTRL_NUM_MAX;
rx_reord->rx_status_pos = (rx_reord->rx_status_pos + sn_skipped) % RX_CNTRL_REORD_WIN_SIZE;
//SSV_LOG_DBG("reord_flush (%d %d %d)\n", rx_reord->win_start, rx_reord->rx_status_pos, sn_skipped);
// Forward the first packets of the new window that have already been received
ssv_reord_fwd(rx_reord);
}
/**
****************************************************************************************
* @brief Update the reordering information accordingly with the provided BlockAck Request
* PDU
*
* @param[in] sta_idx Index of the transmitter station
* @param[in] frame Pointer to the received frame
****************************************************************************************
*/
bool ssv_reord_bar_check(struct ssv_softc *sc, struct host_reorder_info *rx_reorder_info, struct sk_buff *skb)
{
uint8_t sta_idx = rx_reorder_info->sta_idx;
uint8_t tid = rx_reorder_info->tid;
uint16_t ssn = rx_reorder_info->sn;
struct rxu_cntrl_reord *reord = &sc->sta_table[sta_idx].reord_info.ba_agmts_rx[tid];
bool upload = false;
if ((ssn == reord->win_start) ||
(((ssn - reord->win_start) & MAC_SEQCTRL_NUM_MAX) > (MAC_SEQCTRL_NUM_MAX >> 1))) {
goto exit;
}
/*
* Flush all needed packet so that:
* - WinStart = SSN
* - WinEnd = WinStart + WinSize - 1
*/
ssv_reord_flush(reord, (ssn - reord->win_start) & MAC_SEQCTRL_NUM_MAX);
exit:
dev_kfree_skb_any(skb);
return upload;
}
/**
****************************************************************************************
* @brief Perform the reordering checks on the received frame
* This function may decide to:
* - Upload and forward the frame immediately to the host if it is in order
* - Upload and not forward if the frame is not in order
* - Discard the frame if already received or too old
*
* The RX window is updated according to the previous actions.
*
* @param[in] rxdesc RX descriptor attached to the received frame
* @param[in] sta_idx Index of the transmitter station
*
* @return Whether the frame shall be uploaded or not
****************************************************************************************
*/
bool ssv_reord_data_check(struct ssv_softc *sc, struct host_reorder_info *rx_reorder_info, struct sk_buff *skb)
{
uint8_t sta_idx = rx_reorder_info->sta_idx;
uint8_t tid = rx_reorder_info->tid;
uint16_t sn = rx_reorder_info->sn;
// Returned status
bool upload = true;
// SN position in the sn status bit field
uint16_t sn_pos;
while (1)
{
struct rxu_cntrl_reord *reord = &sc->sta_table[sta_idx].reord_info.ba_agmts_rx[tid];
// Check if the received packet has the lowest expected SN
if (sn == reord->win_start) {
//SSV_LOG_DBG("first\n");
// Perform the PN check if required
if (rx_reorder_info->frame_info & RXU_CNTRL_PN_CHECK_NEEDED) {
if(ssv_reord_check_pn(rx_reorder_info->pn, reord->pn) == true) {
reord->pn = rx_reorder_info->pn;
} else {
upload = false;
}
}
//SSV_LOG_DBG("first upload: %d\n", upload);
//Check if we need to upload the frame
if (upload) {
ssv_netif_receive_skb(skb);
} else {
dev_kfree_skb_any(skb);
}
// Received packet is within the reordering window, the RX agreement can be
// considered as active
reord->active = true;
// Store current time
reord->sn_rx_time = jiffies_to_msecs(jiffies);
// Update the RX Window
ssv_reord_update(reord);
// And forward any ready frames following this one
ssv_reord_fwd(reord);
break;
}
// The packet is not the first expected one, check if it is in the window or not
sn_pos = (sn - reord->win_start) & MAC_SEQCTRL_NUM_MAX;
//SSV_LOG_DBG("sn_pos: %d ", sn_pos);
if (sn_pos >= RX_CNTRL_REORD_WIN_SIZE) {
//SSV_LOG_DBG("over buffer\n");
if (sn_pos < (MAC_SEQCTRL_NUM_MAX >> 1)) {
// Packet is outside the window, and considered as a newer one, so we have
// to move the window and flush the old packets that were waiting for
// reordering
//SSV_LOG_DBG("over buffer reord_flush\n");
ssv_reord_flush(reord, sn_pos - RX_CNTRL_REORD_WIN_SIZE + 1);
// After flushing, the new packet might finally become the first of the
// window, so just in case restart the process from the beginning
continue;
}
// Received packet is older than expected. Two cases apply:
// - Either the agreement is active, and in that case the packet has to be
// discarded
// - Or the agreement is still not active, and in that case we shall
// just consider the packet as being sent outside the BA agreement,
// i.e not consider it as a old one
if (reord->active) {
//SSV_LOG_DBG("reord->active free buffer\n");
// The agreement is active, discard this old packet
dev_kfree_skb_any(skb);
upload = false;
break;
}
// The agreement is still not active, reordering should not be involved
// Perform the duplicate check
//[Tim] check, not info with frame cntl, so i dont know how to check
//upload = rxu_cntrl_duplicate_check(rx_status->frame_cntl, sta_idx, 1);
// Perform the PN check if required
//[Tim] check, pn not have key how to check
if (upload && (rx_reorder_info->frame_info & RXU_CNTRL_PN_CHECK_NEEDED)) {
if(ssv_reord_check_pn(rx_reorder_info->pn, reord->pn) == true) {
reord->pn = rx_reorder_info->pn;
} else {
upload = false;
}
}
//SSV_LOG_DBG("reord->active pn free check: %d\n", upload);
// Check if we need to upload the frame
if (upload) {
ssv_netif_receive_skb(skb);
} else {
dev_kfree_skb_any(skb);
}
break;
}
// Received packet is within the reordering window, the RX agreement can be
// considered as active
reord->active = true;
sn_pos = (sn_pos + reord->rx_status_pos) % RX_CNTRL_REORD_WIN_SIZE;
// Check if the packet has already been received
if (reord->elt[sn_pos].skb)
{
// Discard the MPDU
//SSV_LOG_DBG("the same buffer\n");
dev_kfree_skb_any(skb);
upload = false;
break;
}
// Store the PN and keys if required
//[Tim] check, pn not have key how to check
if (rx_reorder_info->frame_info & RXU_CNTRL_PN_CHECK_NEEDED)
{
reord->elt[sn_pos].pn = rx_reorder_info->pn;
reord->elt[sn_pos].pn_check = true;
} else {
reord->elt[sn_pos].pn_check = false;
}
//SSV_LOG_DBG("add sn_pos: %d\n", sn_pos);
// Data has been received out of order
//rxu_msdu_upload_and_indicate(rxdesc, RX_STAT_ALLOC);
// Store the Host ID in the reordering element
sc->rx.rx_reord_count++; //for MIB RX
sc->rx_bysta[sta_idx].rx_reord_count++;
ssv_reord_ooo_add(reord, &reord->elt[sn_pos], skb);/* linux kernel alloc buffer and copy data*/
break;
}
// If packet is accepted, indicate activity on this RX BlockAck agreement
//[Tim] bam_rx_active, that bam to know time is update, i think porting to host, it not use
if (upload) {
//bam_rx_active(sta_idx, rx_status->tid);
}
return (upload);
}
void ssv_rxreord_free_all(struct ssv_softc *sc, u8 sta_idx)
{
uint16_t i = 0, j = 0;
struct rxu_cntrl_reord *rx_reord = NULL;
struct rxu_cntrl_reord_elt *elt = NULL;
//SSV_LOG_DBG("free all before\n");
for(i = 0; i < TID_MAX; i++) {
rx_reord = &sc->sta_table[sta_idx].reord_info.ba_agmts_rx[i];
for (j = 0; j < RX_CNTRL_REORD_WIN_SIZE; j++) {
uint8_t index =( j % RX_CNTRL_REORD_WIN_SIZE);
elt = &rx_reord->elt[index];
//SSV_LOG_DBG("free skb: [%d]%p\n",index, elt->skb);
if (elt->skb) {
dev_kfree_skb_any(elt->skb);
elt->skb = NULL;
}
}
}
//SSV_LOG_DBG("free all after\n");
}
void ssv_rxreord_flush_tid(struct ssv_softc *sc, u8 sta_idx, u8 tid)
{
uint16_t i = 0;
struct rxu_cntrl_reord *rx_reord = NULL;
struct rxu_cntrl_reord_elt *elt = NULL;
//SSV_LOG_DBG("flush tid begin: %d %d %d\n", sc->sta_table[sta_idx].reord_info.connect, sta_idx, tid);
if(sc->sta_table[sta_idx].reord_info.connect == 1) {
//mutex_lock(&sc->sta_table[sta_idx].reord_info.data_mutex);
rx_reord = &sc->sta_table[sta_idx].reord_info.ba_agmts_rx[tid];
for (i = 0; i < RX_CNTRL_REORD_WIN_SIZE; i++) {
elt = &rx_reord->elt[i];
if (elt->skb) {
//SSV_LOG_DBG("upload skb: [%d]%p\n",i, elt->skb);
ssv_netif_receive_skb(elt->skb);
elt->skb = NULL;
}
}
//mutex_unlock(&sc->sta_table[sta_idx].reord_info.data_mutex);
}
//SSV_LOG_DBG("flush tid end\n");
}
void ssv_rxreord_info_dump(struct host_reorder_info *rx_reorder_info)
{
SSV_LOG_DBG("frame_info: %x sta_idx: %d tid: %d baw_size: %d pn: %lld win_start: %d sn: %d cookie: %d\n",
rx_reorder_info->frame_info,
rx_reorder_info->sta_idx,
rx_reorder_info->tid,
rx_reorder_info->baw_size,
rx_reorder_info->pn,
rx_reorder_info->win_start,
rx_reorder_info->sn,
rx_reorder_info->cookie);
}
void ssv_rxreord_tid_dump(struct ssv_softc *sc, struct host_reorder_info *rx_reorder_info)
{
uint16_t i = 0;
struct rxu_cntrl_reord *rx_reord = NULL;
struct rxu_cntrl_reord_elt *elt = NULL;
uint8_t tid = (rx_reorder_info->tid > TID_7 ? TID_NOQOS: rx_reorder_info->tid);
rx_reord = &sc->sta_table[rx_reorder_info->sta_idx].reord_info.ba_agmts_rx[tid];
SSV_LOG_DBG("tid: %d start: %d %d pos: %d pkt_cnt: %d pn: %lld ",tid, rx_reord->first_win_start, rx_reord->win_start, rx_reord->rx_status_pos, rx_reord->ooo_pkt_cnt, rx_reord->pn);
for (i = 0; i < RX_CNTRL_REORD_WIN_SIZE; i++) {
elt = &rx_reord->elt[i];
if(elt->skb == NULL)
SSV_LOG_DBG("0 ");
else
SSV_LOG_DBG("%p ", elt->skb);
}
SSV_LOG_DBG("\n");
}
bool ssv_rxreord_update_rxinfo_and_upload(struct ssv_softc *sc, struct host_reorder_info *rx_reorder_info)
{
bool upload = true;
struct rxu_cntrl_reord *rx_reord = NULL;
uint16_t *last_seq_cntl=NULL;
uint8_t tid = (rx_reorder_info->tid > TID_7 ? TID_NOQOS: rx_reorder_info->tid);
rx_reord = &sc->sta_table[rx_reorder_info->sta_idx].reord_info.ba_agmts_rx[tid];
//SSV_LOG_DBG("rxreord_update_rxinfo_and_upload flush begin\n");
if(rx_reorder_info->frame_info & RXU_CNTRL_NO_RX_BA_TO_HOST) {
//rece not reorder data, but qos data
if(((tid >=TID_0) && (tid <=TID_7)) && (rx_reord->active ==true)) {
//flush old data
//SSV_LOG_DBG("rxreord_update_rxinfo_and_upload flush\n");
ssv_rxreord_flush_tid(sc, rx_reorder_info->sta_idx, rx_reorder_info->tid);
memset(rx_reord, 0, sizeof(struct rxu_cntrl_reord));
SSV_LOG_DBG("===delete reorder tid: %d win_start: %d win_end: %d===\n", tid, rx_reord->first_win_start, rx_reord->win_end);
}
// Perform the PN check if required
if (rx_reorder_info->frame_info & RXU_CNTRL_PN_CHECK_NEEDED) {
if(ssv_reord_check_pn(rx_reorder_info->pn, rx_reord->pn) == true) {
rx_reord->pn = rx_reorder_info->pn;
} else {
upload = false;
}
}
if((tid >=TID_0) && (tid <=TID_7)){ //QoS Data
last_seq_cntl = &(sc->sta_table[rx_reorder_info->sta_idx].rx_last_seqcntl[rx_reorder_info->tid]);
}
else{ //not QoS Data
last_seq_cntl = &(sc->sta_table[rx_reorder_info->sta_idx].rx_last_seqcntl[TID_NOQOS]);
}
if((rx_reorder_info->cookie == 1)&&
(*last_seq_cntl == rx_reorder_info->sn))
{
//This is a duplicate frame, we need to drop it
upload = false;
}
*last_seq_cntl = rx_reorder_info->sn;
//SSV_LOG_DBG("rxreord_update_rxinfo_and_upload upload: %d\n", upload);
} else {
//rece not reorder data
reorder_check:
//rece first reorder data, need init win_start windows
if(rx_reord->active ==false) {
rx_reord->win_start = rx_reorder_info->win_start;
rx_reord->first_win_start = rx_reorder_info->win_start;
rx_reord->win_end = rx_reorder_info->baw_size;
rx_reord->tid = tid;
rx_reord->sn_rx_time = jiffies_to_msecs(jiffies);
rx_reord->active = true;
//SSV_LOG_DBG("rxreord_update_rxinfo_and_upload init: %d %d %d\n", rx_reord->win_start, rx_reord->first_win_start, rx_reord->win_end);
SSV_LOG_DBG("===create reorder tid: %d win_start: %d win_end: %d===\n", tid, rx_reord->first_win_start, rx_reord->win_end);
} else {
if((rx_reorder_info->win_start != rx_reord->first_win_start) || (rx_reorder_info->baw_size != rx_reord->win_end)) {
//flush old data, and tid re-create
SSV_LOG_DBG("i think maybe not goto here\n");
ssv_rxreord_flush_tid(sc, rx_reorder_info->sta_idx, rx_reorder_info->tid);
memset(rx_reord, 0, sizeof(struct rxu_cntrl_reord));
SSV_LOG_DBG("===delete reorder tid: %d win_start: %d win_end: %d===\n", tid, rx_reord->first_win_start, rx_reord->win_end);
goto reorder_check;
}
#ifdef REODER_DROPPKT_TESTMODE
if(randNum == 0) {
get_random_bytes(&randNum, 4096);
randNum = randNum & 0Xfff;
} else {
randNum--;
if(randNum == 0) {
upload =false;
SSV_LOG_DBG("##### drop #####\n");
}
}
#endif
}
//SSV_LOG_DBG("rxreord_update_rxinfo_and_upload upload2: %d\n", upload);
}
return upload;
}
void ssv_rxreord_timeout_cb(struct ssv_softc *sc)
{
//struct rxu_cntrl_reord_elt *elt = NULL;
uint32_t now_time = jiffies_to_msecs(jiffies);
uint16_t i = 0, j = 0;
struct rxu_cntrl_reord *rx_reord = NULL;
for(i = 0; i < (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX); i++) {
if(sc->sta_table[i].reord_info.connect == 1) {
//SSV_LOG_DBG("=====[%d] ssv_rxreord_timeout_cb:%x %x connect: %d =====\n",i, (unsigned int)sc, (unsigned int)&sc->reord_info[0], sc->reord_info[0].connect);
for(j = 0; j < TID_MAX; j++) {
rx_reord = &sc->sta_table[i].reord_info.ba_agmts_rx[j];
if(rx_reord->active == false)
continue;
//mutex_lock(&sc->sta_table[i].reord_info.data_mutex);
if(rx_reord->ooo_pkt_cnt > 0) {
#if 0
if(time_after((unsigned long)now_time, (unsigned long)rx_reord->sn_rx_time))
SSV_LOG_DBG("jiffies is overflow, we do nothing\n");
#endif
if((now_time - rx_reord->sn_rx_time) > HOUSE_KEEPING_TIMEOUT) {
// Consider the awaited packets as lost
while (!rx_reord->elt[rx_reord->rx_status_pos].skb) {
ssv_reord_update(rx_reord);
}
// Forward all the frames that were waiting after the missing ones
ssv_reord_fwd(rx_reord);
}
}
//mutex_unlock(&sc->sta_table[i].reord_info.data_mutex);
}
}
}
}
void ssv_rxreord_create(struct ssv_softc *sc, u8 sta_idx)
{
SSV_LOG_DBG("===== rxreord create: %d lcok en: %d =====\n", sta_idx, sc->sta_table[sta_idx].reord_info.lock_en);
if(sc->sta_table[sta_idx].reord_info.lock_en == 0) {
mutex_init(&sc->sta_table[sta_idx].reord_info.data_mutex);
//SSV_LOG_DBG("#####sta_idx: %d mutex init check: %p#####\n", sta_idx, &sc->sta_table[sta_idx].reord_info.data_mutex);
sc->sta_table[sta_idx].reord_info.lock_en = 1;
}
memset(&sc->sta_table[sta_idx].reord_info.ba_agmts_rx, 0, sizeof(struct rxu_cntrl_reord)*TID_MAX);
sc->sta_table[sta_idx].reord_info.connect = 1;
}
void ssv_rxreord_delete(struct ssv_softc *sc, u8 sta_idx)
{
SSV_LOG_DBG("===== rxreord delete: %d =====\n", sta_idx);
//mutex_lock(&sc->sta_table[sta_idx].reord_info.data_mutex);
sc->sta_table[sta_idx].reord_info.connect = 0;
ssv_rxreord_free_all(sc, sta_idx);
memset(&sc->sta_table[sta_idx].reord_info.ba_agmts_rx, 0, sizeof(struct rxu_cntrl_reord)*TID_MAX);
//mutex_unlock(&sc->sta_table[sta_idx].reord_info.data_mutex);
}
#if defined(REODER_DUPPPKT_TESTMODE)
void ssv_rxreord_duptest_into(u16 len, struct host_reorder_info *rx_reorder_info)
{
if(randNum == 0) {
get_random_bytes(&randNum, 4096);
randNum = randNum & 0Xfff;
} else {
randNum--;
if(randNum == 0) {
SSV_LOG_DBG("##### dup #####\n");
dup_skb = __dev_alloc_skb(len, GFP_KERNEL);
if (dup_skb) {
memcpy(&dup_rx_reorder_info,rx_reorder_info,sizeof( struct host_reorder_info));
} else {
SSV_LOG_DBG("%s(): cannot alloc skb buffer\n", __FUNCTION__);
randNum = 1;
}
}
}
}
void ssv_rxreord_duptest_exit(struct ssv_softc *sc)
{
if(randNum == 0) {
ssv_reord_data_check(sc, &dup_rx_reorder_info, dup_skb);
}
}
#endif
static void _ssv_rx_mgmt(struct ssv_softc *sc, struct ssv_vif *ssv_vif,
struct sk_buff *skb, struct rx_info *rx_info)
{
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
if ((NL80211_IFTYPE_AP == SSV_VIF_TYPE(ssv_vif))||(NL80211_IFTYPE_P2P_GO == SSV_VIF_TYPE(ssv_vif)))
{
if (ieee80211_is_auth(mgmt->frame_control)){
struct list_head *entry=NULL;
u8 sta_num=0;
list_for_each(entry,&ssv_vif->ap.sta_list)
sta_num++;
if(sta_num==(NX_REMOTE_STA_MAX-1))
{
ssv_xmit_deauth_frame(sc, ssv_vif, NULL, mgmt->sa, mgmt->da, mgmt->da, 6);
return;
}
}
}
// SSV_LOG_DBG("mgmt->frame_control = 0x%04x\n", mgmt->frame_control);
if (ieee80211_is_beacon(mgmt->frame_control)) {
cfg80211_report_obss_beacon(sc->wiphy, skb->data, skb->len,
PHY_INFO_CHAN(rx_info->phy_info),
rx_info->vect.rx_vec_1.rssi1
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
, GFP_ATOMIC
#endif
);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) || defined(CONFIG_SSV_CHANNEL_FOLLOW)
} else if ((ieee80211_is_deauth(mgmt->frame_control) ||
ieee80211_is_disassoc(mgmt->frame_control)) &&
(mgmt->u.deauth.reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
mgmt->u.deauth.reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)) {
cfg80211_rx_unprot_mlme_mgmt(ssv_vif->ndev, skb->data, skb->len);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
} else if ((SSV_VIF_TYPE(ssv_vif) == NL80211_IFTYPE_STATION) &&
(ieee80211_is_action(mgmt->frame_control) &&
(mgmt->u.action.category == 6))) {
struct cfg80211_ft_event_params ft_event;
ft_event.target_ap = (uint8_t *)&mgmt->u.action + ETH_ALEN + 2;
ft_event.ies = (uint8_t *)&mgmt->u.action + ETH_ALEN*2 + 2;
ft_event.ies_len = rx_info->vect.frmlen - (ft_event.ies - (uint8_t *)mgmt);
ft_event.ric_ies = NULL;
ft_event.ric_ies_len = 0;
cfg80211_ft_event(ssv_vif->ndev, &ft_event);
#endif
} else {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
// SSV_LOG_DBG("to call cfg80211_rx_mgmt, rx_info->vect.rx_vec_1.rssi1 = %d\n", rx_info->vect.rx_vec_1.rssi1);
cfg80211_rx_mgmt(&ssv_vif->wdev, PHY_INFO_CHAN(rx_info->phy_info),
rx_info->vect.rx_vec_1.rssi1, skb->data, skb->len, 0);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
cfg80211_rx_mgmt(&ssv_vif->wdev, PHY_INFO_CHAN(rx_info->phy_info),
rx_info->vect.rx_vec_1.rssi1, skb->data, skb->len, 0, GFP_ATOMIC);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
cfg80211_rx_mgmt(&ssv_vif->wdev, PHY_INFO_CHAN(rx_info->phy_info),
rx_info->vect.rx_vec_1.rssi1, skb->data, skb->len, GFP_ATOMIC);
#else
cfg80211_rx_mgmt(ssv_vif->wdev.netdev, PHY_INFO_CHAN(rx_info->phy_info),
rx_info->vect.rx_vec_1.rssi1, skb->data, skb->len, GFP_ATOMIC);
#endif
}
}
/**
* ssv_rx_mgmt - Process one 802.11 management frame
*
* @sc: main driver data
* @ssv_vif: vif that received the buffer
* @skb: skb received
* @rxhdr: HW rx descriptor
*
* Process the management frame and free the corresponding skb
*/
static void ssv_rx_mgmt(struct ssv_softc *sc, struct ssv_vif *ssv_vif,
struct sk_buff *skb, struct rx_info *rx_info)
{
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
if (is_broadcast_ether_addr(mgmt->da)) {
struct ssv_vif *tmp_vif = NULL;
list_for_each_entry(tmp_vif, &sc->vifs, list) {
if (tmp_vif->up) {
_ssv_rx_mgmt(sc, tmp_vif, skb, rx_info);
}
}
} else {
_ssv_rx_mgmt(sc, ssv_vif, skb, rx_info);
}
dev_kfree_skb_any(skb);
}
int ssv_update_sta_last_rx(struct ssv_softc *sc, const u8 *mac_addr)
{
int i;
for (i = 0; i < NX_REMOTE_STA_MAX; i++) {
struct ssv_sta *sta = &sc->sta_table[i];
if (sta->valid && (memcmp(mac_addr, &sta->mac_addr, 6) == 0))
{
sta->last_rx = jiffies_to_msecs(jiffies);
break;
}
}
return 0;
}
void ssv_rx_sw_ack_handler(struct ssv_softc *sc, struct txdesc_api *msg_desc_api)
{
struct sk_buff *clone_skb;
struct ssv_vif *ssv_vif = ssv_rx_get_vif(sc, msg_desc_api->host.vif_idx);
u8* mac_addr;
int qidx = 0, qlen = 0;
if (NULL == ssv_vif)
{
SSV_LOG_DBG("ssv_vif is NULL!!!\n");
return;
}
mac_addr = (u8*)msg_desc_api->host.eth_dest_addr.array;
qlen = skb_queue_len(&sc->wait_sw_ack_q);
for (qidx = 0; qidx < qlen; qidx++)
{
clone_skb = skb_dequeue(&sc->wait_sw_ack_q);
{
bool ack = 0;
struct txdesc_api *clone_desc_api =
(struct txdesc_api *)(clone_skb->data + sizeof(struct sdio_hdr)+sizeof(struct tx_bmu_desc));
if (msg_desc_api->host.sw_ack & 0x8)
ack = 1;
if (ack) {
ssv_update_sta_last_rx(sc, mac_addr);
}
if (clone_desc_api->host.sw_seq == msg_desc_api->host.sw_seq)
{
if ((clone_skb->cb[0] == 'p') && (clone_skb->cb[1] == 'r') && (clone_skb->cb[2] == 'o') &&
(clone_skb->cb[3] == 'b') && (clone_skb->cb[4] == 'e') && (clone_skb->cb[5] == 't') &&
(clone_skb->cb[6] == 'x')) {
dev_kfree_skb_any(clone_skb);
} else if ((clone_skb->cb[0] == 'p') && (clone_skb->cb[1] == 'i') && (clone_skb->cb[2] == 'n') &&
(clone_skb->cb[3] == 'g') && (clone_skb->cb[4] == 'm') && (clone_skb->cb[5] == 'a') &&
(clone_skb->cb[6] == 'c')) {
unsigned long end = jiffies;
SSV_LOG_DBG(KERN_INFO "Get ping response from MAC layer: seq=%u time=%u ms\n", msg_desc_api->host.sw_seq, jiffies_to_msecs(end - sc->ssv_ping.start));
sc->ssv_ping.result = true;
dev_kfree_skb_any(clone_skb);
} else {
cfg80211_mgmt_tx_status(
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
&ssv_vif->wdev,
#else
ssv_vif->wdev.netdev,
#endif
(unsigned long)clone_skb,
(clone_skb->data + sizeof(struct txdesc_api) +
sizeof(struct sdio_hdr)+sizeof(struct tx_bmu_desc)),
clone_desc_api->host.packet_len,
ack, //ack fail set 0
GFP_ATOMIC);
dev_kfree_skb_any(clone_skb);
}
break;
}
else
{
//re enqueue to wait_sw_ack_q
skb_queue_tail(&sc->wait_sw_ack_q, clone_skb);
}
}
}
}
void ssv6xxx_restart_check_rx(struct ssv_softc *sc, struct sk_buff *skb, u8 pkt_type)
{
char *raw_data = (char *)(skb->data);
ST_IPC_PRIV_MSG *msg_priv = (ST_IPC_PRIV_MSG *)raw_data;
if(pkt_type == E_IPC_TYPE_MSG) {
sc->ipc_env->cb.recv_msg_ind(sc, (void *)(skb->data));
} else if(pkt_type == E_IPC_TYPE_PRIV_MSG){
if((msg_priv->msgid != E_HOST_PRIV_MSG_TYPE_RX_REORD_TIMEOUT) &&
(msg_priv->msgid != E_HOST_PRIV_MSG_TYPE_CHECK_FW_STATUS)) {
SSV_LOG_DBG("priv msg unknown id: %d\n", msg_priv->msgid);
}
} else {
SSV_LOG_DBG("unknown event: %d\n", pkt_type);
}
}
void ssv6xxx_check_fw_status_process(struct ssv_softc *sc)
{
u32 cur_status_cnt = 0;
ssv_drv_hci_read_word(sc->hci_priv, sc->hci_ops, 0x08d010f8, &cur_status_cnt);
sc->fw_cur_status_cnt = cur_status_cnt;
if(sc->fw_cur_status_cnt != sc->fw_pre_status_cnt)
{
sc->fw_pre_status_cnt = sc->fw_cur_status_cnt;
sc->fw_cur_status_idle_time = 0;
}
else
{
if(ssv_cfg.fw_status_idle_cnt <= ++sc->fw_cur_status_idle_time)
{
SSV_LOG_DBG("[%s] Not detect FW status for %d times(over %d ms), the FW will be reloaded.\n", __FUNCTION__, sc->fw_cur_status_idle_time, sc->fw_cur_status_idle_time*100); //100 = TIMEOUT_CHECK_FW_STATUS
queue_work(sc->fw_reset_wq, &sc->fw_reset_work);
sc->fw_cur_status_idle_time = 0;
}
}
}
static int ssv_rx_priv_msg_handler(struct ssv_softc *sc, struct sk_buff *skb)
{
ST_IPC_PRIV_MSG *msg = (ST_IPC_PRIV_MSG *)skb->data;
int ret=0;
// SSV_LOG_DBG("[%s][%d] msg->msgid = %u, msg->msglen = %u\n", __FUNCTION__, __LINE__, msg->msgid, msg->msglen);
switch(msg->msgid)
{
case E_IPC_PRIV_MSG_TYPE_TXTPUT_IND:
{
SSV_LOG_DBG(KERN_INFO "txtput result: %u kbps\n", *((u32 *)msg->data));
dev_kfree_skb_any(skb);
break;
}
case E_IPC_PRIV_MSG_TYPE_RXTPUT_IND:
{
// SSV_LOG_DBG(KERN_INFO "rxtput seq_no: %u\n", *((u32 *)msg->data));
ssv_rxtput_calculation(sc, msg->msglen, *((u32 *)msg->data));
dev_kfree_skb_any(skb);
break;
}
case E_IPC_PRIV_MSG_TYPE_SW_ACK_IND:
{
//struct txdesc_api *msg_desc_api = (struct txdesc_api *)msg->data;
//SSV_LOG_DBG("msg sw seq === %d, vif id = %d\n", msg_desc_api->host.sw_seq, msg_desc_api->host.vif_idx);
ssv_rx_sw_ack_handler(sc, (struct txdesc_api*)msg->data);
dev_kfree_skb_any(skb);
break;
}
case E_HOST_PRIV_MSG_TYPE_RX_REORD_CREATE:
{
ssv_rxreord_create(sc, *((u8 *)(msg->data)));
dev_kfree_skb_any(skb);
break;
}
case E_HOST_PRIV_MSG_TYPE_RX_REORD_DEL:
{
ssv_rxreord_delete(sc, *((u8 *)(msg->data)));
dev_kfree_skb_any(skb);
break;
}
case E_HOST_PRIV_MSG_TYPE_RX_REORD_TIMEOUT:
{
ssv_rxreord_timeout_cb(sc);
dev_kfree_skb_any(skb);
break;
}
case E_HOST_PRIV_MSG_TYPE_PROBE_CLIENT:
{
ssv_probe_client(sc);
dev_kfree_skb_any(skb);
break;
}
#ifdef SEND_KEEP_ALIVE
case E_HOST_PRIV_MSG_TYPE_SEND_KEEP_ALIVE:
{
ssv_send_keep_alive(sc);
dev_kfree_skb_any(skb);
break;
}
#endif
#if 0 //move to rftool rx callback
case E_IPC_PRIV_MSG_TYPE_RSP_EFUSE_IND:
{
struct ssv_efuse_tool_param *param = (struct ssv_efuse_tool_param *)msg->data;
ssv_res_update_to_cfg(param);
complete(&sc->ssv_cmd_done);
dev_kfree_skb_any(skb);
break;
}
#endif
case E_HOST_PRIV_MSG_TYPE_CHECK_FW_STATUS:
{
ssv6xxx_check_fw_status_process(sc);
dev_kfree_skb_any(skb);
break;
}
case E_IPC_PRIV_MSG_TYPE_RSP_REG_RW:
{
ssv_resp_reg_rw_handler(sc, msg->data);
dev_kfree_skb_any(skb);
break;
}
case E_IPC_PRIV_MSG_TYPE_PING_IPC_IND:
{
unsigned long end = jiffies;
SSV_LOG_DBG(KERN_INFO "Get ping response from IPC layer: seq=%u time=%u ms\n", *(unsigned int *)msg->data, jiffies_to_msecs(end - sc->ssv_ping.start));
if (*(unsigned int *)msg->data == sc->ssv_ping.seq) {
int i;
bool check = true;
for (i = sizeof(unsigned int) ; i < msg->msglen ; i++) {
if (msg->data[i] != i%0xFF) {
check =false;
}
}
sc->ssv_ping.result = check;
}
dev_kfree_skb_any(skb);
break;
}
default:
{
//SSV_LOG_DBG("[%s][%d] Unknown private msg type(%u)!!\n", __FUNCTION__, __LINE__, msg->msgid);
//dev_kfree_skb_any(skb);
ret = -1;
break;
}
}
return ret;
}
#if 0 //TBD: for struct ieee80211_radiotap_header
#define NX_MAC_VER 21 ///@FIXME: hard-code to sync firmware version
/// Table of conversion between a RX vector rate to a MAC HW rate
const int8_t rxv2macrate[] = {
0, /* 0 */
1, /* 1 */
2, /* 2 */
3, /* 3 */
-1, /* 4 */
-1, /* 5 */
-1, /* 6 */
-1, /* 7 */
10, /* 8 */
8, /* 9 */
6, /* 10 */
4, /* 11 */
11, /* 12 */
9, /* 13 */
7, /* 14 */
5 /* 15 */
};
static int _ssv_rx_get_rate_idx(struct ssv_softc *sc, struct rx_info *info)
{
/// Number of BW
u8 n_bw = sc->phy_ch_bw + 1;
/// Total number of rates
uint16_t n_rates = N_CCK + N_OFDM;
/// First HT rate index
u16 first_ht = n_rates;
/// Rates per HT MCS
u8 ht_rates_per_mcs = n_bw * 2;
/// First VHT rate index
u16 first_vht = 0;
/// Max VHT MCS
u8 max_vht_mcs;
/// Rates per VHT MCS
u8 vht_rates_per_mcs;
int rate_idx = 0, mcs = 0, sgi = 0, nss = 0;
struct rx_vector_1 *rx_vect = &info->vect.rx_vec_1;
{
n_rates += 8 * sc->mod_params->nss * ht_rates_per_mcs;
max_vht_mcs = 9 + 1; //IEEE80211_VHT_MCS_SUPPORT_0_9
first_vht = n_rates;
vht_rates_per_mcs = n_bw * 2;
n_rates += max_vht_mcs * sc->mod_params->nss * vht_rates_per_mcs;
}
switch (rx_vect->format_mod) {
case FORMATMOD_NON_HT:
case FORMATMOD_NON_HT_DUP_OFDM:
{
int idx = rxv2macrate[rx_vect->leg_rate];
if (idx < 4) {
rate_idx = idx * 2 + rx_vect->pre_type;
} else {
rate_idx = N_CCK + idx - 4;
}
break;
}
case FORMATMOD_HT_MF:
case FORMATMOD_HT_GF:
#if NX_MAC_VER >= 20
mcs = rx_vect->ht.mcs % 8;
nss = rx_vect->ht.mcs / 8;
sgi = rx_vect->ht.short_gi;
#else
mcs = rx_vect->mcs % 8;
nss = rx_vect->mcs / 8;
sgi = rx_vect->short_gi;
#endif
rate_idx = first_ht + nss * (8 * ht_rates_per_mcs) +
mcs * ht_rates_per_mcs + rx_vect->ch_bw * 2 + sgi;
break;
case FORMATMOD_VHT:
#if NX_MAC_VER < 20
default:
mcs = rx_vect->mcs;
nss = rx_vect->stbc ? rx_vect->n_sts/2 : rx_vect->n_sts;
sgi = rx_vect->short_gi;
#else
mcs = rx_vect->vht.mcs;
nss = rx_vect->vht.nss;
sgi = rx_vect->vht.short_gi;
#endif
rate_idx = first_vht + nss * (max_vht_mcs * vht_rates_per_mcs) +
mcs * vht_rates_per_mcs + rx_vect->ch_bw * 2 + sgi;
break;
#if 0
#if NX_MAC_VER >= 20
case FORMATMOD_HE_SU:
mcs = rx_vect->he.mcs;
nss = rx_vect->he.nss;
sgi = rx_vect->he.gi_type;
rate_idx = fhost_conf_rx.first_he_su + nss * (fhost_conf_rx.max_he_mcs * fhost_conf_rx.he_su_rates_per_mcs) +
mcs * fhost_conf_rx.he_su_rates_per_mcs + rx_vect->ch_bw * 3 + sgi;
break;
case FORMATMOD_HE_MU:
mcs = rx_vect->he.mcs;
nss = rx_vect->he.nss;
sgi = rx_vect->he.gi_type;
rate_idx = fhost_conf_rx.first_he_mu + nss * fhost_conf_rx.max_he_mcs * fhost_conf_rx.he_mu_rates_per_mcs +
mcs * fhost_conf_rx.he_mu_rates_per_mcs + rx_vect->he.ru_size * 3 + sgi;
break;
default:
mcs = rx_vect->he.mcs;
nss = rx_vect->he.nss;
sgi = rx_vect->he.gi_type;
rate_idx = fhost_conf_rx.first_he_er + rx_vect->ch_bw * 9 +
mcs * fhost_conf_rx.he_er_rates_per_mcs + sgi;
#endif
#endif
}
// SSV_LOG_DBG("[%s][%d] rx_vect->format_mod = %u\n", __FUNCTION__, __LINE__, rx_vect->format_mod);
#if 0 //debug
if(0 != mcs)
{
SSV_LOG_DBG("[%s][%d] rate_idx = %d, mcs = %d, leg_rate = %u\n", __FUNCTION__, __LINE__, rate_idx, mcs, rx_vect->leg_rate);
}
#endif
return rate_idx;
}
#endif //#if 0 //TBD: for struct ieee80211_radiotap_header
struct phy_channel_info_desc {
/** PHY channel information 1 */
u32 phy_band : 8;
u32 phy_channel_type : 8;
u32 phy_prim20_freq : 16;
/** PHY channel information 2 */
u32 phy_center1_freq : 16;
u32 phy_center2_freq : 16;
};
static u8 ssv_rx_rtap_hdrlen(struct rx_vector_1 *rxvect,
bool has_vend_rtap)
{
u8 rtap_len;
/* Compute radiotap header length */
rtap_len = sizeof(struct ieee80211_radiotap_header) + 8;
// Check for multiple antennas
if (hweight32(rxvect->antenna_set) > 1)
// antenna and antenna signal fields
rtap_len += 4 * hweight8(rxvect->antenna_set);
// TSFT
if (!has_vend_rtap) {
rtap_len = ALIGN(rtap_len, 8);
rtap_len += 8;
}
// IEEE80211_HW_SIGNAL_DBM
rtap_len++;
// Check if single antenna
if (hweight32(rxvect->antenna_set) == 1)
rtap_len++; //Single antenna
// padding for RX FLAGS
rtap_len = ALIGN(rtap_len, 2);
// Check for HT frames
if ((rxvect->format_mod == FORMATMOD_HT_MF) ||
(rxvect->format_mod == FORMATMOD_HT_GF))
rtap_len += 3;
// Check for AMPDU
if (!(has_vend_rtap) && ((rxvect->format_mod >= FORMATMOD_VHT) ||
((rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) &&
(rxvect->ht.aggregation)))) {
rtap_len = ALIGN(rtap_len, 4);
rtap_len += 8;
}
// Check for VHT frames
if (rxvect->format_mod == FORMATMOD_VHT) {
rtap_len = ALIGN(rtap_len, 2);
rtap_len += 12;
}
// Check for HE frames
if (rxvect->format_mod == FORMATMOD_HE_SU) {
rtap_len = ALIGN(rtap_len, 2);
rtap_len += sizeof(struct ieee80211_radiotap_he);
}
// Check for multiple antennas
if (hweight32(rxvect->antenna_set) > 1) {
// antenna and antenna signal fields
rtap_len += 2 * hweight8(rxvect->antenna_set);
}
// Check for vendor specific data
if (has_vend_rtap) {
/* vendor presence bitmap */
rtap_len += 4;
/* alignment for fixed 6-byte vendor data header */
rtap_len = ALIGN(rtap_len, 2);
}
return rtap_len;
}
static void ssv_rx_add_rtap_hdr(struct ssv_vif *ssv_vif,
struct sk_buff *skb,
struct rx_vector_1 *rxvect,
struct phy_channel_info_desc *phy_info,
struct rx_vector *vect,
int rtap_len,
u8 vend_rtap_len,
u32 vend_it_present)
{
struct ieee80211_radiotap_header *rtap;
u8 *pos, rate_idx;
__le32 *it_present;
u32 it_present_val = 0;
bool fec_coding = false;
bool short_gi = false;
bool stbc = false;
bool aggregation = false;
struct ssv_softc *sc = ssv_vif->sc;
rtap = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
memset((u8 *) rtap, 0, rtap_len);
rtap->it_version = 0;
rtap->it_pad = 0;
rtap->it_len = cpu_to_le16(rtap_len + vend_rtap_len);
it_present = &rtap->it_present;
// Check for multiple antennas
if (hweight32(rxvect->antenna_set) > 1) {
int chain;
unsigned long chains = rxvect->antenna_set;
for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
it_present_val |=
BIT(IEEE80211_RADIOTAP_EXT) |
BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
put_unaligned_le32(it_present_val, it_present);
it_present++;
it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
}
}
// Check if vendor specific data is present
if (vend_rtap_len) {
it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
BIT(IEEE80211_RADIOTAP_EXT);
put_unaligned_le32(it_present_val, it_present);
it_present++;
it_present_val = vend_it_present;
}
put_unaligned_le32(it_present_val, it_present);
pos = (void *)(it_present + 1);
// IEEE80211_RADIOTAP_TSFT
if (vect) {
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
// padding
while ((pos - (u8 *)rtap) & 7)
*pos++ = 0;
put_unaligned_le64((((u64)le32_to_cpu(vect->tsfhi) << 32) +
(u64)le32_to_cpu(vect->tsflo)), pos);
//maybe zero
pos += 8;
}
// IEEE80211_RADIOTAP_FLAGS
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_FLAGS);
//if (hwvect && (!hwvect->frm_successful_rx))
// *pos |= IEEE80211_RADIOTAP_F_BADFCS;
if (!rxvect->pre_type
&& (rxvect->format_mod <= FORMATMOD_NON_HT_DUP_OFDM))
*pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
pos++;
// IEEE80211_RADIOTAP_RATE
// check for HT, VHT or HE frames
if (rxvect->format_mod >= FORMATMOD_HE_SU) {
rate_idx = rxvect->he.mcs;
fec_coding = rxvect->he.fec;
stbc = rxvect->he.stbc;
aggregation = true;
*pos = 0;
} else if (rxvect->format_mod == FORMATMOD_VHT) {
rate_idx = rxvect->vht.mcs;
fec_coding = rxvect->vht.fec;
short_gi = rxvect->vht.short_gi;
stbc = rxvect->vht.stbc;
aggregation = true;
*pos = 0;
} else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
rate_idx = rxvect->ht.mcs;
fec_coding = rxvect->ht.fec;
short_gi = rxvect->ht.short_gi;
stbc = rxvect->ht.stbc;
aggregation = rxvect->ht.aggregation;
*pos = 0;
} else {
struct wiphy *wiphy = sc->wiphy;
struct ieee80211_supported_band *band = wiphy->bands[NL80211_BAND_2GHZ];
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
BUG_ON((rate_idx = legrates_lut[rxvect->leg_rate]) == -1);
//if (phy_info->phy_band == NL80211_BAND_5GHZ)
// rate_idx -= 4; /* rwnx_ratetable_5ghz[0].hw_value == 4 */
*pos = DIV_ROUND_UP(band->bitrates[rate_idx].bitrate, 5);
}
pos++;
// IEEE80211_RADIOTAP_CHANNEL
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
put_unaligned_le16(phy_info->phy_prim20_freq, pos);
pos += 2;
if (phy_info->phy_band == NL80211_BAND_5GHZ)
put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, pos);
else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM)
put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, pos);
else
put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ, pos);//b to bg
//put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, pos);
pos += 2;
if (hweight32(rxvect->antenna_set) == 1) {
// IEEE80211_RADIOTAP_DBM_ANTSIGNAL
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
*pos++ = rxvect->rssi1;
// IEEE80211_RADIOTAP_ANTENNA
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_ANTENNA);
*pos++ = rxvect->antenna_set;
}
// IEEE80211_RADIOTAP_LOCK_QUALITY is missing
// IEEE80211_RADIOTAP_DB_ANTNOISE is missing
// IEEE80211_RADIOTAP_RX_FLAGS
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RX_FLAGS);
// 2 byte alignment
if ((pos - (u8 *)rtap) & 1)
*pos++ = 0;
put_unaligned_le16(0, pos);
//Right now, we only support fcs error (no RX_FLAG_FAILED_PLCP_CRC)
pos += 2;
// Check if HT
if ((rxvect->format_mod == FORMATMOD_HT_MF)
|| (rxvect->format_mod == FORMATMOD_HT_GF)) {
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
*pos++ = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_BW;
*pos = 0;
if (short_gi)
*pos |= IEEE80211_RADIOTAP_MCS_SGI;
if (rxvect->ch_bw == PHY_CHNL_BW_40)
*pos |= IEEE80211_RADIOTAP_MCS_BW_40;
if (rxvect->format_mod == FORMATMOD_HT_GF)
*pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
if (fec_coding)
*pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
*pos++ |= stbc << 5;
#else
*pos++ |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
#endif
*pos++ = rate_idx;
}
// check for HT or VHT frames
if (aggregation && vect) {
// 4 byte alignment
while ((pos - (u8 *)rtap) & 3)
pos++;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
rtap->it_present |= cpu_to_le32(1 << 20);
#else
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
#endif
//SSV_LOG_DBG("aggregation = %u \n", (vect->ampdu_stat_info) >> 14);
put_unaligned_le32((vect->ampdu_stat_info) >> 14, pos);
pos += 4;
put_unaligned_le32(0, pos);
pos += 4;
}
// Check for VHT frames
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
if (rxvect->format_mod == FORMATMOD_VHT) {
u16 vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
u8 vht_nss = rxvect->vht.nss + 1;
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
if ((rxvect->ch_bw == PHY_CHNL_BW_160)
&& phy_info->phy_center2_freq)
vht_details &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
put_unaligned_le16(vht_details, pos);
pos += 2;
// flags
if (short_gi)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
if (stbc)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
pos++;
// bandwidth
if (rxvect->ch_bw == PHY_CHNL_BW_40)
*pos++ = 1;
if (rxvect->ch_bw == PHY_CHNL_BW_80)
*pos++ = 4;
else if ((rxvect->ch_bw == PHY_CHNL_BW_160)
&& phy_info->phy_center2_freq)
*pos++ = 0; //80P80
else if (rxvect->ch_bw == PHY_CHNL_BW_160)
*pos++ = 11;
else // 20 MHz
*pos++ = 0;
// MCS/NSS
*pos = (rate_idx << 4) | vht_nss;
pos += 4;
if (fec_coding)
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
*pos |= 0x01;
#else
*pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
#endif
pos++;
// group ID
pos++;
// partial_aid
pos += 2;
}
#endif
// Check for HE frames
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) || defined(IEEE80211_HE_MAC_CAP2_TRS)
if (rxvect->format_mod == FORMATMOD_HE_SU) {
struct ieee80211_radiotap_he he;
#define HE_PREP(f, val) cpu_to_le16(FIELD_PREP(IEEE80211_RADIOTAP_HE_##f, val))
#define D1_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_##f##_KNOWN)
#define D2_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_##f##_KNOWN)
he.data1 = D1_KNOWN(DATA_MCS) | D1_KNOWN(BSS_COLOR) | D1_KNOWN(BEAM_CHANGE) |
D1_KNOWN(UL_DL) | D1_KNOWN(CODING) | D1_KNOWN(STBC) |
D1_KNOWN(BW_RU_ALLOC) | D1_KNOWN(DOPPLER) | D1_KNOWN(DATA_DCM);
he.data2 = D2_KNOWN(GI) | D2_KNOWN(TXBF);
if (stbc) {
he.data6 |= HE_PREP(DATA6_NSTS, 2);
he.data3 |= HE_PREP(DATA3_STBC, 1);
} else {
he.data6 |= HE_PREP(DATA6_NSTS, rxvect->he.nss);
}
he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
he.data3 |= HE_PREP(DATA3_BEAM_CHANGE, rxvect->he.beam_change);
he.data3 |= HE_PREP(DATA3_UL_DL, rxvect->he.uplink_flag);
he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
he.data3 |= HE_PREP(DATA3_DATA_MCS, rxvect->he.mcs);
he.data3 |= HE_PREP(DATA3_DATA_DCM, rxvect->he.dcm);
he.data3 |= HE_PREP(DATA3_CODING, rxvect->he.fec);
he.data5 |= HE_PREP(DATA5_GI, rxvect->he.gi_type);
he.data5 |= HE_PREP(DATA5_TXBF, rxvect->he.beamformed);
he.data5 |= HE_PREP(DATA5_LTF_SIZE, rxvect->he.he_ltf_type + 1);
switch (rxvect->ch_bw) {
case PHY_CHNL_BW_20:
he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ);
break;
case PHY_CHNL_BW_40:
he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ);
break;
case PHY_CHNL_BW_80:
he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ);
break;
case PHY_CHNL_BW_160:
he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ);
break;
default:
WARN_ONCE(1, "Invalid SU BW %d\n", rxvect->ch_bw);
}
he.data6 |= HE_PREP(DATA6_DOPPLER, rxvect->he.doppler);
/* ensure 2 byte alignment */
while ((pos - (u8 *)rtap) & 1)
pos++;
rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE);
memcpy(pos, &he, sizeof(he));
pos += sizeof(he);
}
#endif
// Rx Chains
if (hweight32(rxvect->antenna_set) > 1) {
int chain;
unsigned long chains = rxvect->antenna_set;
u8 rssis[4] = {rxvect->rssi1, rxvect->rssi1, rxvect->rssi1, rxvect->rssi1};
for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
*pos++ = rssis[chain];
*pos++ = chain;
}
}
}
static void ssv_rx_monitor(struct ssv_vif *ssv_vif, struct sk_buff *skb,struct rx_info *rx_info)
{
int rtap_len = ssv_rx_rtap_hdrlen(&rx_info->vect.rx_vec_1, false);
ssv_rx_add_rtap_hdr(ssv_vif, skb, &rx_info->vect.rx_vec_1,
(struct phy_channel_info_desc *)&rx_info->phy_info, /*NULL,*/&rx_info->vect,
rtap_len, 0, 0);
skb->dev = ssv_vif->ndev;
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
memset(skb->cb, 0, sizeof(skb->cb));
netif_rx(skb);
}
int ssv_rx_packet_ind(void *app_param, struct sk_buff *skb)
{
struct ssv_softc *sc = (struct ssv_softc *)app_param;
struct rx_info *rx_info = NULL;
struct host_reorder_info *rx_reorder_info = NULL;
u32 *rx_desc = (u32 *)&skb->data[0];
u8 pkt_type = (u8)((*rx_desc) & 0xFF);
u16 real_pkt_len = (u16)((*rx_desc) >> 8);
if(sc->pktrec != NULL) {
if(((sc->dump_level & 0x1)==0x1) ||((sc->dump_level & 0x4)==0x4)) {
sc->pktrec(skb, NULL);
}
}
skb_pull(skb, 4); //Remove HWIF header(4-bytes).
rx_info = (struct rx_info *)skb->data;
skb_pull(skb, sizeof(struct rx_info)); //Remove HW RX information(84-bytes).
rx_reorder_info = (struct host_reorder_info *)skb->data;
skb_pull(skb, RX_BUF_HEADROOM_SIZE); //Remove SW RX headroom(24-bytes).
if(sc->fw_reset_run == true) {
ssv6xxx_restart_check_rx(sc, skb, pkt_type);
dev_kfree_skb_any(skb);
return 0;
}
switch(pkt_type)
{
//MSG
case E_IPC_TYPE_MSG:
{
if(0==sc->ipc_env->cb.recv_msg_ind(sc, (void *)(skb->data)))
{
dev_kfree_skb_any(skb);
return 0;
}
else
{
/* free message */
//SSV_LOG_DBG("unknown message id %d\n", ((struct ipc_e2a_msg *)(skb->data))->id);
dev_kfree_skb_any(skb);
return 0;
}
}
//ACK
case E_IPC_TYPE_ACK:
{
void *hostid = sc->ipc_env->msga2e_hostid;
ASSERT_ERR(hostid);
sc->ipc_env->msga2e_hostid = NULL;
sc->ipc_env->cb.recv_msgack_ind(sc, hostid);
dev_kfree_skb_any(skb);
return 0;
}
//DATA
case E_IPC_TYPE_DATA:
{
u8 vif_idx = (u8)((rx_info->flags & RX_FLAGS_VIF_INDEX_MSK) >> RX_FLAGS_VIF_INDEX_OFT);
u8 sta_idx = (u8)((rx_info->flags & RX_FLAGS_STA_INDEX_MSK) >> RX_FLAGS_STA_INDEX_OFT);
struct ssv_vif *ssv_vif = ssv_rx_get_vif(sc, vif_idx);
bool is_80211 = (rx_info->flags & RX_FLAGS_IS_MPDU_BIT)?true:false;
enum data_frame_types data_type = SSV_DATA_UNKNOW;
// SSV_LOG_DBG("[%s][%d] skb-len = %d, real_pkt_len = %u\n", __FUNCTION__, __LINE__, skb->len, real_pkt_len);
if (NULL == ssv_vif)
{
dev_kfree_skb_any(skb);
// SSV_LOG_DBG("[%s][%d] NULL == ssv_vif\n", __FUNCTION__, __LINE__);
return 0;
}
if(false == is_80211){
data_type = ssv_get_data_frame_type(skb);
}
_ssv_rx_mib(sc, (u8 *)skb->data, is_80211, sta_idx, data_type);
//_ssv_rx_mib(sc, (u8 *)skb->data, is_80211);
if (skb->len != real_pkt_len) {
//SSV_LOG_DBG("[%s][%d] skb->len %d, real_pkt_len %d\n", __FUNCTION__, __LINE__, skb->len, real_pkt_len);
if (real_pkt_len > skb->len) {
dev_kfree_skb_any(skb);
return 0;
}
skb_trim(skb, real_pkt_len);
}
//skb->len = real_pkt_len;
skb->dev = ssv_vif->ndev;
if (true == is_80211)
{
if (NL80211_IFTYPE_MONITOR == SSV_VIF_TYPE(ssv_vif)){
ssv_rx_monitor(ssv_vif, skb, rx_info);
}else{
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
if(ieee80211_is_data(mgmt->frame_control)){
if(false==sc->is_stoping_apm)
{
SSV_LOG_DBG("unknow sta data frame to me: %x:%x:%x:%x:%x:%x\r\n",
mgmt->sa[0],
mgmt->sa[1],
mgmt->sa[2],
mgmt->sa[3],
mgmt->sa[4],
mgmt->sa[5]);
//TO DO: send deauth frame to this client
ssv_xmit_deauth_frame(sc, ssv_vif, NULL, mgmt->sa, mgmt->da, mgmt->da, 6);
}
dev_kfree_skb_any(skb);
}else{
{
struct ssv_vif *tmp_vif = NULL;
bool found = false;
struct sk_buff *skb2 = NULL;
list_for_each_entry(tmp_vif, &sc->vifs, list) {
if (tmp_vif->use_monitor) {
found = true;
break;
}
}
if (true == found) {
skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) {
skb2->dev = tmp_vif->ndev;
ssv_rx_monitor(tmp_vif, skb2, rx_info);
}
}
}
ssv_rx_mgmt(sc, ssv_vif, skb, rx_info);
}
}
return 0;
}
// Check whether to drop the received packet.
if(((NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX) <= sta_idx) || (false == sc->sta_table[sta_idx].valid))
{
dev_kfree_skb_any(skb);
return 0;
}
{
u8* mac_addr = (u8*)(skb->data+6);
ssv_update_sta_last_rx(sc, mac_addr);
}
//skb->len = real_pkt_len;
//skb->dev = ssv_vif->ndev;
#if 0
skb->protocol = eth_type_trans(skb, ssv_vif->ndev);
memset(skb->cb, 0, sizeof(skb->cb));
#endif
//check data is reorder to host pattern
if(rx_reorder_info->upattern == RX_HOST_REORD_PATTERN) {
if(sc->sta_table[rx_reorder_info->sta_idx].reord_info.level == 1) {
ssv_rxreord_info_dump(rx_reorder_info);
ssv_rxreord_tid_dump(sc, rx_reorder_info);
}
//SSV_LOG_DBG("check info ok upload before\n");
if(ssv_rxreord_update_rxinfo_and_upload(sc, rx_reorder_info) == false) {
dev_kfree_skb_any(skb);
return 0;
}
//SSV_LOG_DBG("check info ok upload after\n");
//check data is qos data or no qos data
if(!(rx_reorder_info->frame_info & RXU_CNTRL_NO_RX_BA_TO_HOST)) {
//recv bar
if(sc->sta_table[rx_reorder_info->sta_idx].reord_info.connect == 1) {
//mutex_lock(&sc->sta_table[rx_reorder_info->sta_idx].reord_info.data_mutex);
if((rx_reorder_info->frame_info & RXU_CNTRL_BAR_TO_HOST)) {
//SSV_LOG_DBG("recv bar\n");
ssv_reord_bar_check(sc, rx_reorder_info, skb);
//recv qos data
} else {
//SSV_LOG_DBG("recv data\n");
#if defined(REODER_DUPPPKT_TESTMODE)
ssv_rxreord_duptest_into(skb->len, rx_reorder_info);
#endif
ssv_reord_data_check(sc, rx_reorder_info, skb);
#if defined(REODER_DUPPPKT_TESTMODE)
ssv_rxreord_duptest_exit(sc);
#endif
}
//mutex_unlock(&sc->sta_table[rx_reorder_info->sta_idx].reord_info.data_mutex);
} else {
dev_kfree_skb_any(skb);
}
return 0;
}
}
ssv_netif_receive_skb(skb);
return 0;
}
//PRIVATE MSG
case E_IPC_TYPE_PRIV_MSG:
{
if(0==ssv_rx_priv_msg_handler(sc, skb))
{
return 0;
}
else
{
goto unknown_msg;
}
}
default:
{
//SSV_LOG_ERR("[%s][%d] Unknown packet type(%u)!!\n", __FUNCTION__, __LINE__, pkt_type);
//dev_kfree_skb_any(skb);
//BUG_ON(1);
goto unknown_msg;
}
}
unknown_msg:
skb_push(skb, RX_BUF_HEADROOM_SIZE); //recovery SW RX headroom(24-bytes).
skb_push(skb, sizeof(struct rx_info)); //recovery HW RX information(84-bytes).
skb_push(skb, 4); //recovery HWIF header(4-bytes).
return -1;
}
int ssv_push_private_msg_to_host(struct ssv_softc *sc, u32 msgid, u8 *data, u32 datalen)
{
u8 *msg_buf = NULL;
u32 msg_buf_len = 0;
int ret = _ssv_create_host_private_msg(msgid, data, datalen, &msg_buf, &msg_buf_len);
if(0 != ret)
{
goto END;
}
ret = _ssv_push_private_msg_to_host(sc, msg_buf, msg_buf_len);
_ssv_free_host_private_msg(msg_buf);
END:
return ret;
}