/**
 ******************************************************************************
 *
 * @file rwnx_rx.c
 *
 * Copyright (C) RivieraWaves 2012-2019
 *
 ******************************************************************************
 */
#include <linux/dma-mapping.h>
#include <linux/ieee80211.h>
#include <linux/etherdevice.h>
#include <net/ieee80211_radiotap.h>

#include "rwnx_defs.h"
#include "rwnx_rx.h"
#include "rwnx_tx.h"
#include "rwnx_prof.h"
#include "ipc_host.h"
#include "rwnx_events.h"
#include "rwnx_compat.h"
#include "aicwf_txrxif.h"
#ifdef AICWF_ARP_OFFLOAD
#include <linux/ip.h>
#include <linux/udp.h>
#include "rwnx_msg_tx.h"
#endif
#include "rwnx_main.h"
#ifdef CONFIG_BAND_STEERING
#include "aicwf_manager.h"
#endif

#ifndef IEEE80211_MAX_CHAINS
#define IEEE80211_MAX_CHAINS 4
#endif

u8 dhcped = 0;

u16 tx_legrates_lut_rate[] = {
    10 ,
    20 ,
    55 ,
    110 ,
    60 ,
    90 ,
    120,
    180,
    240,
    360,
    480,
    540
};

struct rwnx_legrate legrates_lut[] = {
	[0] = { .idx = 0, .rate = 10},
	[1] = { .idx = 1, .rate = 20},
	[2] = { .idx = 2, .rate = 55},
	[3] = { .idx = 3, .rate = 110},
	[4] = { .idx = -1, .rate = 0},
	[5] = { .idx = -1, .rate = 0},
	[6] = { .idx = -1, .rate = 0},
	[7] = { .idx = -1, .rate = 0},
	[8] = { .idx = 10, .rate = 480},
	[9] = { .idx = 8, .rate = 240},
	[10] = { .idx = 6, .rate = 120},
	[11] = { .idx = 4, .rate = 60},
	[12] = { .idx = 11, .rate = 540},
	[13] = { .idx = 9, .rate = 360},
	[14] = { .idx = 7, .rate = 180},
	[15] = { .idx = 5, .rate = 90},
};

#ifdef CONFIG_BAND_STEERING
typedef struct _op_class_ {
	u8_l        op_class;
	u8_l        band;     /* 0: 2.4g, 1: 5g*/
} _op_class_;

static const _op_class_ g_op_class[] = {
	{ 81, BAND_ON_24G },
	{ 82, BAND_ON_24G },
	{ 83, BAND_ON_24G },
	{ 84, BAND_ON_24G },
	{ 115, BAND_ON_5G },
	{ 116, BAND_ON_5G },
	{ 117, BAND_ON_5G },
	{ 118, BAND_ON_5G },
	{ 119, BAND_ON_5G },
	{ 120, BAND_ON_5G },
	{ 121, BAND_ON_5G },
	{ 122, BAND_ON_5G },
	{ 123, BAND_ON_5G },
	{ 124, BAND_ON_5G },
	{ 125, BAND_ON_5G },
	{ 126, BAND_ON_5G },
	{ 127, BAND_ON_5G },
	{ 128, BAND_ON_5G }
};

#define BAND_CAP_2G    BIT(BAND_ON_24G)
#define BAND_CAP_5G    BIT(BAND_ON_5G)
#endif

struct vendor_radiotap_hdr {
    u8 oui[3];
    u8 subns;
    u16 len;
    u8 data[];
};

void rwnx_skb_align_8bytes(struct sk_buff *skb);


/**
 * rwnx_rx_get_vif - Return pointer to the destination vif
 *
 * @rwnx_hw: 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 or vif is not specified in the descriptor.
 */
static inline
struct rwnx_vif *rwnx_rx_get_vif(struct rwnx_hw *rwnx_hw, int vif_idx)
{
    struct rwnx_vif *rwnx_vif = NULL;

    if (vif_idx < NX_VIRT_DEV_MAX) {
        rwnx_vif = rwnx_hw->vif_table[vif_idx];
        if(!rwnx_vif){
            AICWFDBG(LOGERROR, "%s rwnx_hw->vif_table[%d] NULL\r\n", __func__, vif_idx);
            return NULL;
        }else if(!rwnx_vif->up){
            AICWFDBG(LOGERROR, "%s rwnx_hw->vif_table[%d] is down\r\n", __func__, vif_idx);
            return NULL;
        }
    }

    return rwnx_vif;
}

/**
 * rwnx_rx_vector_convert - Convert a legacy RX vector into a new RX vector format
 *
 * @rwnx_hw: main driver data.
 * @rx_vect1: Rx vector 1 descriptor of the received frame.
 * @rx_vect2: Rx vector 2 descriptor of the received frame.
 */
static void rwnx_rx_vector_convert(struct rwnx_hw *rwnx_hw,
                                   struct rx_vector_1 *rx_vect1,
                                   struct rx_vector_2 *rx_vect2)
{
    struct rx_vector_1_old rx_vect1_leg;
    struct rx_vector_2_old rx_vect2_leg;
    u32_l phy_vers = rwnx_hw->version_cfm.version_phy_2;

    // Check if we need to do the conversion. Only if old modem is used
    if (__MDM_MAJOR_VERSION(phy_vers) > 0) {
        rx_vect1->rssi1 = rx_vect1->rssi_leg;
        return;
    }

    // Copy the received vector locally
    memcpy(&rx_vect1_leg, rx_vect1, sizeof(struct rx_vector_1_old));

    // Reset it
    memset(rx_vect1, 0, sizeof(struct rx_vector_1));

    // Perform the conversion
    rx_vect1->format_mod = rx_vect1_leg.format_mod;
    rx_vect1->ch_bw = rx_vect1_leg.ch_bw;
    rx_vect1->antenna_set = rx_vect1_leg.antenna_set;
    rx_vect1->leg_length = rx_vect1_leg.leg_length;
    rx_vect1->leg_rate = rx_vect1_leg.leg_rate;
    rx_vect1->rssi1 = rx_vect1_leg.rssi1;

    switch (rx_vect1->format_mod) {
        case FORMATMOD_NON_HT:
        case FORMATMOD_NON_HT_DUP_OFDM:
            rx_vect1->leg.lsig_valid = rx_vect1_leg.lsig_valid;
            rx_vect1->leg.chn_bw_in_non_ht = rx_vect1_leg.num_extn_ss;
            rx_vect1->leg.dyn_bw_in_non_ht = rx_vect1_leg.dyn_bw;
            break;
        case FORMATMOD_HT_MF:
        case FORMATMOD_HT_GF:
            rx_vect1->ht.aggregation = rx_vect1_leg.aggregation;
            rx_vect1->ht.fec = rx_vect1_leg.fec_coding;
            rx_vect1->ht.lsig_valid = rx_vect1_leg.lsig_valid;
            rx_vect1->ht.length = rx_vect1_leg.ht_length;
            rx_vect1->ht.mcs = rx_vect1_leg.mcs;
            rx_vect1->ht.num_extn_ss = rx_vect1_leg.num_extn_ss;
            rx_vect1->ht.short_gi = rx_vect1_leg.short_gi;
            rx_vect1->ht.smoothing = rx_vect1_leg.smoothing;
            rx_vect1->ht.sounding = rx_vect1_leg.sounding;
            rx_vect1->ht.stbc = rx_vect1_leg.stbc;
            break;
        case FORMATMOD_VHT:
            rx_vect1->vht.beamformed = !rx_vect1_leg.smoothing;
            rx_vect1->vht.fec = rx_vect1_leg.fec_coding;
            rx_vect1->vht.length = rx_vect1_leg.ht_length | rx_vect1_leg._ht_length << 8;
            rx_vect1->vht.mcs = rx_vect1_leg.mcs & 0x0F;
            rx_vect1->vht.nss = rx_vect1_leg.stbc ? rx_vect1_leg.n_sts/2 : rx_vect1_leg.n_sts;
            rx_vect1->vht.doze_not_allowed = rx_vect1_leg.doze_not_allowed;
            rx_vect1->vht.short_gi = rx_vect1_leg.short_gi;
            rx_vect1->vht.sounding = rx_vect1_leg.sounding;
            rx_vect1->vht.stbc = rx_vect1_leg.stbc;
            rx_vect1->vht.group_id = rx_vect1_leg.group_id;
            rx_vect1->vht.partial_aid = rx_vect1_leg.partial_aid;
            rx_vect1->vht.first_user = rx_vect1_leg.first_user;
            break;
    }

    if (!rx_vect2)
        return;

    // Copy the received vector 2 locally
    memcpy(&rx_vect2_leg, rx_vect2, sizeof(struct rx_vector_2_old));

    // Reset it
    memset(rx_vect2, 0, sizeof(struct rx_vector_2));

    rx_vect2->rcpi1 = rx_vect2_leg.rcpi;
    rx_vect2->rcpi2 = rx_vect2_leg.rcpi;
    rx_vect2->rcpi3 = rx_vect2_leg.rcpi;
    rx_vect2->rcpi4 = rx_vect2_leg.rcpi;

    rx_vect2->evm1 = rx_vect2_leg.evm1;
    rx_vect2->evm2 = rx_vect2_leg.evm2;
    rx_vect2->evm3 = rx_vect2_leg.evm3;
    rx_vect2->evm4 = rx_vect2_leg.evm4;
}

/**
 * rwnx_rx_statistic - save some statistics about received frames
 *
 * @rwnx_hw: main driver data.
 * @hw_rxhdr: Rx Hardware descriptor of the received frame.
 * @sta: STA that sent the frame.
 */
static void rwnx_rx_statistic(struct rwnx_hw *rwnx_hw, struct hw_rxhdr *hw_rxhdr,
                              struct rwnx_sta *sta)
{
#if 1//def CONFIG_RWNX_DEBUGFS
    struct rwnx_stats *stats = &rwnx_hw->stats;
    struct rwnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
    struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
    int mpdu, ampdu, mpdu_prev, rate_idx;

    /* save complete hwvect */
    sta->stats.last_rx = hw_rxhdr->hwvect;

    /* update ampdu rx stats */
    mpdu = hw_rxhdr->hwvect.mpdu_cnt;
    ampdu = hw_rxhdr->hwvect.ampdu_cnt;
    mpdu_prev = stats->ampdus_rx_map[ampdu];

    if (mpdu_prev < mpdu ) {
        stats->ampdus_rx_miss += mpdu - mpdu_prev - 1;
    } else {
        stats->ampdus_rx[mpdu_prev]++;
    }
    stats->ampdus_rx_map[ampdu] = mpdu;

    /* update rx rate statistic */
    if (!rate_stats->size)
        return;

    if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
        int mcs;
        int bw = rxvect->ch_bw;
        int sgi;
        int nss;
        switch (rxvect->format_mod) {
            case FORMATMOD_HT_MF:
            case FORMATMOD_HT_GF:
                mcs = rxvect->ht.mcs % 8;
                nss = rxvect->ht.mcs / 8;
                sgi = rxvect->ht.short_gi;
                rate_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 +  bw * 2 + sgi;
                break;
            case FORMATMOD_VHT:
                mcs = rxvect->vht.mcs;
                nss = rxvect->vht.nss;
                sgi = rxvect->vht.short_gi;
                rate_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
                break;
            case FORMATMOD_HE_SU:
                mcs = rxvect->he.mcs;
                nss = rxvect->he.nss;
                sgi = rxvect->he.gi_type;
                rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
                break;
            default:
                mcs = rxvect->he.mcs;
                nss = rxvect->he.nss;
                sgi = rxvect->he.gi_type;
                rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU
                                                + nss * 216 + mcs * 18 + rxvect->he.ru_size * 3 + sgi;
                break;
        }
    } else {
        int idx = legrates_lut[rxvect->leg_rate].idx;
        if (idx < 4) {
            rate_idx = idx * 2 + rxvect->pre_type;
        } else {
            rate_idx = N_CCK + idx - 4;
        }
    }
    if (rate_idx < rate_stats->size) {
        if (!rate_stats->table[rate_idx])
            rate_stats->rate_cnt++;
        rate_stats->table[rate_idx]++;
        rate_stats->cpt++;
    } else {
        wiphy_err(rwnx_hw->wiphy, "RX: Invalid index conversion => %d/%d\n",
                  rate_idx, rate_stats->size);
    }
#endif
}

/**
 * rwnx_rx_data_skb - Process one data frame
 *
 * @rwnx_hw: main driver data
 * @rwnx_vif: vif that received the buffer
 * @skb: skb received
 * @rxhdr: HW rx descriptor
 * @return: true if buffer has been forwarded to upper layer
 *
 * If buffer is amsdu , it is first split into a list of skb.
 * Then each skb may be:
 * - forwarded to upper layer
 * - resent on wireless interface
 *
 * When vif is a STA interface, every skb is only forwarded to upper layer.
 * When vif is an AP interface, multicast skb are forwarded and resent, whereas
 * skb for other BSS's STA are only resent.
 */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
#define RAISE_RX_SOFTIRQ() \
    cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
#endif /* LINUX_VERSION_CODE  */

void rwnx_rx_data_skb_resend(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif, struct sk_buff *skb)
{
	struct sk_buff *rx_skb = skb;
	//bool amsdu = rxhdr->flags_is_amsdu;
	const struct ethhdr *eth;
	struct sk_buff *skb_copy;

	rx_skb->dev = rwnx_vif->ndev;
	skb_reset_mac_header(rx_skb);
	eth = eth_hdr(rx_skb);

    //printk("resend\n");
	/* resend pkt on wireless interface */
	/* always need to copy buffer when forward=0 to get enough headrom for tsdesc */
	skb_copy = skb_copy_expand(rx_skb, sizeof(struct rwnx_txhdr) +
		RWNX_SWTXHDR_ALIGN_SZ + 3 + 24 + 8, 0, GFP_ATOMIC);

	if (skb_copy) {
		int res;
		skb_copy->protocol = htons(ETH_P_802_3);
		skb_reset_network_header(skb_copy);
		skb_reset_mac_header(skb_copy);

		rwnx_vif->is_resending = true;
		res = dev_queue_xmit(skb_copy);
		rwnx_vif->is_resending = false;
		/* note: buffer is always consummed by dev_queue_xmit */
		if (res == NET_XMIT_DROP) {
			rwnx_vif->net_stats.rx_dropped++;
			rwnx_vif->net_stats.tx_dropped++;
		} else if (res != NET_XMIT_SUCCESS) {
			netdev_err(rwnx_vif->ndev,
					   "Failed to re-send buffer to driver (res=%d)",
					   res);
			rwnx_vif->net_stats.tx_errors++;
		}
	} else {
		netdev_err(rwnx_vif->ndev, "Failed to copy skb");
	}
}
#ifdef AICWF_RX_REORDER
static void rwnx_rx_data_skb_forward(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif, struct sk_buff *skb)
{
	struct sk_buff *rx_skb;

    rx_skb = skb;
	rx_skb->dev = rwnx_vif->ndev;
    skb_reset_mac_header(rx_skb);

	/* Update statistics */
	rwnx_vif->net_stats.rx_packets++;
	rwnx_vif->net_stats.rx_bytes += rx_skb->len;

    //printk("forward\n");
#ifdef CONFIG_BR_SUPPORT
    void *br_port = NULL;

    if (1) {//(check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE) == _TRUE) {
        /* Insert NAT2.5 RX here! */
	#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
        br_port = rwnx_vif->ndev->br_port;
	#else
        rcu_read_lock();
        br_port = rcu_dereference(rwnx_vif->ndev->rx_handler_data);
        rcu_read_unlock();
	#endif

        if (br_port) {
            int nat25_handle_frame(struct rwnx_vif *vif, struct sk_buff *skb);

            if (nat25_handle_frame(rwnx_vif, rx_skb) == -1) {
                /* priv->ext_stats.rx_data_drops++; */
                /* DEBUG_ERR("RX DROP: nat25_handle_frame fail!\n"); */
                /* return FAIL; */

            }
        }
    }
#endif /* CONFIG_BR_SUPPORT */

	rwnx_skb_align_8bytes(rx_skb);

	rx_skb->protocol = eth_type_trans(rx_skb, rwnx_vif->ndev);
	memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
	REG_SW_SET_PROFILING(rwnx_hw, SW_PROF_IEEE80211RX);


#ifdef CONFIG_FILTER_TCP_ACK
	filter_rx_tcp_ack(rwnx_hw, rx_skb->data, cpu_to_le16(rx_skb->len));
#endif

	#ifdef CONFIG_RX_NETIF_RECV_SKB //modify by aic
	local_bh_disable();
	netif_receive_skb(rx_skb);
	local_bh_enable();
	#else
	if (in_interrupt()) {
		netif_rx(rx_skb);
	} else {
	/*
	* If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
	* * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
	*/
	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
		netif_rx_ni(rx_skb);
	#else
		ulong flags;
		netif_rx(rx_skb);
		local_irq_save(flags);
		RAISE_RX_SOFTIRQ();
		local_irq_restore(flags);
	#endif
	}
	#endif
	REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_IEEE80211RX);

	rwnx_hw->stats.last_rx = jiffies;
}
#endif


static bool rwnx_rx_data_skb(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
                             struct sk_buff *skb,  struct hw_rxhdr *rxhdr)
{
    struct sk_buff_head list;
    struct sk_buff *rx_skb;
    bool amsdu = rxhdr->flags_is_amsdu;
    bool resend = false, forward = true;
    u8 flags_vif_idx = rxhdr->flags_vif_idx;
    u8 flags_dst_idx = rxhdr->flags_dst_idx;

    skb->dev = rwnx_vif->ndev;

    __skb_queue_head_init(&list);

    if (amsdu) {
        #if 1
        rwnx_rxdata_process_amsdu(rwnx_hw, skb, flags_vif_idx, &list);
        #else
        int count;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
        ieee80211_amsdu_to_8023s(skb, &list, rwnx_vif->ndev->dev_addr,
                                 RWNX_VIF_TYPE(rwnx_vif), 0, NULL, NULL, false);
#else
        ieee80211_amsdu_to_8023s(skb, &list, rwnx_vif->ndev->dev_addr,
                                 RWNX_VIF_TYPE(rwnx_vif), 0, NULL, NULL);
#endif

        count = skb_queue_len(&list);
        if (count > ARRAY_SIZE(rwnx_hw->stats.amsdus_rx))
            count = ARRAY_SIZE(rwnx_hw->stats.amsdus_rx);
        rwnx_hw->stats.amsdus_rx[count - 1]++;
        #endif
    } else {
        rwnx_hw->stats.amsdus_rx[0]++;
        __skb_queue_head(&list, skb);
    }

    if (((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP) ||
         (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN) ||
         (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO)) &&
        !(rwnx_vif->ap.flags & RWNX_AP_ISOLATE)) {
        const struct ethhdr *eth;
        rx_skb = skb_peek(&list);
        skb_reset_mac_header(rx_skb);
        eth = eth_hdr(rx_skb);

        if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
            /* broadcast pkt need to be forwared to upper layer and resent
               on wireless interface */
            resend = true;
        } else {
            /* unicast pkt for STA inside the BSS, no need to forward to upper
               layer simply resend on wireless interface */
            if (flags_dst_idx != RWNX_INVALID_STA)
            {
                struct rwnx_sta *sta = &rwnx_hw->sta_table[flags_dst_idx];
                if (sta->valid && (sta->vlan_idx == rwnx_vif->vif_index))
                {
                    forward = false;
                    resend = true;
                }
            }
        }
    } else if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT) {
        const struct ethhdr *eth;
        rx_skb = skb_peek(&list);
        skb_reset_mac_header(rx_skb);
        eth = eth_hdr(rx_skb);

        if (!is_multicast_ether_addr(eth->h_dest)) {
            /* unicast pkt for STA inside the BSS, no need to forward to upper
               layer simply resend on wireless interface */
            if (flags_dst_idx != RWNX_INVALID_STA)
            {
                forward = false;
                resend = true;
            }
        }
    }

    while (!skb_queue_empty(&list)) {
        rx_skb = __skb_dequeue(&list);

        /* resend pkt on wireless interface */
        if (resend) {
            struct sk_buff *skb_copy;
            /* always need to copy buffer when forward=0 to get enough headrom for tsdesc */
            skb_copy = skb_copy_expand(rx_skb, sizeof(struct rwnx_txhdr) +
                                       RWNX_SWTXHDR_ALIGN_SZ + 3 + 24 + 8, 0, GFP_ATOMIC);

            if (skb_copy) {
                int res;
                skb_copy->protocol = htons(ETH_P_802_3);
                skb_reset_network_header(skb_copy);
                skb_reset_mac_header(skb_copy);

                rwnx_vif->is_resending = true;
                res = dev_queue_xmit(skb_copy);
                rwnx_vif->is_resending = false;
                /* note: buffer is always consummed by dev_queue_xmit */
                if (res == NET_XMIT_DROP) {
                    rwnx_vif->net_stats.rx_dropped++;
                    rwnx_vif->net_stats.tx_dropped++;
                } else if (res != NET_XMIT_SUCCESS) {
                    netdev_err(rwnx_vif->ndev,
                               "Failed to re-send buffer to driver (res=%d)",
                               res);
                    rwnx_vif->net_stats.tx_errors++;
                }
            } else {
                netdev_err(rwnx_vif->ndev, "Failed to copy skb");
            }
        }

        /* forward pkt to upper layer */
        if (forward) {
#ifdef CONFIG_BR_SUPPORT
            void *br_port = NULL;


            /* Update statistics */
            rwnx_vif->net_stats.rx_packets++;
            rwnx_vif->net_stats.rx_bytes += rx_skb->len;

            if (1) {//(check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE) == _TRUE) {
                /* Insert NAT2.5 RX here! */
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
                br_port = rwnx_vif->ndev->br_port;
#else
                rcu_read_lock();
                br_port = rcu_dereference(rwnx_vif->ndev->rx_handler_data);
                rcu_read_unlock();
#endif

                if (br_port) {
                    int nat25_handle_frame(struct rwnx_vif *vif, struct sk_buff *skb);

                    if (nat25_handle_frame(rwnx_vif, rx_skb) == -1) {
                        /* priv->ext_stats.rx_data_drops++; */
                        /* DEBUG_ERR("RX DROP: nat25_handle_frame fail!\n"); */
                        /* return FAIL; */
                    }
                }
            }
#endif /* CONFIG_BR_SUPPORT */

		rwnx_skb_align_8bytes(rx_skb);

        rx_skb->protocol = eth_type_trans(rx_skb, rwnx_vif->ndev);

#ifdef AICWF_ARP_OFFLOAD
            if(RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT)
                arpoffload_proc(rx_skb, rwnx_vif);
#endif
            memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
            REG_SW_SET_PROFILING(rwnx_hw, SW_PROF_IEEE80211RX);


#ifdef CONFIG_FILTER_TCP_ACK
            filter_rx_tcp_ack(rwnx_hw, rx_skb->data, cpu_to_le16(rx_skb->len));
#endif

            #ifdef CONFIG_RX_NETIF_RECV_SKB //modify by aic
            local_bh_disable();
            netif_receive_skb(rx_skb);
            local_bh_enable();
            #else
            if (in_interrupt()) {
                netif_rx(rx_skb);
            } else {
            /*
            * If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
            * * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
            */
            #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
                netif_rx_ni(rx_skb);
            #else
                ulong flags;
                netif_rx(rx_skb);
                local_irq_save(flags);
                RAISE_RX_SOFTIRQ();
                local_irq_restore(flags);
            #endif
            }
            #endif
            REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_IEEE80211RX);

            rwnx_hw->stats.last_rx = jiffies;
        }
    }

    return forward;
}

#ifdef CONFIG_HE_FOR_OLD_KERNEL
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0))
const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
				 const u8 *match, int match_len,
				 int match_offset)
{
	const struct element *elem;

	/* match_offset can't be smaller than 2, unless match_len is
	 * zero, in which case match_offset must be zero as well.
	 */
	if (WARN_ON((match_len && match_offset < 2) ||
		    (!match_len && match_offset)))
		return NULL;

	for_each_element_id(elem, eid, ies, len) {
		if (elem->datalen >= match_offset - 2 + match_len &&
		    !memcmp(elem->data + match_offset - 2, match, match_len))
			return (void *)elem;
	}

	return NULL;
}
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))

static inline const u8 *cfg80211_find_ext_ie(u8 ext_eid, const u8* ies, int len)
{
        return cfg80211_find_ie_match(WLAN_EID_EXTENSION, ies, len,
                                                        &ext_eid, 1, 2);
}
#endif

#endif

#ifdef CONFIG_BAND_STEERING
static void handle_op_class_band(struct tmp_feature_sta *f_sta, const u8 *ie, int ie_len)
{
    int op_num, i, j;
    u8 op_class;

    if (!ie) {
        return;
    }

    op_num = ie[1];
    for (i = 0; i < op_num; i++) {
        op_class = ie[i + 2];
        for (j = 0; j < ARRAY_SIZE(g_op_class); j++) {
            if (op_class == g_op_class[j].op_class) {
                switch (g_op_class[j].band) {
                case BAND_ON_24G:
                    f_sta->supported_band |= BAND_CAP_2G;
                    break;
                case BAND_ON_5G:
                    f_sta->supported_band |= BAND_CAP_5G;
                    break;
                default:
                    break;
                }
                break;
            }
        }
    }
}

static void set_band_by_freq(struct tmp_feature_sta *f_sta, u16 freq)
{
    if (freq >= 5180) {
        f_sta->supported_band |= BAND_CAP_5G;
    } else {
        f_sta->supported_band |= BAND_CAP_2G;
    }
}

static void handle_assoc_request(struct rwnx_vif *rwnx_vif,
                               struct tmp_feature_sta *f_sta,
                               struct ieee80211_mgmt *mgmt,
                               struct sk_buff *skb,
                               struct rx_vector_1 *rxvect,
                               struct hw_rxhdr *hw_rxhdr,
                               bool is_reassoc)
{
    const u8 *ie;
    u8 *variable_field;
    size_t variable_len;

    aicwf_nl_send_frame_rpt_msg(rwnx_vif, WIFI_ASSOCREQ, mgmt->sa, rxvect->rssi1);
    f_sta->sta_idx = rwnx_vif->ap.tmp_sta_idx;

    if (is_reassoc) {
        variable_field = mgmt->u.reassoc_req.variable;
    } else {
        variable_field = mgmt->u.assoc_req.variable;
    }
    variable_len = skb->len - (variable_field - skb->data);

    ie = cfg80211_find_ie(WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
                         variable_field, variable_len);

    if (ie) {
        handle_op_class_band(f_sta, ie, variable_len);
        AICWFDBG(LOGINFO, "%s usb %sassoc supported_band: %u\n", __func__,
              is_reassoc ? "re" : "", f_sta->supported_band);
    } else {
        AICWFDBG(LOGINFO, "%s usb %sassoc no find op_class\n", __func__, is_reassoc ? "re" : "");
        set_band_by_freq(f_sta, hw_rxhdr->phy_info.phy_prim20_freq);
    }
}
#endif

/**
 * rwnx_rx_mgmt - Process one 802.11 management frame
 *
 * @rwnx_hw: main driver data
 * @rwnx_vif: vif to upload the buffer to
 * @skb: skb received
 * @rxhdr: HW rx descriptor
 *
 * Forward the management frame to a given interface.
 */
static void rwnx_rx_mgmt(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
                         struct sk_buff *skb,  struct hw_rxhdr *hw_rxhdr)
{
    struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
    struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
#ifdef CONFIG_HE_FOR_OLD_KERNEL
	struct ieee80211_he_cap_elem *he;
	struct ieee80211_he_mcs_nss_supp *he_mcs;
#endif

	//printk("rwnx_rx_mgmt\n");
	if(skb->data[0]!=0x80 && skb->data[0] != 0x40)
		AICWFDBG(LOGDEBUG,"rxmgmt:%x,%x\n", skb->data[0], skb->data[1]);

#ifdef CONFIG_BAND_STEERING
	bool queued = false;
	struct tmp_feature_sta *f_sta = &rwnx_hw->feature_table[rwnx_vif->ap.tmp_sta_idx];
	if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP) {
#if 0
		if(skb->data[0] != 0x80)
			printk("rx mgmt:%02x\n", mgmt->frame_control);
#endif

		if (ieee80211_is_assoc_req(mgmt->frame_control)) {
			handle_assoc_request(rwnx_vif, f_sta, mgmt, skb, rxvect, hw_rxhdr, false);
		} else if (ieee80211_is_reassoc_req(mgmt->frame_control)) {
			handle_assoc_request(rwnx_vif, f_sta, mgmt, skb, rxvect, hw_rxhdr, true);
		}

		if (ieee80211_is_auth(mgmt->frame_control)) {
			aicwf_nl_send_frame_rpt_msg(rwnx_vif, WIFI_AUTH, mgmt->sa, rxvect->rssi1);
		}

		if (ieee80211_is_probe_req(mgmt->frame_control)) {
			if (!rwnx_vif->ap.start)
				return;
			aicwf_nl_send_frame_rpt_msg(rwnx_vif, WIFI_PROBEREQ, mgmt->sa, rxvect->rssi1);
			AICWFDBG(LOGSTEER, "usb rx p_req: %pM\n", mgmt->sa);
			for (int i = 0; i < MAX_PENDING_PROBES; i++) {
				if (!rwnx_vif->pb_pool[i].in_use) {
					rwnx_vif->pb_pool[i].in_use = true;
					memcpy(rwnx_vif->pb_pool[i].da, mgmt->sa, 6);
					queue_work(rwnx_vif->rsp_wq, &rwnx_vif->pb_pool[i].rsp_work);
					queued = true;
					break;
				}
			}
			if (!queued)
				AICWFDBG(LOGERROR, "usb probe_rsp pool full, drop %pM\n", mgmt->sa);
			return;
		}

	}
#endif

#if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
	struct aic_sta *sta = &rwnx_hw->aic_table[rwnx_vif->ap.aic_index];
	const u8* ie;
	u32 len;

    if (ieee80211_is_assoc_req(mgmt->frame_control) && rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) {
        printk("ASSOC_REQ: sta_idx %d MAC %pM\n", rwnx_vif->ap.aic_index, mgmt->sa);
        sta->sta_idx = rwnx_vif->ap.aic_index;
        len = skb->len - (mgmt->u.assoc_req.variable - skb->data);

        #ifdef CONFIG_HE_FOR_OLD_KERNEL
        ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, mgmt->u.assoc_req.variable, len);
        if (ie && ie[1] >= sizeof(*he) + 1) {
            printk("assoc_req: find he\n");
			he = (struct ieee80211_he_cap_elem *)(ie+3);
			he_mcs = (struct ieee80211_he_mcs_nss_supp *)((u8 *)he + sizeof(*he));
			memcpy(&sta->he_cap_elem,ie+3,sizeof(struct ieee80211_he_cap_elem));

			sta->he_mcs_nss_supp.rx_mcs_80 = he_mcs->rx_mcs_80;
			sta->he_mcs_nss_supp.tx_mcs_80 = he_mcs->tx_mcs_80;
            sta->he = true;
        }
        else {
            printk("assoc_req: no find he\n");
            sta->he = false;
        }
        #endif

        #ifdef CONFIG_VHT_FOR_OLD_KERNEL
        struct ieee80211_vht_cap *vht;
        ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, mgmt->u.assoc_req.variable, len);
        if (ie && ie[1] >= sizeof(*vht)) {
            printk("assoc_req: find vht\n");
			memcpy(&sta->vht_cap_info,ie+2,4);
			memcpy(&sta->supp_mcs,ie+2+4,sizeof(struct ieee80211_vht_mcs_info));
            sta->vht = true;
        } else {
            printk("assoc_req: no find vht\n");
            sta->vht = false;
        }
        #endif
    }
    if (ieee80211_is_deauth(mgmt->frame_control) && rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) {
        printk("DEAUTH: sta_idx %d MAC %pM reason:%x\n", rwnx_vif->ap.aic_index, mgmt->sa, mgmt->u.deauth.reason_code);
    }
    if (ieee80211_is_disassoc(mgmt->frame_control) && rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) {
        printk("DISASSOC: sta_idx %d MAC %pM reason:%x\n", rwnx_vif->ap.aic_index, mgmt->sa, mgmt->u.disassoc.reason_code);
    }
#endif

    /*if (ieee80211_is_mgmt(mgmt->frame_control) &&
        (skb->len <= 24 || skb->len > 768)) {
        printk("mgmt err\n");
        return;
    }*/

    if (ieee80211_is_beacon(mgmt->frame_control)) {
        if ((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT) &&
            hw_rxhdr->flags_new_peer) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
            cfg80211_notify_new_peer_candidate(rwnx_vif->ndev, mgmt->sa,
                                               mgmt->u.beacon.variable,
                                               skb->len - offsetof(struct ieee80211_mgmt,
                                                                   u.beacon.variable),
                                               GFP_ATOMIC);
#else
            /* TODO: the value of parameter sig_dbm need to be confirmed */
            cfg80211_notify_new_peer_candidate(rwnx_vif->ndev, mgmt->sa,
                                               mgmt->u.beacon.variable,
                                               skb->len - offsetof(struct ieee80211_mgmt,
                                                                   u.beacon.variable),
                                               rxvect->rssi1, GFP_ATOMIC);
#endif
        } else {

    #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
            cfg80211_report_obss_beacon(rwnx_hw->wiphy, skb->data, skb->len,
                                        hw_rxhdr->phy_info.phy_prim20_freq,
                                        rxvect->rssi1, GFP_KERNEL);
    #else
            cfg80211_report_obss_beacon(rwnx_hw->wiphy, skb->data, skb->len,
                                        hw_rxhdr->phy_info.phy_prim20_freq,
                                        rxvect->rssi1);
    #endif
        }
    } 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)) {
        #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)  // TODO: process unprot mgmt
        cfg80211_rx_unprot_mlme_mgmt(rwnx_vif->ndev, skb->data, skb->len);
        #endif
    } else if ((RWNX_VIF_TYPE(rwnx_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 = skb->len - (ft_event.ies - (uint8_t *)mgmt);
        ft_event.ric_ies = NULL;
        ft_event.ric_ies_len = 0;
        cfg80211_ft_event(rwnx_vif->ndev, &ft_event);
    } else {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
        cfg80211_rx_mgmt(&rwnx_vif->wdev, hw_rxhdr->phy_info.phy_prim20_freq,
                         rxvect->rssi1, skb->data, skb->len, 0);
#else
        cfg80211_rx_mgmt(rwnx_vif->ndev, hw_rxhdr->phy_info.phy_prim20_freq,
                         rxvect->rssi1, skb->data, skb->len, 0);
#endif
    }
}

/**
 * rwnx_rx_mgmt_any - Process one 802.11 management frame
 *
 * @rwnx_hw: main driver data
 * @skb: skb received
 * @rxhdr: HW rx descriptor
 *
 * Process the management frame and free the corresponding skb.
 * If vif is not specified in the rx descriptor, the the frame is uploaded
 * on all active vifs.
 */
static void rwnx_rx_mgmt_any(struct rwnx_hw *rwnx_hw, struct sk_buff *skb,
                             struct hw_rxhdr *hw_rxhdr)
{
    struct rwnx_vif *rwnx_vif;
    int vif_idx = hw_rxhdr->flags_vif_idx;
#ifdef CREATE_TRACE_POINTS
    trace_mgmt_rx(hw_rxhdr->phy_info.phy_prim20_freq, vif_idx,
                  hw_rxhdr->flags_sta_idx, (struct ieee80211_mgmt *)skb->data);
#endif

    if (vif_idx == RWNX_INVALID_VIF) {
        list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
            if (! rwnx_vif->up)
                continue;
            rwnx_rx_mgmt(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
        }
    } else {
        rwnx_vif = rwnx_rx_get_vif(rwnx_hw, vif_idx);
        if (rwnx_vif)
            rwnx_rx_mgmt(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
    }

    dev_kfree_skb(skb);
}

/**
 * rwnx_rx_rtap_hdrlen - Return radiotap header length
 *
 * @rxvect: Rx vector used to fill the radiotap header
 * @has_vend_rtap: boolean indicating if vendor specific data is present
 *
 * Compute the length of the radiotap header based on @rxvect and vendor
 * specific data (if any).
 */
static u8 rwnx_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;
}

/**
 * rwnx_rx_add_rtap_hdr - Add radiotap header to sk_buff
 *
 * @rwnx_hw: main driver data
 * @skb: skb received (will include the radiotap header)
 * @rxvect: Rx vector
 * @phy_info: Information regarding the phy
 * @hwvect: HW Info (NULL if vendor specific data is available)
 * @rtap_len: Length of the radiotap header
 * @vend_rtap_len: radiotap vendor length (0 if not present)
 * @vend_it_present: radiotap vendor present
 *
 * Builds a radiotap header and add it to @skb.
 */
static void rwnx_rx_add_rtap_hdr(struct rwnx_hw* rwnx_hw,
                                 struct sk_buff *skb,
                                 struct rx_vector_1 *rxvect,
                                 struct phy_channel_info_desc *phy_info,
                                 struct hw_vect *hwvect,
                                 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;

    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 (hwvect) {
        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(hwvect->tsf_hi) << 32) +
                            (u64)le32_to_cpu(hwvect->tsf_lo)), pos);
        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 ieee80211_supported_band* band =
                rwnx_hw->wiphy->bands[phy_info->phy_band];
        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
        BUG_ON((rate_idx = legrates_lut[rxvect->leg_rate].idx) == -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_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 && hwvect) {
        // 4 byte alignment
        while ((pos - (u8 *)rtap) & 3)
            pos++;
        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
        put_unaligned_le32(hwvect->ampdu_cnt, pos);
        pos += 4;
        put_unaligned_le32(0, pos);
        pos += 4;
    }

    // Check for VHT frames
    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;
    }

    // Check for HE frames
    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);
    }

    // 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;
        }
    }
}

/**
 * rwnx_rx_monitor - Build radiotap header for skb an send it to netdev
 *
 * @rwnx_hw: main driver data
 * @rwnx_vif: vif that received the buffer
 * @skb: sk_buff received
 * @hw_rxhdr_ptr: Pointer to HW RX header
 * @rtap_len: Radiotap Header length
 *
 * Add radiotap header to the receved skb and send it to netdev
 */
static int rwnx_rx_monitor(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
                           struct sk_buff *skb,  struct hw_rxhdr *hw_rxhdr_ptr,
                           u8 rtap_len)
{
    skb->dev = rwnx_vif->ndev;

    if (rwnx_vif->wdev.iftype != NL80211_IFTYPE_MONITOR) {
        netdev_err(rwnx_vif->ndev, "not a monitor vif\n");
        return -1;
    }

    /* Add RadioTap Header */
    rwnx_rx_add_rtap_hdr(rwnx_hw, skb, &hw_rxhdr_ptr->hwvect.rx_vect1,
                         &hw_rxhdr_ptr->phy_info, &hw_rxhdr_ptr->hwvect,
                         rtap_len, 0, 0);

    skb_reset_mac_header(skb);
    skb->ip_summed = CHECKSUM_UNNECESSARY;
    skb->pkt_type = PACKET_OTHERHOST;
    skb->protocol = htons(ETH_P_802_2);

#ifdef CONFIG_FILTER_TCP_ACK
    filter_rx_tcp_ack(rwnx_hw, skb->data, cpu_to_le16(skb->len));
#endif

    local_bh_disable();
    netif_receive_skb(skb);
    local_bh_enable();

    return 0;
}

#ifdef AICWF_ARP_OFFLOAD
void arpoffload_proc(struct sk_buff *skb, struct rwnx_vif *rwnx_vif)
{
    struct iphdr *iphead = (struct iphdr *)(skb->data);
    struct udphdr *udph;
    struct DHCPInfo *dhcph;

    if(skb->protocol == htons(ETH_P_IP)) { // IP
        if(iphead->protocol == IPPROTO_UDP) { // UDP
            udph = (struct udphdr *)((u8 *)iphead + (iphead->ihl << 2));
            if((udph->source == __constant_htons(SERVER_PORT))
                && (udph->dest == __constant_htons(CLIENT_PORT))) { // DHCP offset/ack
                AICWFDBG(LOGDEBUG, "dhcp: offer / ack\n");
                dhcph =	(struct DHCPInfo *)((u8 *)udph + sizeof(struct udphdr));
                if(dhcph->cookie == htonl(DHCP_MAGIC) && dhcph->op == 2 &&
                    !memcmp(dhcph->chaddr, rwnx_vif->ndev->dev_addr, 6)) { // match magic word
                    u32 length = ntohs(udph->len) - sizeof(struct udphdr) - offsetof(struct DHCPInfo, options);
                    u16 offset = 0;
                    u8 *option = dhcph->options;
                    while (option[offset]!= DHCP_OPTION_END && offset<length) {
                        if (option[offset] == DHCP_OPTION_MESSAGE_TYPE) {
                            if (option[offset+2] == DHCP_ACK) {
                                AICWFDBG(LOGDEBUG, "dhcp:   ack\n");
                                dhcped = 1;
				if (rwnx_vif->sta.paired_cipher_type == WLAN_CIPHER_SUITE_CCMP || \
					rwnx_vif->sta.paired_cipher_type == WLAN_CIPHER_SUITE_AES_CMAC || \
					((rwnx_vif->sta.group_cipher_type == 0xff) && \
					 (rwnx_vif->sta.paired_cipher_type == 0xff)))
					rwnx_send_arpoffload_en_req(rwnx_vif->rwnx_hw, rwnx_vif, dhcph->yiaddr, 1);
                                else
					rwnx_send_arpoffload_en_req(rwnx_vif->rwnx_hw, rwnx_vif, dhcph->yiaddr, 0);
                             }
                        }
                        offset += 2 + option[offset+1];
                    }
                }
            }
        }
    }
}
#endif

#ifdef AICWF_RX_REORDER
void reord_rxframe_free(spinlock_t *lock, struct list_head *q, struct list_head *list)
{
    spin_lock_bh(lock);
    list_add(list, q);
    spin_unlock_bh(lock);
}

struct recv_msdu *reord_rxframe_alloc(spinlock_t *lock, struct list_head *q)
{
    struct recv_msdu *rxframe;

    spin_lock_bh(lock);
    if (list_empty(q)) {
        spin_unlock_bh(lock);
        return NULL;
    }
    rxframe = list_entry(q->next, struct recv_msdu, rxframe_list);
    list_del_init(q->next);
    spin_unlock_bh(lock);
    return rxframe;
}

struct reord_ctrl_info *reord_init_sta(struct aicwf_rx_priv* rx_priv, const u8 *mac_addr)
{
    u8 i = 0;
    struct reord_ctrl *preorder_ctrl = NULL;
    struct reord_ctrl_info *reord_info;
#ifdef AICWF_SDIO_SUPPORT
    struct aicwf_bus *bus_if = rx_priv->sdiodev->bus_if;
#else
    struct aicwf_bus *bus_if = rx_priv->usbdev->bus_if;
#endif

    if (bus_if->state == BUS_DOWN_ST || rx_priv == NULL) {
        printk("bad stat!\n");
        return NULL;
    }

    AICWFDBG(LOGINFO, "reord_init_sta:%pM\n", mac_addr);
    reord_info = kmalloc(sizeof(struct reord_ctrl_info), GFP_ATOMIC);
    if (!reord_info)
        return NULL;

    memcpy(reord_info->mac_addr, mac_addr, ETH_ALEN);
    for (i=0; i < 8; i++) {
        preorder_ctrl = &reord_info->preorder_ctrl[i];
        preorder_ctrl->enable = true;
        preorder_ctrl->ind_sn = 0xffff;
        preorder_ctrl->wsize_b = AICWF_REORDER_WINSIZE;
        preorder_ctrl->rx_priv= rx_priv;
        INIT_LIST_HEAD(&preorder_ctrl->reord_list);
        spin_lock_init(&preorder_ctrl->reord_list_lock);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
        init_timer(&preorder_ctrl->reord_timer);
        preorder_ctrl->reord_timer.data = (ulong) preorder_ctrl;
        preorder_ctrl->reord_timer.function = reord_timeout_handler;
#else
        timer_setup(&preorder_ctrl->reord_timer, reord_timeout_handler, 0);
#endif
        INIT_WORK(&preorder_ctrl->reord_timer_work, reord_timeout_worker);
    }

    return reord_info;
}

int reord_flush_tid(struct aicwf_rx_priv *rx_priv, struct sk_buff *skb, u8 tid)
{
    struct reord_ctrl_info *reord_info;
    struct reord_ctrl *preorder_ctrl;
    struct rwnx_vif *rwnx_vif = (struct rwnx_vif *)rx_priv->rwnx_vif;
    struct ethhdr *eh = (struct ethhdr *)(skb->data);
    u8 *mac;
    unsigned long flags;
    u8 found = 0;
    struct list_head *phead, *plist;
    struct recv_msdu *prframe;
    int ret;

    if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
        mac = eh->h_dest;
    else if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO))
        mac = eh->h_source;
    else {
        printk("error mode:%d!\n", rwnx_vif->wdev.iftype);
        dev_kfree_skb(skb);
        return -1;
    }

    spin_lock_bh(&rx_priv->stas_reord_lock);
    list_for_each_entry(reord_info, &rx_priv->stas_reord_list, list) {
        if (!memcmp(mac, reord_info->mac_addr, ETH_ALEN)) {
            found = 1;
            preorder_ctrl = &reord_info->preorder_ctrl[tid];
            break;
        }
    }
    if (!found) {
        spin_unlock_bh(&rx_priv->stas_reord_lock);
        return 0;
    }
    spin_unlock_bh(&rx_priv->stas_reord_lock);

    if(preorder_ctrl->enable == false)
        return 0;
    spin_lock_irqsave(&preorder_ctrl->reord_list_lock, flags);
    phead = &preorder_ctrl->reord_list;
    while (1) {
        if (list_empty(phead)) {
            break;
        }
        plist = phead->next;
        prframe = list_entry(plist, struct recv_msdu, reord_pending_list);
        reord_single_frame_ind(rx_priv, prframe);
        list_del_init(&(prframe->reord_pending_list));
    }

	AICWFDBG(LOGINFO, "flush:tid=%d", tid);
    preorder_ctrl->enable = false;
    spin_unlock_irqrestore(&preorder_ctrl->reord_list_lock, flags);
    if (timer_pending(&preorder_ctrl->reord_timer))
        ret = rwnx_timer_delete_sync(&preorder_ctrl->reord_timer);
    cancel_work_sync(&preorder_ctrl->reord_timer_work);

    return 0;
}

void reord_deinit_sta(struct aicwf_rx_priv* rx_priv, struct reord_ctrl_info *reord_info)
{
    u8 i = 0;
    //unsigned long flags;
    struct reord_ctrl *preorder_ctrl = NULL;
    int ret;

    if (rx_priv == NULL) {
        txrx_err("bad rx_priv!\n");
        return;
    }

	AICWFDBG(LOGINFO, "%s\n", __func__);

    for (i=0; i < 8; i++) {
        struct recv_msdu *req, *next;
        preorder_ctrl = &reord_info->preorder_ctrl[i];
		if(preorder_ctrl->enable){
			preorder_ctrl->enable = false;
	        if (timer_pending(&preorder_ctrl->reord_timer)) {
	            ret = rwnx_timer_delete_sync(&preorder_ctrl->reord_timer);
	        }
	        cancel_work_sync(&preorder_ctrl->reord_timer_work);
		}

        spin_lock_bh(&preorder_ctrl->reord_list_lock);
        list_for_each_entry_safe(req, next, &preorder_ctrl->reord_list, reord_pending_list) {
            list_del_init(&req->reord_pending_list);
            if(req->pkt != NULL)
                dev_kfree_skb(req->pkt);
            req->pkt = NULL;
            reord_rxframe_free(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue, &req->rxframe_list);
        }

		AICWFDBG(LOGINFO, "reord dinit in_irq():%d in_atomic:%d in_softirq:%d\r\n", (int)in_irq()
			,(int)in_atomic(), (int)in_softirq());
        spin_unlock_bh(&preorder_ctrl->reord_list_lock);
    }

    spin_lock_bh(&rx_priv->stas_reord_lock);
    list_del(&reord_info->list);
    spin_unlock_bh(&rx_priv->stas_reord_lock);
    kfree(reord_info);
}

int reord_single_frame_ind(struct aicwf_rx_priv *rx_priv, struct recv_msdu *prframe)
{
    struct list_head *rxframes_freequeue = NULL;
    struct sk_buff *skb = NULL;
    struct rwnx_vif *rwnx_vif = (struct rwnx_vif *)rx_priv->rwnx_vif;
    struct sk_buff_head list;
    struct sk_buff *rx_skb;

    rxframes_freequeue = &rx_priv->rxframes_freequeue;
    skb = prframe->pkt;

    #ifdef CONFIG_BR_SUPPORT
    void *br_port = NULL;

    if (1) {//(check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE) == _TRUE) {
        /* Insert NAT2.5 RX here! */
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
        br_port = rwnx_vif->ndev->br_port;
#else
        rcu_read_lock();
        br_port = rcu_dereference(rwnx_vif->ndev->rx_handler_data);
        rcu_read_unlock();
#endif

        if (br_port) {
            int nat25_handle_frame(struct rwnx_vif *vif, struct sk_buff *skb);

            if (nat25_handle_frame(rwnx_vif, skb) == -1) {
                /* priv->ext_stats.rx_data_drops++; */
                /* DEBUG_ERR("RX DROP: nat25_handle_frame fail!\n"); */
                /* return FAIL; */
            }
        }
    }
#endif /* CONFIG_BR_SUPPORT */

    if (skb == NULL) {
        txrx_err("skb is NULL\n");
        return -1;
    }

    if(prframe->is_ap_reord) {
        if(prframe->ap_resend_cnt) {
            struct sk_buff *resend_skb = prframe->first_resend_skb;
            struct sk_buff *resend_next_skb;
            u8 resend_cnt = 0;
            while(resend_skb) {
                resend_cnt++;
                resend_next_skb = resend_skb->next;
                dev_kfree_skb(resend_skb);
                resend_skb = resend_next_skb;
            }
            if(resend_cnt != prframe->ap_resend_cnt)
                printk("resend cnt error: %d,%d\n", resend_cnt, prframe->ap_resend_cnt);

            if(!prframe->ap_fwd_cnt) {
                prframe->pkt = NULL;
                reord_rxframe_free(&rx_priv->freeq_lock, rxframes_freequeue, &prframe->rxframe_list);
                return 0;
            } //else
              //  printk("both has resend & fwd\n");
        }
    } else {
        if(!prframe->forward) {
            dev_kfree_skb(skb);
            prframe->pkt = NULL;
            reord_rxframe_free(&rx_priv->freeq_lock, rxframes_freequeue, &prframe->rxframe_list);
            return 0;
        }
    }

    __skb_queue_head_init(&list);
    if(!prframe->is_ap_reord)
    {
        if(prframe->is_amsdu) {
            rwnx_rxdata_process_amsdu(rwnx_vif->rwnx_hw, skb, rwnx_vif->vif_index, &list); //rxhdr not used below since skb free!
        } else {
           __skb_queue_head(&list, skb);
        }
    } else {
        struct sk_buff *fwd_skb = prframe->first_fwd_skb;
        struct sk_buff *fwd_next_skb;
        u8 fwd_cnt = 0;
        while(fwd_skb) {
            //printk("single ind:%p\n", fwd_skb);
            fwd_cnt++;
            fwd_next_skb = fwd_skb->next;
            __skb_queue_tail(&list, fwd_skb);
            fwd_skb = fwd_next_skb;
        }
        if(fwd_cnt != prframe->ap_fwd_cnt)
            printk("fwd cnt error: %d,%d\n", fwd_cnt, prframe->ap_fwd_cnt);
    }

    while (!skb_queue_empty(&list)) {
        rx_skb = __skb_dequeue(&list);

        rwnx_vif->net_stats.rx_packets++;
        rwnx_vif->net_stats.rx_bytes += rx_skb->len;
        //printk("netif sn=%d, len=%d\n", precv_frame->attrib.seq_num, skb->len);
        rx_skb->dev = rwnx_vif->ndev;
        rwnx_skb_align_8bytes(rx_skb);
        rx_skb->protocol = eth_type_trans(rx_skb, rwnx_vif->ndev);

#ifdef AICWF_ARP_OFFLOAD
        if(RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
            arpoffload_proc(rx_skb, rwnx_vif);
        }
#endif
        memset(rx_skb->cb, 0, sizeof(rx_skb->cb));

#ifdef CONFIG_FILTER_TCP_ACK
         filter_rx_tcp_ack(rwnx_vif->rwnx_hw, rx_skb->data, cpu_to_le16(rx_skb->len));
#endif

#ifdef CONFIG_RX_NETIF_RECV_SKB//AIDEN test
        local_bh_disable();
        netif_receive_skb(rx_skb);
        local_bh_enable();
#else
        if (in_interrupt()) {
            netif_rx(rx_skb);
        } else {
        /*
        * If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
        * * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
        */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
        netif_rx_ni(rx_skb);
#else
        ulong flags;
        netif_rx(rx_skb);
        local_irq_save(flags);
        RAISE_RX_SOFTIRQ();
        local_irq_restore(flags);
#endif
        }
#endif//CONFIG_RX_NETIF_RECV_SKB
    }

    prframe->pkt = NULL;
    reord_rxframe_free(&rx_priv->freeq_lock, rxframes_freequeue, &prframe->rxframe_list);

    return 0;
}

bool reord_rxframes_process(struct aicwf_rx_priv *rx_priv, struct reord_ctrl *preorder_ctrl, int bforced)
{
    struct list_head *phead, *plist;
    struct recv_msdu *prframe;
    bool bPktInBuf = false;

    if (bforced == true) {
        phead = &preorder_ctrl->reord_list;
        if (list_empty(phead)) {
            return false;
        }

        plist = phead->next;
        prframe = list_entry(plist, struct recv_msdu, reord_pending_list);
        preorder_ctrl->ind_sn = prframe->seq_num;
    }

    phead = &preorder_ctrl->reord_list;
    if (list_empty(phead)) {
        return bPktInBuf;
    }

    list_for_each_entry(prframe, phead, reord_pending_list) {
        if (!SN_LESS(preorder_ctrl->ind_sn, prframe->seq_num)) {
            if (SN_EQUAL(preorder_ctrl->ind_sn, prframe->seq_num)) {
                preorder_ctrl->ind_sn = (preorder_ctrl->ind_sn + 1) & 0xFFF;
            }
        } else {
            bPktInBuf = true;
            break;
        }
    }

    return bPktInBuf;
}

void reord_rxframes_ind(struct aicwf_rx_priv *rx_priv,
    struct reord_ctrl *preorder_ctrl)
{
    struct list_head *phead, *plist;
    struct recv_msdu *prframe;

	//spin_lock_bh(&preorder_ctrl->reord_list_lock);//AIDEN
    phead = &preorder_ctrl->reord_list;
    while (1) {
        //spin_lock_bh(&preorder_ctrl->reord_list_lock);
        if (list_empty(phead)) {
            //spin_unlock_bh(&preorder_ctrl->reord_list_lock);
            break;
        }

        plist = phead->next;
        prframe = list_entry(plist, struct recv_msdu, reord_pending_list);

        if (!SN_LESS(preorder_ctrl->ind_sn, prframe->seq_num)) {
            list_del_init(&(prframe->reord_pending_list));
            //spin_unlock_bh(&preorder_ctrl->reord_list_lock);
			reord_single_frame_ind(rx_priv, prframe);
        } else {
            //spin_unlock_bh(&preorder_ctrl->reord_list_lock);
            break;
        }
    }
	//spin_unlock_bh(&preorder_ctrl->reord_list_lock);//AIDEN
}

int reorder_timeout = REORDER_UPDATE_TIME;
module_param(reorder_timeout, int, 0660);


#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
void reord_timeout_handler (ulong data)
#else
void reord_timeout_handler (struct timer_list *t)
#endif
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
	struct reord_ctrl *preorder_ctrl = (struct reord_ctrl *)data;
#else
	struct reord_ctrl *preorder_ctrl = from_timer(preorder_ctrl, t, reord_timer);
#endif

	AICWFDBG(LOGTRACE, "%s Enter \r\n", __func__);


	if (g_rwnx_plat->usbdev->state == USB_DOWN_ST) {
        usb_err("bus is down\n");
        return;
	}
#if 0//AIDEN
    struct aicwf_rx_priv *rx_priv = preorder_ctrl->rx_priv;

    if (reord_rxframes_process(rx_priv, preorder_ctrl, true)==true) {
        mod_timer(&preorder_ctrl->reord_timer, jiffies + msecs_to_jiffies(reorder_timeout/*REORDER_UPDATE_TIME*/));
    }
#endif

    if(!work_pending(&preorder_ctrl->reord_timer_work))
        schedule_work(&preorder_ctrl->reord_timer_work);

}

void reord_timeout_worker(struct work_struct *work)
{
    struct reord_ctrl *preorder_ctrl = container_of(work, struct reord_ctrl, reord_timer_work);
    struct aicwf_rx_priv *rx_priv = preorder_ctrl->rx_priv;

	spin_lock_bh(&preorder_ctrl->reord_list_lock);
#if 1//AIDEN
	if (reord_rxframes_process(rx_priv, preorder_ctrl, true)==true) {
		mod_timer(&preorder_ctrl->reord_timer, jiffies + msecs_to_jiffies(reorder_timeout/*REORDER_UPDATE_TIME*/));
	}
#endif

    reord_rxframes_ind(rx_priv, preorder_ctrl);
	spin_unlock_bh(&preorder_ctrl->reord_list_lock);

    return ;
}

int reord_process_unit(struct recv_msdu *pframe, struct aicwf_rx_priv *rx_priv, struct sk_buff *skb, u16 seq_num, u8 tid, u8 forward, u8 is_amsdu)
{
    int ret=0;
    u8 *mac;
    struct reord_ctrl *preorder_ctrl;
    struct reord_ctrl_info *reord_info;
    struct rwnx_vif *rwnx_vif = (struct rwnx_vif *)rx_priv->rwnx_vif;
    struct ethhdr *eh = (struct ethhdr *)(skb->data);
    u8 *da = eh->h_dest;
    u8 is_mcast = ((*da) & 0x01)? 1 : 0;

    #if 0
    struct recv_msdu *pframe;
    pframe = reord_rxframe_alloc(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue);
    if (!pframe) {
        dev_kfree_skb(skb);
        return -1;
    }
    #endif

    INIT_LIST_HEAD(&pframe->reord_pending_list);
    pframe->seq_num = seq_num;
    pframe->tid = tid;
    pframe->rx_data = skb->data;
    //pframe->len = skb->len;
    pframe->pkt = skb;
    pframe->forward = forward;
    preorder_ctrl = pframe->preorder_ctrl;
    pframe->is_amsdu = is_amsdu;

    if ((ntohs(eh->h_proto) == ETH_P_PAE) || is_mcast)
        return reord_single_frame_ind(rx_priv, pframe);

    if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
        mac = eh->h_dest;
    else if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO))
        mac = eh->h_source;
    else {
        dev_kfree_skb(skb);
        return -1;
    }

    spin_lock_bh(&rx_priv->stas_reord_lock);
    list_for_each_entry(reord_info, &rx_priv->stas_reord_list, list) {
        if (!memcmp(mac, reord_info->mac_addr, ETH_ALEN)) {
            preorder_ctrl = &reord_info->preorder_ctrl[pframe->tid];
            break;
        }
    }


    if (&reord_info->list == &rx_priv->stas_reord_list) {//first time???
        reord_info = reord_init_sta(rx_priv, mac);
        //reord_info has 8 preorder_ctrl,
        //There is a one-to-one matched preorder_ctrl and tid
        if (!reord_info) {
            spin_unlock_bh(&rx_priv->stas_reord_lock);
            reord_single_frame_ind(rx_priv, pframe);
            return -1;
        }
        list_add_tail(&reord_info->list, &rx_priv->stas_reord_list);
        preorder_ctrl = &reord_info->preorder_ctrl[pframe->tid];
    } else {
        if(preorder_ctrl->enable == false) {
            preorder_ctrl->enable = true;
            preorder_ctrl->ind_sn = 0xffff;
            preorder_ctrl->wsize_b = AICWF_REORDER_WINSIZE;
            preorder_ctrl->rx_priv= rx_priv;
        }
    }
    spin_unlock_bh(&rx_priv->stas_reord_lock);

    if (preorder_ctrl->enable == false) {
        preorder_ctrl->ind_sn = pframe->seq_num;
        spin_lock_bh(&preorder_ctrl->reord_list_lock);//AIDEN
        reord_single_frame_ind(rx_priv, pframe);
        spin_unlock_bh(&preorder_ctrl->reord_list_lock);//AIDEN
        preorder_ctrl->ind_sn = (preorder_ctrl->ind_sn + 1)%4096;
        return 0;
    }

    spin_lock_bh(&preorder_ctrl->reord_list_lock);
    if (reord_need_check(preorder_ctrl, pframe->seq_num)) {
#if 1
		if(pframe->rx_data[42] == 0x80){//this is rtp package
			if(pframe->seq_num == preorder_ctrl->ind_sn){
				//printk("%s pframe->seq_num1:%d \r\n", __func__, pframe->seq_num);
	        	reord_single_frame_ind(rx_priv, pframe);//not need to reorder
			}else{
				//printk("%s free pframe->seq_num:%d \r\n", __func__, pframe->seq_num);
			    if (pframe->pkt){
			        dev_kfree_skb(pframe->pkt);
			        pframe->pkt = NULL;
			    }
			   	reord_rxframe_free(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue, &pframe->rxframe_list);
			}
		}else{
			//printk("%s pframe->seq_num2:%d \r\n", __func__, pframe->seq_num);
			reord_single_frame_ind(rx_priv, pframe);//not need to reorder
		}
#else
        reord_single_frame_ind(rx_priv, pframe);//not need to reor
#endif

        spin_unlock_bh(&preorder_ctrl->reord_list_lock);
		return 0;
    }

    if (reord_rxframe_enqueue(preorder_ctrl, pframe)) {
        spin_unlock_bh(&preorder_ctrl->reord_list_lock);
        goto fail;
    }

    if (reord_rxframes_process(rx_priv, preorder_ctrl, false) == true) {
        if (!timer_pending(&preorder_ctrl->reord_timer)) {//if this timer not countdown, mod_timer timer to start count down
            ret = mod_timer(&preorder_ctrl->reord_timer, jiffies + msecs_to_jiffies(reorder_timeout/*REORDER_UPDATE_TIME*/));
        }
    } else {
		if(timer_pending(&preorder_ctrl->reord_timer)) {
	        	ret = rwnx_timer_delete(&preorder_ctrl->reord_timer);
		}
    }

	reord_rxframes_ind(rx_priv, preorder_ctrl);

    spin_unlock_bh(&preorder_ctrl->reord_list_lock);



    return 0;

fail:
    if (pframe->pkt){
        dev_kfree_skb(pframe->pkt);
        pframe->pkt = NULL;
    }
    reord_rxframe_free(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue, &pframe->rxframe_list);
    return ret;
}

int reord_need_check(struct reord_ctrl *preorder_ctrl, u16 seq_num)
{
    u8 wsize = preorder_ctrl->wsize_b;
    u16 wend = (preorder_ctrl->ind_sn + wsize -1) & 0xFFF;//0xFFF: 12 bits for seq num

	//first time: wend = 0 + 64 - 1= 63

    if (preorder_ctrl->ind_sn == 0xFFFF) {//first time
        preorder_ctrl->ind_sn = seq_num;
    }

    if( SN_LESS(seq_num, preorder_ctrl->ind_sn)) {//first time seq_num = preorder_ctrl->ind_sn
        return -1;//no need to reord
    }

    if (SN_EQUAL(seq_num, preorder_ctrl->ind_sn)) {
        preorder_ctrl->ind_sn = (preorder_ctrl->ind_sn + 1) & 0xFFF;
    } else if (SN_LESS(wend, seq_num)) {
        if (seq_num >= (wsize-1))
            preorder_ctrl->ind_sn = seq_num-(wsize-1);
        else
            preorder_ctrl->ind_sn = 0xFFF - (wsize - (seq_num + 1)) + 1;
    }

    return 0;
}

int reord_rxframe_enqueue(struct reord_ctrl *preorder_ctrl, struct recv_msdu *prframe)
{
    struct list_head *preord_list = &preorder_ctrl->reord_list;
    struct list_head *phead, *plist;
    struct recv_msdu *pnextrframe;

//first time:not any prframe in preord_list, so phead = phead->next
    phead = preord_list;
    plist = phead->next;

    while(phead != plist) {
        pnextrframe = list_entry(plist, struct recv_msdu, reord_pending_list);
        if(SN_LESS(pnextrframe->seq_num, prframe->seq_num))	{
            plist = plist->next;
            continue;
        } else if(SN_EQUAL(pnextrframe->seq_num, prframe->seq_num)) {
            return -1;
        } else {
            break;
        }
    }

	//link prframe in plist
    list_add_tail(&(prframe->reord_pending_list), plist);

    return 0;
}
#endif /* AICWF_RX_REORDER */

void remove_sec_hdr_mgmt_frame(struct hw_rxhdr *hw_rxhdr,struct sk_buff *skb)
{
    u8 hdr_len = 24;
    u8 mgmt_header[24] = {0};

    if(!hw_rxhdr->hwvect.ga_frame){
        if(((skb->data[0] & 0x0C) == 0) && (skb->data[1] & 0x40) == 0x40){ //protect management frame
            printk("frame type %x\n",skb->data[0]);
            if(hw_rxhdr->hwvect.decr_status == RWNX_RX_HD_DECR_CCMP128){
                memcpy(mgmt_header,skb->data,hdr_len);
                skb_pull(skb,8);
                memcpy(skb->data,mgmt_header,hdr_len);
                hw_rxhdr->hwvect.len -= 8;
            } else {
                printk("unsupport decr_status:%d\n",hw_rxhdr->hwvect.decr_status);
            }
        }
    }
}

void rwnx_rxdata_process_amsdu(struct rwnx_hw *rwnx_hw, struct sk_buff *skb, u8 vif_idx,
                                        struct sk_buff_head *list)
{
	u16 len_alligned = 0;
	u16 sublen = 0;
	struct sk_buff *sub_skb = NULL;
    struct rwnx_vif *rwnx_vif;

    //if (is_amsdu) 
    {
        //skb_pull(skb, pull_len-8);
        /* |amsdu sub1 | amsdu sub2 | ... */
        len_alligned = 0;
        sublen = 0;
        sub_skb = NULL;
        while (skb->len > 16) {
            sublen = (skb->data[12]<<8)|(skb->data[13]);
            if (skb->len > (sublen+14))
                len_alligned = roundup(sublen + 14, 4);
            else if (skb->len == (sublen+14))
                len_alligned = sublen+14;
            else {
                printk("accroding to amsdu: this will not happen\n");
                break;
            }
            //printk("sublen = %d, %x, %x, %x, %x\r\n", sublen,skb->data[0], skb->data[1], skb->data[12], skb->data[13]);
#if 1
            sub_skb = __dev_alloc_skb(sublen - 6 + 12, GFP_ATOMIC);
            if(!sub_skb){
                printk("sub_skb alloc fail:%d\n", sublen);
                break;
            }
            skb_put(sub_skb, sublen - 6 + 12);
            memcpy(sub_skb->data, skb->data, MAC_ADDR_LEN);
            memcpy(&sub_skb->data[6], &skb->data[6], MAC_ADDR_LEN);
            memcpy(&sub_skb->data[12], &skb->data[14 + 6], sublen - 6);

            rwnx_vif = rwnx_rx_get_vif(rwnx_hw, vif_idx);
            if (!rwnx_vif) {
                printk("frame received but no active vif (%d), skb->len:%u\n", vif_idx, skb->len);
                //dev_kfree_skb(sub_skb);
                break;
            }
            __skb_queue_tail(list, sub_skb);

            //printk("a:%p\n", sub_skb);
            //if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, sub_skb, hw_rxhdr))
            //    dev_kfree_skb(sub_skb);
#endif
            skb_pull(skb, len_alligned);
        }
        //printk("af:%p\n", skb);
        //printk("cnt=%d\n", cnt);
        dev_kfree_skb(skb);
        //return 0;
    }
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
void defrag_timeout_cb(ulong data)
#else
void defrag_timeout_cb(struct timer_list *t)
#endif
{
	struct defrag_ctrl_info *defrag_ctrl = NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
	defrag_ctrl = (struct defrag_ctrl_info *)data;
#else
	defrag_ctrl = from_timer(defrag_ctrl, t, defrag_timer);
#endif

	printk("%s:%p\r\n", __func__, defrag_ctrl);
	spin_lock_bh(&defrag_ctrl->rwnx_hw->defrag_lock);
	list_del_init(&defrag_ctrl->list);
	dev_kfree_skb(defrag_ctrl->skb);
	kfree(defrag_ctrl);
	spin_unlock_bh(&defrag_ctrl->rwnx_hw->defrag_lock);
}

extern void rwnx_data_dump(char* tag, void* data, unsigned long len);

u8 rwnx_rxdataind_aicwf(struct rwnx_hw *rwnx_hw, void *hostid, void *rx_priv)
{
    struct hw_rxhdr *hw_rxhdr;
    struct rxdesc_tag *rxdesc = NULL;
    struct rwnx_vif *rwnx_vif;
    struct sk_buff *skb  = hostid;
    int msdu_offset = sizeof(struct hw_rxhdr) + 2;
    u16_l status = 0;
    struct aicwf_rx_priv *rx_priv_tmp;
    u8 hdr_len = 24;
    u8 ra[MAC_ADDR_LEN] = {0};
    u8 ta[MAC_ADDR_LEN] = {0};
    u8 ether_type[2] = {0};
    u8 pull_len = 0;
    u16 seq_num = 0;
    u8_l frag_num = 0;
    u8 tid = 0;
    u8 is_qos = 0;
    u8 is_frag = 0;
    struct defrag_ctrl_info *defrag_info = NULL;
    struct defrag_ctrl_info *defrag_info_tmp = NULL;
    struct sk_buff *skb_tmp = NULL;
    int ret;
    u8 sta_idx = 0;
    u16_l frame_ctrl;
    u8 is_amsdu = 0;
    bool resend = false, forward = true;
    const struct ethhdr *eth;

    REG_SW_SET_PROFILING(rwnx_hw, SW_PROF_RWNXDATAIND);
    hw_rxhdr = (struct hw_rxhdr *)skb->data;

#ifdef AICWF_RX_REORDER
    if(hw_rxhdr->is_monitor_vif) {
        status = RX_STAT_MONITOR;
        //printk("monitor rx\n");
    }
#endif

    if(hw_rxhdr->flags_upload)
        status |= RX_STAT_FORWARD;

    /* Check if we need to delete the buffer */
    if (status & RX_STAT_DELETE) {
        /* Remove the SK buffer from the rxbuf_elems table */
    #if 0
        rwnx_ipc_rxbuf_elem_pull(rwnx_hw, skb);
    #endif
        /* Free the buffer */
        dev_kfree_skb(skb);
        goto end;
    }

    /* Check if we need to forward the buffer coming from a monitor interface */
    if (status & RX_STAT_MONITOR) {
        struct sk_buff *skb_monitor = NULL;
        struct hw_rxhdr hw_rxhdr_copy;
        u8 rtap_len;
        u16 frm_len = 0;

        //Check if monitor interface exists and is open
        rwnx_vif = rwnx_rx_get_vif(rwnx_hw, rwnx_hw->monitor_vif);
        if (!rwnx_vif) {
            dev_err(rwnx_hw->dev, "Received monitor frame but there is no monitor interface open, skb->len:%u\n", skb->len);
            goto check_len_update;
        }

        rwnx_rx_vector_convert(rwnx_hw,
                               &hw_rxhdr->hwvect.rx_vect1,
                               &hw_rxhdr->hwvect.rx_vect2);
        rtap_len = rwnx_rx_rtap_hdrlen(&hw_rxhdr->hwvect.rx_vect1, false);

        if (status == RX_STAT_MONITOR) 
        {
            /* Remove the SK buffer from the rxbuf_elems table. It will also
               unmap the buffer and then sync the buffer for the cpu */
            //rwnx_ipc_rxbuf_elem_pull(rwnx_hw, skb);
            skb->data += (msdu_offset + 2); //sdio/usb word allign

            //Save frame length
            frm_len = le32_to_cpu(hw_rxhdr->hwvect.len);

            // Reserve space for frame
            skb->len = frm_len;

            //Check if there is enough space to add the radiotap header
            if (skb_headroom(skb) > rtap_len) {

                skb_monitor = skb;

                //Duplicate the HW Rx Header to override with the radiotap header
                memcpy(&hw_rxhdr_copy, hw_rxhdr, sizeof(hw_rxhdr_copy));

                hw_rxhdr = &hw_rxhdr_copy;
            } else {
                //Duplicate the skb and extend the headroom
                skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);

                //Reset original skb->data pointer
                skb->data = (void*) hw_rxhdr;
            }
        } else {
        #ifdef CONFIG_RWNX_MON_DATA
        skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
        skb_monitor->data += (msdu_offset + 2); //sdio/usb word allign

        //Save frame length
        frm_len = le32_to_cpu(hw_rxhdr->hwvect.len);
        #endif
        }

        skb_reset_tail_pointer(skb);
        skb->len = 0;
        skb_reset_tail_pointer(skb_monitor);
        skb_monitor->len = 0;
        skb_put(skb_monitor, frm_len);

        if (rwnx_rx_monitor(rwnx_hw, rwnx_vif, skb_monitor, hw_rxhdr, rtap_len))
            dev_kfree_skb(skb_monitor);

        if (status == RX_STAT_MONITOR) {
            if (skb_monitor != skb) {
                dev_kfree_skb(skb);
            }
        }
    }

check_len_update:
    /* Check if we need to update the length */
    if (status & RX_STAT_LEN_UPDATE) {
        if (rxdesc){
            hw_rxhdr->hwvect.len = rxdesc->frame_len;
        }

        if (status & RX_STAT_ETH_LEN_UPDATE) {
            /* Update Length Field inside the Ethernet Header */
            struct ethhdr *hdr = (struct ethhdr *)((u8 *)hw_rxhdr + msdu_offset);

			if (rxdesc){
            	hdr->h_proto = htons(rxdesc->frame_len - sizeof(struct ethhdr));
			}
        }

        goto end;
    }

    /* Check if it must be discarded after informing upper layer */
    if (status & RX_STAT_SPURIOUS) {
        struct ieee80211_hdr *hdr;

        hdr = (struct ieee80211_hdr *)(skb->data + msdu_offset);
        rwnx_vif = rwnx_rx_get_vif(rwnx_hw, hw_rxhdr->flags_vif_idx);
        if (rwnx_vif) {
            rwnx_rx_spurious_frame(rwnx_vif->ndev, hdr->addr2, GFP_ATOMIC);
        }
        goto end;
    }

    /* Check if we need to forward the buffer */
    if (status & RX_STAT_FORWARD) {
        rwnx_rx_vector_convert(rwnx_hw,
                               &hw_rxhdr->hwvect.rx_vect1,
                               &hw_rxhdr->hwvect.rx_vect2);
        skb_pull(skb, msdu_offset + 2); //+2 since sdio allign 58->60
#define MAC_FCTRL_MOREFRAG 0x0400
		frame_ctrl = (skb->data[1] << 8) | skb->data[0];
		seq_num = ((skb->data[22] & 0xf0) >> 4) | (skb->data[23] << 4);
		frag_num = (skb->data[22] & 0x0f);
        is_amsdu = 0;

		if ((skb->data[0] & 0x0f) == 0x08) {
			if ((skb->data[0] & 0x80) == 0x80) {//qos data
				hdr_len = 26;
				tid = skb->data[24] & 0x0F;
				is_qos = 1;
				if (skb->data[24] & 0x80)
					is_amsdu = 1;
			}

			if(skb->data[1] & 0x80)// htc
				hdr_len += 4;

            if((skb->data[1] & 0x3) == 0x1)  {// to ds
                memcpy(ra, &skb->data[16], MAC_ADDR_LEN);//destination addr
                memcpy(ta, &skb->data[10], MAC_ADDR_LEN);//source addr
            } else if((skb->data[1] & 0x3) == 0x2) { //from ds
                memcpy(ta, &skb->data[16], MAC_ADDR_LEN);//destination addr
                memcpy(ra, &skb->data[4], MAC_ADDR_LEN);//BSSID
            }

            pull_len += (hdr_len + 8);

			switch (hw_rxhdr->hwvect.decr_status) {
			case RWNX_RX_HD_DECR_CCMP128:
				pull_len += 8;//ccmp_header
				//skb_pull(&skb->data[skb->len-8], 8); //ccmp_mic_len
				memcpy(ether_type, &skb->data[hdr_len + 6 + 8], 2);
				break;
			case RWNX_RX_HD_DECR_TKIP:
				pull_len += 8;//tkip_header
				memcpy(ether_type, &skb->data[hdr_len + 6 + 8], 2);
				break;
			case RWNX_RX_HD_DECR_WEP:
				pull_len += 4;//wep_header
				memcpy(ether_type, &skb->data[hdr_len + 6 + 4], 2);
				break;
			case RWNX_RX_HD_DECR_WAPI:
                pull_len += 18;//wapi_header
                memcpy(ether_type, &skb->data[hdr_len + 6 + 18], 2);
                break;

			default:
				memcpy(ether_type, &skb->data[hdr_len + 6], 2);
				break;
			}
            if(is_amsdu)
                hw_rxhdr->flags_is_amsdu = 1;
            else
                hw_rxhdr->flags_is_amsdu = 0;

            if((ether_type[0] == 0x8e && ether_type[1] == 0x88) || (ether_type[0] == 0x88 && ether_type[1] == 0x8e))
                printk("rx eapol\n");
			if (is_amsdu) {
                skb_pull(skb, pull_len-8);
			}

			if (hw_rxhdr->flags_dst_idx != RWNX_INVALID_STA)
				sta_idx = hw_rxhdr->flags_dst_idx;

			if (!hw_rxhdr->flags_need_reord && ((frame_ctrl & MAC_FCTRL_MOREFRAG) || frag_num)) {
				printk("rxfrag:%d,%d,%d,sn=%d,%d\r\n", (frame_ctrl & MAC_FCTRL_MOREFRAG), frag_num, skb->len, seq_num,pull_len);
				if (frame_ctrl & MAC_FCTRL_MOREFRAG) {
					spin_lock_bh(&rwnx_hw->defrag_lock);
					if (!list_empty(&rwnx_hw->defrag_list)) {
						list_for_each_entry(defrag_info_tmp, &rwnx_hw->defrag_list, list) {
							if ((defrag_info_tmp->sn == seq_num) && (defrag_info_tmp->tid == tid) && \
									defrag_info_tmp->sta_idx == sta_idx) {
								defrag_info = defrag_info_tmp;
								break;
							}
						}
					}

					//printk("rx frag: sn=%d, fn=%d, skb->len=%d\r\n", seq_num, frag_num, skb->len);
					if (defrag_info) {
						is_frag = 1;
						if (defrag_info->next_fn != frag_num) {
							printk("discard:%d:%d\n", defrag_info->next_fn, frag_num);
							dev_kfree_skb(skb);
							spin_unlock_bh(&rwnx_hw->defrag_lock);
							return 0;
						}

						skb_put(defrag_info->skb, skb->len-(pull_len-8));
						memcpy(&defrag_info->skb->data[defrag_info->frm_len], \
								&skb->data[pull_len-8], skb->len - (pull_len-8));
						//printk("middle:%d,%d\n", skb->len-(pull_len-8), skb->len);
						defrag_info->frm_len += (skb->len - (pull_len - 8));
						defrag_info->next_fn++;
						dev_kfree_skb(skb);
						spin_unlock_bh(&rwnx_hw->defrag_lock);
						return 0;
					} else {
						defrag_info = kzalloc(sizeof(struct defrag_ctrl_info), GFP_KERNEL);
						if (defrag_info == NULL) {
							printk("no defrag_ctrl_info\r\n");
							dev_kfree_skb(skb);
							spin_unlock_bh(&rwnx_hw->defrag_lock);
							return 0;
						}
						defrag_info->skb = __dev_alloc_skb(2000, GFP_KERNEL);
						if (defrag_info->skb == NULL) {
							printk("no fragment skb\r\n");
							dev_kfree_skb(skb);
							kfree(defrag_info);
							spin_unlock_bh(&rwnx_hw->defrag_lock);
							return 0;
						}
						is_frag = 1;
						skb_pull(skb, pull_len);
						skb_push(skb, 14);
						memcpy(skb->data, ra, MAC_ADDR_LEN);
						memcpy(&skb->data[6], ta, MAC_ADDR_LEN);
						memcpy(&skb->data[12], ether_type, 2);
						defrag_info->sn = seq_num;
						defrag_info->next_fn = 1;
						defrag_info->tid = tid;
						defrag_info->sta_idx = sta_idx;

						skb_put(defrag_info->skb, skb->len);
						memcpy(defrag_info->skb->data, skb->data, skb->len);
						defrag_info->frm_len = skb->len;
						defrag_info->rwnx_hw = rwnx_hw;
						//printk("first:%p,%p,%p,%p,%p, %d,%d\r\n", defrag_info, defrag_info->skb, defrag_info->skb->head, defrag_info->skb->tail, defrag_info->skb->end, defrag_info->frm_len, skb->len);
						list_add_tail(&defrag_info->list, &rwnx_hw->defrag_list);
						spin_unlock_bh(&rwnx_hw->defrag_lock);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
						init_timer(&defrag_info->defrag_timer);
						defrag_info->defrag_timer.data = (unsigned long)defrag_info;
						defrag_info->defrag_timer.function = defrag_timeout_cb;
#else
						timer_setup(&defrag_info->defrag_timer, defrag_timeout_cb, 0);
#endif
						ret = mod_timer(&defrag_info->defrag_timer, jiffies + msecs_to_jiffies(DEFRAG_MAX_WAIT));
						dev_kfree_skb(skb);
						return 0;
					}
				} else {
					//check whether the last fragment
					if (!list_empty(&rwnx_hw->defrag_list)) {
						spin_lock_bh(&rwnx_hw->defrag_lock);
						list_for_each_entry(defrag_info_tmp, &rwnx_hw->defrag_list, list) {
							if (((defrag_info_tmp->sn == seq_num) && (defrag_info_tmp->tid == tid) && \
										defrag_info_tmp->sta_idx == sta_idx)) {
								defrag_info = defrag_info_tmp;
								break;
							}
						}

						if (!defrag_info) 
							spin_unlock_bh(&rwnx_hw->defrag_lock);
						else {
							if (defrag_info->next_fn != frag_num) {
								printk("discard:%d:%d\n", defrag_info->next_fn, frag_num);
								dev_kfree_skb(skb);
								spin_unlock_bh(&rwnx_hw->defrag_lock);
								return 0;
							}

							skb_put(defrag_info->skb, skb->len - (pull_len-8));
							memcpy(&defrag_info->skb->data[defrag_info->frm_len], \
										&skb->data[pull_len-8], skb->len - (pull_len-8));
							defrag_info->frm_len += (skb->len - (pull_len-8));
							is_frag = 1;
							//printk("last: sn=%d, fn=%d, %d, %d\r\n", seq_num, frag_num, defrag_info->frm_len, skb->len);

							rwnx_vif = rwnx_rx_get_vif(rwnx_hw, hw_rxhdr->flags_vif_idx);
							if (!rwnx_vif) {
								printk("Frame received but no active vif (%d)", hw_rxhdr->flags_vif_idx);
								dev_kfree_skb(skb);
								spin_unlock_bh(&rwnx_hw->defrag_lock);
								return 0;
							}
							dev_kfree_skb(skb);

							skb_tmp = defrag_info->skb;
							list_del_init(&defrag_info->list);
							if (timer_pending(&defrag_info->defrag_timer)) {
								ret = rwnx_timer_delete(&defrag_info->defrag_timer);
							}
							kfree(defrag_info);
							spin_unlock_bh(&rwnx_hw->defrag_lock);

							if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb_tmp, hw_rxhdr))
								dev_kfree_skb(skb_tmp);

							return 0;
						}
					}
				}
			}

			if (!is_frag && !is_amsdu) {
				skb_pull(skb, pull_len);
				skb_push(skb, 14);
				memcpy(skb->data, ra, MAC_ADDR_LEN);
				memcpy(&skb->data[6], ta, MAC_ADDR_LEN);
				memcpy(&skb->data[12], ether_type, 2);
			}
		}

        if (hw_rxhdr->flags_is_80211_mpdu) {
            remove_sec_hdr_mgmt_frame(hw_rxhdr,skb);
            rwnx_rx_mgmt_any(rwnx_hw, skb, hw_rxhdr);
        } else {
            rwnx_vif = rwnx_rx_get_vif(rwnx_hw, hw_rxhdr->flags_vif_idx);

            if (!rwnx_vif) {
                dev_err(rwnx_hw->dev, "Frame received but no active vif (%d), skb->len:%u\n",
                        hw_rxhdr->flags_vif_idx, skb->len);
                dev_kfree_skb(skb);
                goto end;
            }

            if (hw_rxhdr->flags_sta_idx != RWNX_INVALID_STA) {
                struct rwnx_sta *sta;

                sta = &rwnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
                rwnx_rx_statistic(rwnx_hw, hw_rxhdr, sta);

                if (sta->vlan_idx != rwnx_vif->vif_index) {
                    rwnx_vif = rwnx_hw->vif_table[sta->vlan_idx];
                    if (!rwnx_vif) {
                        dev_kfree_skb(skb);
                        goto end;
                    }
                }

                if (hw_rxhdr->flags_is_4addr && !rwnx_vif->use_4addr) {
                    rwnx_rx_unexpected_4addr_frame(rwnx_vif->ndev,
                                                   sta->mac_addr, GFP_ATOMIC);
                }
            }

            skb->priority = 256 + tid;//hw_rxhdr->flags_user_prio;

#ifdef AICWF_RX_REORDER
            rx_priv_tmp = rx_priv;
            rx_priv_tmp->rwnx_vif = (void *)rwnx_vif;

    	    if( (rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ){
                if(is_qos && hw_rxhdr->flags_need_reord){
    				struct recv_msdu *pframe;
    				pframe = reord_rxframe_alloc(&rx_priv_tmp->freeq_lock, &rx_priv_tmp->rxframes_freequeue);
    				if (!pframe) {
                        printk("no pframe\n");
    					dev_kfree_skb(skb);
    					return -1;
    				}
    				pframe->is_ap_reord = 0;
    				pframe->ap_fwd_cnt = 0;
    				pframe->ap_resend_cnt = 0;
    				reord_process_unit(pframe, (struct aicwf_rx_priv *)rx_priv, skb, seq_num, tid, 1, hw_rxhdr->flags_is_amsdu);
                } else if (is_qos  && !hw_rxhdr->flags_need_reord) {
                    reord_flush_tid((struct aicwf_rx_priv *)rx_priv, skb, tid);
                    if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr) && !hw_rxhdr->flags_is_amsdu)
                        dev_kfree_skb(skb);
                } else {
                    if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr) && !hw_rxhdr->flags_is_amsdu)
                        dev_kfree_skb(skb);
                }
	        } else if( (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ) {
#ifdef CONFIG_DYNAMIC_PERPWR
				struct rwnx_sta *sta;
#endif
				struct sk_buff_head list;
				struct sk_buff *rx_skb;

#ifdef CONFIG_DYNAMIC_PERPWR
				sta = &rwnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
				rssi_update_txpwrloss(sta, hw_rxhdr->hwvect.rx_vect1.rssi1);
#endif
#ifdef CONFIG_BAND_STEERING
				(&rwnx_hw->sta_table[hw_rxhdr->flags_sta_idx])->rssi = hw_rxhdr->hwvect.rx_vect1.rssi1;
#endif
				u8 flags_dst_idx = hw_rxhdr->flags_dst_idx;
				u8 flags_need_reord = hw_rxhdr->flags_need_reord;
				u8 flags_vif_idx = hw_rxhdr->flags_vif_idx;
                u8 flags_is_amsdu = hw_rxhdr->flags_is_amsdu;
				//u8 reord_cnt = 0;
				struct recv_msdu *pframe = NULL;

				__skb_queue_head_init(&list);

                //printk("dst_idx=%d, len=%d\n", flags_dst_idx, skb->len);
				if (flags_is_amsdu) {
					rwnx_rxdata_process_amsdu(rwnx_hw, skb, flags_vif_idx, &list); //rxhdr not used below since skb free!
				} else {
					rwnx_hw->stats.amsdus_rx[0]++;
					__skb_queue_head(&list, skb);
				}

				while (!skb_queue_empty(&list)) {
					rx_skb = __skb_dequeue(&list);

					skb_reset_mac_header(rx_skb);
					eth = eth_hdr(rx_skb);
					//printk("da:%pM, %x,%x, len=%d\n", eth->h_dest, skb->data[12], skb->data[13], skb->len);

                    resend = false;
                    forward = true;
					if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
					/* broadcast pkt need to be forwared to upper layer and resent
					   on wireless interface */
						resend = true;
					} else {
						/* unicast pkt for STA inside the BSS, no need to forward to upper
						   layer simply resend on wireless interface */
						if ((flags_dst_idx != RWNX_INVALID_STA) && !flags_is_amsdu) {
							struct rwnx_sta *sta = &rwnx_hw->sta_table[flags_dst_idx];
							if (sta->valid && (sta->vlan_idx == rwnx_vif->vif_index)) {
								resend = true;
								forward = false;
							}
						} else {
                            struct rwnx_sta *cur, *tmp;
                            bool found = false;
                            spin_lock_bh(&rwnx_hw->cb_lock);
                            list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
                                if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
                                    found = true;
                                    break;
                                }
                            }
                            spin_unlock_bh(&rwnx_hw->cb_lock);
                            if(found) {
                                //printk("amsdu found da\n");
                                resend = true;
                                forward = false;
                            }
                        }
					}

                if(resend){
					rwnx_rx_data_skb_resend(rwnx_hw, rwnx_vif, rx_skb);
                }

                if(forward) {
					if (is_qos && flags_need_reord) {
						if(pframe == NULL) {
							pframe = reord_rxframe_alloc(&rx_priv_tmp->freeq_lock, &rx_priv_tmp->rxframes_freequeue);
							if (!pframe) {
								dev_kfree_skb(rx_skb);
								return -1;
							}
                            rx_skb->next = NULL;
							pframe->ap_fwd_cnt = 1;
							pframe->ap_resend_cnt = 0;
							pframe->first_fwd_skb = rx_skb;
						    pframe->last_fwd_skb = rx_skb;
 							pframe->first_resend_skb = NULL;
 							pframe->last_resend_skb = NULL;
						} else {
							rx_skb->next = NULL;
							pframe->ap_fwd_cnt++;
							if(pframe->first_fwd_skb == NULL) {
								pframe->first_fwd_skb = rx_skb;
								pframe->last_fwd_skb = rx_skb;
							} else {
								pframe->last_fwd_skb->next = rx_skb;
								pframe->last_fwd_skb = rx_skb;
							}
						}
					} else if (is_qos  && !flags_need_reord) {
                        reord_flush_tid((struct aicwf_rx_priv *)rx_priv, rx_skb, tid);
                        rwnx_rx_data_skb_forward(rwnx_hw, rwnx_vif, rx_skb);
                    } else
                        rwnx_rx_data_skb_forward(rwnx_hw, rwnx_vif, rx_skb);
                } else if(resend) {
					if (is_qos && flags_need_reord) {
							if(pframe == NULL) {
								pframe = reord_rxframe_alloc(&rx_priv_tmp->freeq_lock, &rx_priv_tmp->rxframes_freequeue);
								if (!pframe) {
									dev_kfree_skb(rx_skb);
									return -1;
								}
                                rx_skb->next = NULL;
								pframe->ap_fwd_cnt = 0;
								pframe->ap_resend_cnt = 1;
								pframe->first_fwd_skb = NULL;
								pframe->last_fwd_skb = NULL;
								pframe->first_resend_skb = rx_skb;
								pframe->last_resend_skb = rx_skb;
							} else {
								rx_skb->next = NULL;
								pframe->ap_resend_cnt++;
								if(pframe->first_resend_skb == NULL) {
									pframe->first_resend_skb = rx_skb;
									pframe->last_resend_skb = rx_skb;
								} else {
									pframe->last_resend_skb->next = rx_skb;
									pframe->last_resend_skb = rx_skb;
								}
							}
						}
					else if (is_qos  && !flags_need_reord) {
							if(!reord_flush_tid((struct aicwf_rx_priv *)rx_priv, rx_skb, tid))
								dev_kfree_skb(rx_skb);
						} else {
							dev_kfree_skb(rx_skb);
						}
				} else
                    dev_kfree_skb(rx_skb);
				}

				if(pframe != NULL) {
					pframe->is_ap_reord = 1;
                    //printk("reord:%d,%d\n", pframe->ap_fwd_cnt, pframe->ap_resend_cnt);
					reord_process_unit(pframe, (struct aicwf_rx_priv *)rx_priv, rx_skb, seq_num, tid, 0, 0);
				}
			}
#else
            if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
                    dev_kfree_skb(skb);
#endif
        }
    }

end:
    REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_RWNXDATAIND);
    return 0;
}

