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>
2199 lines
79 KiB
C
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;
|
|
}
|