summaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorLudovic Pouzenc <ludovic@pouzenc.fr>2018-09-14 23:55:51 +0200
committerLudovic Pouzenc <ludovic@pouzenc.fr>2018-09-14 23:55:51 +0200
commitec7f575de8a5e23b2e53dd4099d54e719e0319a6 (patch)
tree0810f2af1827b88334f366acd85a2c0c878d59ad /target
parentb1de0730aebffb68c8fc6283860ad6cb5c2b3e30 (diff)
downloadmtk-20170518-ec7f575de8a5e23b2e53dd4099d54e719e0319a6.zip
mtk-20170518-ec7f575de8a5e23b2e53dd4099d54e719e0319a6.tar.gz
mtk-20170518-ec7f575de8a5e23b2e53dd4099d54e719e0319a6.tar.bz2
target/linux/mediatek: add dirty Mediatek module wifi_fwd IPv4 port 53 (dns) and 67 (dhcp), IPv6 port 53
Diffstat (limited to 'target')
-rw-r--r--target/linux/mediatek/patches/1018-wifi-forward.patch5715
1 files changed, 5715 insertions, 0 deletions
diff --git a/target/linux/mediatek/patches/1018-wifi-forward.patch b/target/linux/mediatek/patches/1018-wifi-forward.patch
new file mode 100644
index 0000000..e40773b
--- /dev/null
+++ b/target/linux/mediatek/patches/1018-wifi-forward.patch
@@ -0,0 +1,5715 @@
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd/Kconfig
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd/Kconfig
+@@ -0,0 +1 @@
++
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd/Makefile
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_WIFI_PKT_FWD) += wifi_fwd.o
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd/wifi_fwd.c
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd/wifi_fwd.c
+@@ -0,0 +1,2726 @@
++/****************************************************************************
++ * Mediatek Inc.
++ * 5F., No.5, Taiyuan 1st St., Zhubei City,
++ * Hsinchu County 302, Taiwan, R.O.C.
++ * (c) Copyright 2014, Mediatek, Inc.
++ *
++ * All rights reserved. Ralink's source code is an unpublished work and the
++ * use of a copyright notice does not imply otherwise. This source code
++ * contains confidential trade secret material of Ralink Tech. Any attemp
++ * or participation in deciphering, decoding, reverse engineering or in any
++ * way altering the source code is stricitly prohibited, unless the prior
++ * written consent of Ralink Technology, Inc. is obtained.
++ ****************************************************************************
++
++ Module Name:
++ wifi_fwd.c
++
++ Abstract:
++
++ Revision History:
++ Who When What
++ -------- ---------- ----------------------------------------------
++ Annie Lu 2014-06-30 Initial version
++ */
++
++#include "wifi_fwd.h"
++
++struct net_device *ap_2g = NULL, *apcli_2g = NULL, *ap_5g = NULL, *apcli_5g = NULL, *br_lan = NULL;
++struct net_device *ap1_2g = NULL, *apcli1_2g = NULL, *ap1_5g = NULL, *apcli1_5g = NULL;
++struct FwdPair *WiFiFwdBase = NULL;
++struct DevInfo *FwdDevice = NULL;
++struct PacketSource *pkt_src = NULL;
++struct TxSourceEntry *tx_src_tbl = NULL;
++unsigned int tx_src_count = 0; /* count the value of tx_src_tbl */
++
++/* currently supports up to 3 */
++void * adapter_tmp_1 = NULL;
++void * adapter_tmp_2 = NULL;
++void * adapter_tmp_3 = NULL;
++
++unsigned char wf_debug_level;
++static unsigned long wifi_fwd_op_flag;
++static unsigned char rep_net_dev;
++
++static unsigned char next_2g_band;
++static unsigned char fwd_counter;
++static signed char main_5g_h_link_cnt;
++static signed char main_5g_link_cnt;
++static signed char main_2g_link_cnt;
++
++static unsigned int eth_rep5g_h_wrg_uni_cnt;
++static unsigned int eth_rep5g_h_wrg_bct_cnt;
++static unsigned int eth_rep5g_wrg_uni_cnt;
++static unsigned int eth_rep5g_wrg_bct_cnt;
++static unsigned int eth_rep2g_wrg_uni_cnt;
++static unsigned int eth_rep2g_wrg_bct_cnt;
++
++static unsigned int band_cb_offset;
++static unsigned int recv_from_cb_offset;
++
++REPEATER_ADAPTER_DATA_TABLE global_map_tbl_1, global_map_tbl_2, global_map_tbl_3;
++
++struct APCLI_BRIDGE_LEARNING_MAPPING_MAP global_map_tbl;
++spinlock_t global_map_tbl_lock;
++unsigned long global_map_tbl_irq_flags;
++spinlock_t global_band_tbl_lock;
++unsigned long global_band_tbl_irq_flags;
++spinlock_t priority_tbl_lock;
++unsigned long priority_tbl_irq_flags;
++
++
++#define ARP_ETH_TYPE 0x0806
++#define LLTD_ETH_TYPE 0x88D9
++
++#ifndef TCP
++#define TCP 0x06
++#endif /* !TCP */
++
++#ifndef UDP
++#define UDP 0x11
++#endif /* !UDP */
++
++#ifndef ETH_P_IPV6
++#define ETH_P_IPV6 0x86DD
++#endif /* !ETH_P_IPV6 */
++
++#ifndef ETH_P_IP
++#define ETH_P_IP 0x0800 /* Internet Protocol packet */
++#endif /* ETH_P_IP */
++
++#ifndef LENGTH_802_3
++#define LENGTH_802_3 14
++#endif /* !LENGTH_802_3 */
++
++#define IPV6_NEXT_HEADER_UDP 0x11
++#define IPV6_HDR_LEN 40
++
++static const REPEATER_ADAPTER_DATA_TABLE *rep_table[] =
++{
++ &global_map_tbl_1,
++ &global_map_tbl_2,
++ &global_map_tbl_3,
++};
++
++static const void **adapter_table[] =
++{
++ &adapter_tmp_1,
++ &adapter_tmp_2,
++ &adapter_tmp_3,
++};
++
++unsigned char num_rep_tbl = (sizeof(rep_table) / sizeof(REPEATER_ADAPTER_DATA_TABLE *));
++unsigned char num_adapter_tbl = (sizeof(adapter_table) / sizeof(void **));
++
++struct net_device *ra_dev_get_by_name(const char *name)
++{
++ struct net_device *dev;
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
++ dev = dev_get_by_name(&init_net, name);
++#else
++ dev = dev_get_by_name(name);
++#endif
++
++ if (dev)
++ dev_put(dev);
++
++ return dev;
++}
++
++static unsigned char is_fastlane_mode(struct net_device *dev)
++{
++ int link_cnt = 0;
++
++ if (main_2g_link_cnt != 0)
++ link_cnt++;
++ if (main_5g_link_cnt != 0)
++ link_cnt++;
++ if (main_5g_h_link_cnt != 0)
++ link_cnt++;
++
++ if (link_cnt == 1)
++ return 1;
++ else
++ return 0;
++}
++
++static unsigned char is_concurrent_mode(struct net_device *dev)
++{
++ int link_cnt = 0;
++
++ if (main_2g_link_cnt != 0)
++ link_cnt++;
++ if (main_5g_link_cnt != 0)
++ link_cnt++;
++ if (main_5g_h_link_cnt != 0)
++ link_cnt++;
++
++ if (link_cnt > 1)
++ return 1;
++ else
++ return 0;
++}
++
++static unsigned char is_mbss_mode(unsigned int band_from)
++{
++ if (((main_2g_link_cnt >= 2) && IS_PACKET_FROM_2G(band_from))
++ || ((main_5g_link_cnt >= 2) && IS_PACKET_FROM_5G(band_from))
++ || ((main_5g_h_link_cnt >= 2) && IS_PACKET_FROM_5G_H(band_from)))
++ return 1;
++ else
++ return 0;
++}
++
++/*
++ return value:
++ >=0: array index of FwdDevice
++ -1: search failed
++*/
++static int wifi_fwd_find_device(struct net_device *dev)
++{
++ int idx = 0;
++
++ for (idx=0; idx<WIFI_FWD_DEVICE_SIZE; idx++)
++ {
++ if ((FwdDevice != NULL) &&
++ (FwdDevice[idx].valid == TRUE) &&
++ (FwdDevice[idx].dev== dev))
++ return idx;
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] no entry found\n", __FUNCTION__));
++ return -1;
++}
++
++/*
++ return value:
++ >=0: array index of FwdDevice
++ -1: search failed
++*/
++static int wifi_fwd_find_device_by_info(BAND_INDEX band, unsigned int type)
++{
++ int idx = 0;
++
++ for (idx=0; idx<WIFI_FWD_DEVICE_SIZE; idx++)
++ {
++ if ((FwdDevice != NULL) &&
++ (FwdDevice[idx].valid == TRUE) &&
++ (FwdDevice[idx].band == band) &&
++ (FwdDevice[idx].type == type))
++ return idx;
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] no entry found\n", __FUNCTION__));
++ return -1;
++}
++
++static void cal_link_cnt_by_idx(unsigned char index, unsigned char policy)
++{
++ struct DevInfo *dev_info = NULL;
++
++ if (index > WIFI_FWD_DEVICE_SIZE)
++ return;
++
++ dev_info = FwdDevice + index;
++
++ if (dev_info == NULL)
++ return;
++
++ if (IS_APCLI_DEV(dev_info->type) && IS_VALID(dev_info->valid))
++ {
++ switch(dev_info->band)
++ {
++ case band_2g:
++ if (policy > 0)
++ main_2g_link_cnt++;
++ else
++ main_2g_link_cnt--;
++
++ if (main_2g_link_cnt < 0)
++ main_2g_link_cnt = 0;
++
++ break;
++
++ case band_5g:
++ if (policy > 0)
++ main_5g_link_cnt++;
++ else
++ main_5g_link_cnt--;
++
++ if (main_5g_link_cnt < 0)
++ main_5g_link_cnt = 0;
++
++ break;
++
++ case band_5g_h:
++ if (policy > 0)
++ main_5g_h_link_cnt++;
++ else
++ main_5g_h_link_cnt--;
++
++ if (main_5g_h_link_cnt < 0)
++ main_5g_h_link_cnt = 0;
++
++ break;
++
++ default:
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s][%d] unknown band(%x)\n",
++ __FUNCTION__, __LINE__, dev_info->band));
++ break;
++ }
++ }
++
++ if (policy > 0)
++ fwd_counter++;
++ else
++ fwd_counter--;
++}
++
++static void cal_link_count_by_net_dev(struct net_device *dev, unsigned char policy)
++{
++ int link_idx = 0;
++
++ if (dev == NULL)
++ return;
++
++ link_idx = wifi_fwd_find_device(dev);
++
++ if (link_idx >=0)
++ cal_link_cnt_by_idx(link_idx, policy);
++}
++
++static void dump_bridge_by_name(void)
++{
++ br_lan = ra_dev_get_by_name("br-lan");
++}
++
++static void wifi_fwd_reset_link_count(void)
++{
++ fwd_counter = 0;
++ main_5g_h_link_cnt = 0;
++ main_5g_link_cnt = 0;
++ main_2g_link_cnt = 0;
++}
++
++static bool wifi_fwd_needed(void)
++{
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACTIVE)
++ || !WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED)
++ || (fwd_counter == 0))
++ return FALSE;
++ else
++ return TRUE;
++}
++
++static void wifi_fwd_show_entry(void)
++{
++ int idx = 0;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("\n[%s]fwd_link=%d,[5g_h main]=%d [5g main]=%d, [2g main]=%d\n",
++ __FUNCTION__, fwd_counter, main_5g_h_link_cnt, main_5g_link_cnt, main_2g_link_cnt));
++
++ for (idx=0; idx<WIFI_FWD_TBL_SIZE; idx++)
++ {
++ if (WiFiFwdBase[idx].valid)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]index=%d, valid=%d, src_dev=0x%08x, dest_dev=0x%08x\n",
++ __FUNCTION__, idx, WiFiFwdBase[idx].valid, (int)WiFiFwdBase[idx].src, (int)WiFiFwdBase[idx].dest));
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("---------------------------------------------------------------------------------------------\n"));
++
++ for (idx=0; idx<WIFI_FWD_DEVICE_SIZE; idx++)
++ {
++ if (FwdDevice[idx].valid)
++ {
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]index=%d, valid=%d, ",
++ __FUNCTION__, idx, FwdDevice[idx].valid));
++
++ if (FwdDevice[idx].type == INT_MAIN)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("type=MAIN, "));
++ else if (FwdDevice[idx].type == INT_MBSSID)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("type=MBSSID, "));
++ else if (FwdDevice[idx].type == INT_APCLI)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("type=APCLI, "));
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("mbss=0x%02x, ", FwdDevice[idx].mbss_idx));
++
++ if (FwdDevice[idx].band == band_2g)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("band=2G, src_dev=0x%08x, ", (int)FwdDevice[idx].dev));
++ else if (FwdDevice[idx].band == band_5g)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("band=5G, src_dev=0x%08x, ", (int)FwdDevice[idx].dev));
++ else if (FwdDevice[idx].band == band_5g_h)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("band=5G-h, src_dev=0x%08x, ", (int)FwdDevice[idx].dev));
++
++ if (FwdDevice[idx].partner != NULL)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("dest_dev=0x%08x\n", (int)FwdDevice[idx].partner->dev));
++ else
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("\n"));
++ }
++ else
++ continue;
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]eth_rep5g_h_wrg_uni_cnt=%u, eth_rep5g_wrg_uni_cnt=%u, eth_rep2g_wrg_uni_cnt=%u\n",
++ __FUNCTION__, eth_rep5g_h_wrg_uni_cnt, eth_rep5g_wrg_uni_cnt, eth_rep2g_wrg_uni_cnt));
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]eth_rep5g_h_wrg_bct_cnt=%u, eth_rep5g_wrg_bct_cnt=%u, eth_rep2g_wrg_bct_cnt=%u\n",
++ __FUNCTION__, eth_rep5g_h_wrg_bct_cnt, eth_rep5g_wrg_bct_cnt, eth_rep2g_wrg_bct_cnt));
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]rep=%d\n", __FUNCTION__, rep_net_dev));
++}
++
++static void wifi_fwd_delete_entry_by_idx(unsigned char idx)
++{
++ int i = 0;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("\n[%s]--------------------------------------------\n", __FUNCTION__));
++
++ if (idx < WIFI_FWD_TBL_SIZE)
++ {
++ if (WiFiFwdBase[idx].valid)
++ {
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]index=%d, valid=%d, src dev=0x%08X, dest dev=0x%08X\n",
++ __FUNCTION__, idx, WiFiFwdBase[idx].valid, (int)WiFiFwdBase[idx].src, (int)WiFiFwdBase[idx].dest));
++
++ cal_link_count_by_net_dev(WiFiFwdBase[idx].src, 0);
++ memset(&WiFiFwdBase[idx], 0, sizeof(struct FwdPair));
++
++ } else
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]index=%d is void originally\n", __FUNCTION__, idx));
++ }
++ else
++ {
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ memset(&WiFiFwdBase[i], 0, sizeof(struct FwdPair));
++
++ wifi_fwd_reset_link_count();
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] flush all entries\n", __FUNCTION__));
++ }
++}
++
++static void packet_source_show_entry(void)
++{
++ int idx = 0;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("\n[%s]--------------------------------------------\n", __FUNCTION__));
++
++ for (idx=0; idx<WIFI_FWD_TBL_SIZE; idx++)
++ {
++ if (pkt_src[idx].valid)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]index=%d, valid=%d, src=0x%08X, peer=0x%08X, "
++ "h_source=%02X:%02X:%02X:%02X:%02X:%02X, h_dest=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__, idx, pkt_src[idx].valid, (int)pkt_src[idx].src, (int)pkt_src[idx].peer,
++ pkt_src[idx].h_source[0], pkt_src[idx].h_source[1], pkt_src[idx].h_source[2],
++ pkt_src[idx].h_source[3], pkt_src[idx].h_source[4], pkt_src[idx].h_source[5],
++ pkt_src[idx].h_dest[0], pkt_src[idx].h_dest[1], pkt_src[idx].h_dest[2],
++ pkt_src[idx].h_dest[3], pkt_src[idx].h_dest[4], pkt_src[idx].h_dest[5]));
++ }
++}
++
++static void packet_source_delete_entry(unsigned char idx)
++{
++ int i = 0;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("\n[%s]--------------------------------------------\n", __FUNCTION__));
++
++ if (idx < WIFI_FWD_TBL_SIZE)
++ {
++ if (pkt_src[idx].valid)
++ {
++ memset(&pkt_src[idx], 0, sizeof(struct PacketSource));
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]index=%d, valid=%d, src=0x%08X, peer=0x%08X, "
++ "h_source=%02X:%02X:%02X:%02X:%02X:%02X, h_dest=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__, idx, pkt_src[idx].valid, (int)pkt_src[idx].src, (int)pkt_src[idx].peer,
++ pkt_src[idx].h_source[0], pkt_src[idx].h_source[1], pkt_src[idx].h_source[2],
++ pkt_src[idx].h_source[3], pkt_src[idx].h_source[4], pkt_src[idx].h_source[5],
++ pkt_src[idx].h_dest[0], pkt_src[idx].h_dest[1], pkt_src[idx].h_dest[2],
++ pkt_src[idx].h_dest[3], pkt_src[idx].h_dest[4], pkt_src[idx].h_dest[5]));
++ } else
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]index=%d is void originally\n", __FUNCTION__, idx));
++ }
++ else if (idx == WIFI_FWD_TBL_SIZE)
++ {
++ eth_rep5g_h_wrg_uni_cnt = 0;
++ eth_rep5g_h_wrg_bct_cnt = 0;
++ eth_rep5g_wrg_uni_cnt = 0;
++ eth_rep5g_wrg_bct_cnt = 0;
++ eth_rep2g_wrg_uni_cnt = 0;
++ eth_rep2g_wrg_bct_cnt = 0;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] reset counters\n", __FUNCTION__));
++ }
++ else
++ {
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ memset(&pkt_src[i], 0, sizeof(struct PacketSource));
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] flush all entries\n", __FUNCTION__));
++ }
++}
++
++/*
++ return value:
++ >=0: array index of WiFiFwdBase
++ -1: search failed
++*/
++static int wifi_fwd_find_empty_entry(void)
++{
++ int idx = 0;
++
++ for (idx=0; idx<WIFI_FWD_TBL_SIZE; idx++)
++ {
++ if (WiFiFwdBase[idx].valid == FALSE)
++ return idx;
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] table full\n", __FUNCTION__));
++ return -1;
++}
++
++/*
++ return value:
++ >=0: array index of WiFiFwdBase
++ -1: search failed
++*/
++static int wifi_fwd_find_entry(struct net_device *src, struct net_device *dest)
++{
++ int idx = 0;
++
++ for (idx=0; idx<WIFI_FWD_TBL_SIZE; idx++)
++ {
++ if ((WiFiFwdBase[idx].valid == TRUE) &&
++ (WiFiFwdBase[idx].src == src) &&
++ (WiFiFwdBase[idx].dest == dest))
++ return idx;
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] no entry found\n", __FUNCTION__));
++ return -1;
++}
++
++static unsigned char wifi_fwd_establish_entry(struct net_device *src, struct net_device *dest)
++{
++ struct FwdPair *entry = NULL;
++ int idx = 0;
++
++ if (!src || !dest)
++ return 0;
++
++ /* check if it is an existed entry */
++ idx = wifi_fwd_find_entry(src, dest);
++ if (idx >= 0)
++ return 0;
++
++ /* to establish the path between src and dest */
++ idx = wifi_fwd_find_empty_entry();
++ if (idx == -1)
++ return 0;
++
++ entry = WiFiFwdBase + idx;
++ entry->valid = TRUE;
++ entry->src = src;
++ entry->dest = dest;
++ cal_link_count_by_net_dev(entry->src, 1);
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s %d] valid=%d, src=0x%08X, dest=0x%08X\n",
++ __FUNCTION__, idx, entry->valid, (unsigned int)entry->src, (unsigned int)entry->dest));
++
++ return 1;
++}
++
++/*
++ return value:
++ 1: clear OK
++ 0: clear failed (wrong input index)
++*/
++static int wifi_fwd_clear_entry(int index)
++{
++ struct FwdPair *entry = NULL;
++ entry = WiFiFwdBase + index;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[wifi_fwd_clear_entry] original: index=%d, valid=%d, src_dev=0x%08X, dest_dev=0x%08X\n",
++ index, entry->valid, (int)entry->src, (int)entry->dest));
++
++ if (entry->valid) {
++ memset(entry, 0, sizeof(struct FwdPair));
++ return 1;
++ }
++
++ return 0;
++}
++
++/*
++ return value:
++ 1: clear OK
++ 0: clear failed (wrong input index)
++*/
++static int wifi_fwd_clear_device(int index)
++{
++ struct DevInfo *entry = NULL;
++ entry = FwdDevice+ index;
++
++ if (entry->valid) {
++ memset(entry, 0, sizeof(struct DevInfo));
++ return 1;
++ }
++ return 0;
++}
++
++static void wifi_fwd_set_cb_num(unsigned int band_cb_num, unsigned int receive_cb_num)
++{
++ band_cb_offset = band_cb_num;
++ recv_from_cb_offset = receive_cb_num;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s] band_cb_offset=%d, recv_from_cb_offset=%d\n",
++ __FUNCTION__, band_cb_offset, recv_from_cb_offset));
++}
++
++static bool wifi_fwd_check_active(void)
++{
++ if (WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACTIVE)
++ && WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return TRUE;
++ else
++ return FALSE;
++}
++
++static unsigned char wifi_fwd_check_rep(unsigned char rep)
++{
++ switch(rep)
++ {
++ case eth_traffic_band_2g:
++ if (main_2g_link_cnt == 0)
++ {
++ if (next_2g_band == band_5g)
++ {
++ if (main_5g_link_cnt != 0)
++ return eth_traffic_band_5g;
++ else if (main_5g_h_link_cnt != 0)
++ return eth_traffic_band_5g_H;
++ }
++ else
++ {
++ if (main_5g_h_link_cnt != 0)
++ return eth_traffic_band_5g_H;
++ else if (main_5g_link_cnt != 0)
++ return eth_traffic_band_5g;
++ }
++ }
++ else
++ break;
++
++ case eth_traffic_band_5g:
++ if (main_5g_link_cnt == 0)
++ {
++ if (main_5g_h_link_cnt != 0)
++ return eth_traffic_band_5g_H;
++ else if (main_2g_link_cnt != 0)
++ return eth_traffic_band_2g;
++ }
++ else
++ break;
++
++ case eth_traffic_band_5g_H:
++ if (main_5g_h_link_cnt == 0)
++ {
++ if (main_5g_link_cnt != 0)
++ return eth_traffic_band_5g;
++ else if (main_2g_link_cnt != 0)
++ return eth_traffic_band_2g;
++ }
++ else
++ break;
++
++ default:
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s] rep=%d by default\n", __FUNCTION__, eth_traffic_band_5g));
++ return eth_traffic_band_5g;
++ }
++
++ return rep;
++}
++
++static void wifi_fwd_get_rep(unsigned char rep)
++{
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if (rep == 0)
++ rep_net_dev = eth_traffic_band_2g;
++ else if (rep == eth_traffic_band_5g_H)
++ rep_net_dev = eth_traffic_band_5g_H;
++ else
++ rep_net_dev = eth_traffic_band_5g;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s] rep = %d\n", __FUNCTION__, rep_net_dev));
++}
++
++static void wifi_fwd_set_debug_level(unsigned char level)
++{
++ if (level == 0)
++ wf_debug_level = WF_DEBUG_OFF;
++ else
++ wf_debug_level = WF_DEBUG_ON;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s] level=%d\n", __FUNCTION__, wf_debug_level));
++}
++
++static void wifi_fwd_pro_active(void)
++{
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void wifi_fwd_pro_halt(void)
++{
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void wifi_fwd_pro_enabled(void)
++{
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ENABLED);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void wifi_fwd_pro_disabled(void)
++{
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_ENABLED);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void wifi_fwd_access_schedule_active(void)
++{
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void wifi_fwd_access_schedule_halt(void)
++{
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void wifi_fwd_hijack_active(void)
++{
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_HIJACK_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void wifi_fwd_hijack_halt(void)
++{
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_HIJACK_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void packet_tx_source_flush_all(void)
++{
++ struct TxSourceEntry *entry = NULL;
++ int i = 0;
++
++ if (!tx_src_tbl || (tx_src_count == 0))
++ return;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++
++ /* keep the records of wireless stations */
++ if ((entry->valid == 1) && (entry->bfrom_wireless == 0))
++ {
++ tx_src_count--;
++ entry->valid = 0;
++ }
++
++ if (tx_src_count == 0)
++ break;
++ }
++}
++
++/* delete the records of wireless stations */
++static void wf_fwd_delete_entry_inform(unsigned char *addr)
++{
++ struct TxSourceEntry *entry = NULL;
++ int i = 0, count = 0;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if (!addr || (tx_src_count == 0))
++ return;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++
++ if (entry->valid == TRUE)
++ {
++ count++;
++ if(MAC_ADDR_EQUAL(addr, entry->h_source))
++ {
++ entry->valid = FALSE;
++ entry->bfrom_wireless = 0;
++ tx_src_count--;
++
++ if (tx_src_count < 0)
++ tx_src_count = 0;
++
++ return;
++ }
++
++ if (count >= tx_src_count)
++ return;
++ }
++ }
++}
++
++/* add the records of wireless stations */
++static void wf_fwd_add_entry_inform(unsigned char *addr)
++{
++ struct TxSourceEntry *entry = NULL;
++ int i = 0;
++
++ if (!addr)
++ return;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++
++ if (entry->valid == FALSE)
++ {
++ tx_src_count++;
++ entry->valid = TRUE;
++ entry->bfrom_wireless = 1;
++ COPY_MAC_ADDR(entry->h_source, addr);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s %d] valid=%d, src_mac=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__,i, entry->valid,
++ entry->h_source[0], entry->h_source[1], entry->h_source[2],
++ entry->h_source[3], entry->h_source[4], entry->h_source[5]));
++ break;
++ }
++ }
++}
++
++/*
++ return value:
++ 0: insert failed
++ 1: insert success
++ 2: do nothing
++*/
++static int wifi_fwd_insert_tx_source_entry(struct sk_buff *skb,struct DevInfo *tx_dev_info)
++{
++ struct ethhdr *mh = eth_hdr(skb);
++ //unsigned int recv_from = WIFI_FWD_GET_PACKET_RECV_FROM(skb, recv_from_cb_offset);
++ struct TxSourceEntry *entry = NULL;
++ int i = 0;
++ unsigned char bInsert = 0;
++
++ if((IS_VALID(tx_dev_info->valid) && !is_concurrent_mode(tx_dev_info->dev)) || !mh)
++ return 2;
++
++ if(IS_VALID(tx_dev_info->valid) && IS_APCLI_DEV(tx_dev_info->type))
++ {
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++ if ((entry->valid == 1) &&
++ MAC_ADDR_EQUAL(mh->h_source, entry->h_source))
++ return 2;
++ }
++ bInsert = 1;
++ }
++
++ if(!bInsert)
++ return 2;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++ if (entry->valid == 0)
++ {
++ tx_src_count++;
++ entry->valid = 1;
++ entry->bfrom_wireless = 0;
++ COPY_MAC_ADDR(entry->h_source, mh->h_source);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s %d] valid=%d, src_mac=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__,i, entry->valid,
++ entry->h_source[0], entry->h_source[1], entry->h_source[2],
++ entry->h_source[3], entry->h_source[4], entry->h_source[5]));
++ return 1;
++ }
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] No free space for insert tx source entry.\n",__FUNCTION__));
++ return 0;
++}
++
++/*
++ return value:
++ 0: no looping
++ 1: looping found
++ 2: forward to bridge
++*/
++static unsigned char wifi_fwd_check_looping(
++ struct sk_buff *skb,
++ struct DevInfo *dev_info)
++{
++ struct ethhdr *mh = eth_hdr(skb);
++ struct TxSourceEntry *entry = NULL;
++ unsigned int recv_from = WIFI_FWD_GET_PACKET_RECV_FROM(skb, recv_from_cb_offset);
++ int i = 0, count = 0;
++ unsigned char bMulticast = 0;
++
++ /*
++ no need to check while:
++ 1. this skb has no ethernet header
++ 2. this packet is not received from ap-client
++ 3. tx_src_count is 0 -> tx_src_tbl has no entries
++ 4. dev_info is valid and not in concurrent mode
++ */
++ if ((IS_VALID(dev_info->valid) && !is_concurrent_mode(dev_info->dev)) ||
++ !IS_PACKET_FROM_APCLI(recv_from) ||
++ (tx_src_count == 0) ||
++ !mh)
++ return 0;
++
++ if (IS_VALID(dev_info->valid) && IS_APCLI_DEV(dev_info->type))
++ {
++ if ((mh->h_dest[0] & 0x1) == 0x1)
++ bMulticast = 1;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++ if (entry->valid == 1)
++ {
++ count++;
++ if (MAC_ADDR_EQUAL(mh->h_source, entry->h_source))
++ {
++ /* means the source has changed to other side no matter it comes from wireless or ethernet */
++ if (!bMulticast)
++ {
++ wf_fwd_delete_entry_inform(entry->h_source);
++ return 0;
++ }
++
++ return 1;
++ }
++
++ if (count >= tx_src_count)
++ break;
++ }
++ }
++ }
++
++ return 0;
++}
++
++/*
++ return value:
++ 0: do nothing
++ 1: forward to bridge
++*/
++static unsigned char wifi_fwd_pkt_to_bridge_by_dest_addr(
++ struct sk_buff *skb,
++ struct net_device *dev)
++{
++ struct ethhdr *mh = eth_hdr(skb);
++ struct TxSourceEntry *tx_src_entry = NULL;
++ unsigned int recv_from;
++ int i = 0, count = 0;
++
++ recv_from = WIFI_FWD_GET_PACKET_RECV_FROM(skb, recv_from_cb_offset);
++
++ if ((tx_src_count > 0) && IS_PACKET_FROM_APCLI(recv_from) && mh)
++ {
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ tx_src_entry = tx_src_tbl + i;
++ if (tx_src_entry->valid)
++ {
++ count++;
++ if ((tx_src_entry->bfrom_wireless == 0) &&
++ MAC_ADDR_EQUAL(tx_src_entry->h_source, mh->h_dest))
++ return 1;
++
++ if (count >= tx_src_count)
++ break;
++ }
++ }
++ }
++
++ return 0;
++}
++
++void wifi_fwd_check_device(struct net_device *net_dev, signed int type, signed int mbss_idx, unsigned char channel, unsigned char link)
++{
++ struct DevInfo *DeviceEntry = NULL;
++ struct DevInfo *DeviceEntryTmp = NULL;
++ struct DevInfo *DeviceTmp = NULL;
++ struct DevInfo *DevicePartnerCheck = NULL;
++ unsigned char idx = 0, i = 0, j = 0, valid_index = 0;
++ bool get_idx = FALSE;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if (!net_dev || !type)
++ return;
++
++ FWD_IRQ_LOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++
++ for (idx=0; idx<WIFI_FWD_DEVICE_SIZE; idx++)
++ {
++ DeviceEntry = FwdDevice + idx;
++
++ if (IS_VALID(DeviceEntry->valid) && (DeviceEntry->dev == net_dev))
++ {
++ if (link == 0)
++ {
++ DevicePartnerCheck = DeviceEntry->partner;
++
++ /* to avoid there is one more partner point to this device */
++ if (DevicePartnerCheck != NULL)
++ {
++ for (j=0; j<WIFI_FWD_DEVICE_SIZE; j++)
++ {
++ DeviceTmp = FwdDevice + j;
++
++ if (idx == j)
++ continue;
++
++ if ((DeviceTmp->band == DevicePartnerCheck->band) &&
++ (DeviceTmp->type != DevicePartnerCheck->type))
++ {
++ DevicePartnerCheck->partner = DeviceTmp;
++ DeviceTmp->partner = DevicePartnerCheck;
++ }
++ }
++ }
++
++ wifi_fwd_clear_device(idx);
++ }
++
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++ return;
++ }
++
++ if ((DeviceEntry->valid == FALSE) && (get_idx != TRUE))
++ {
++ valid_index = idx;
++ get_idx = TRUE;
++ }
++ }
++
++ if (link == 0)
++ {
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++ return;
++ }
++
++ if (get_idx != TRUE)
++ {
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s] No free space for inserting device info.\n", __FUNCTION__));
++ return;
++ }
++
++ DeviceEntry = FwdDevice + valid_index;
++ DeviceEntry->dev = net_dev;
++ DeviceEntry->index = valid_index;
++ DeviceEntry->type = type;
++ DeviceEntry->mbss_idx = mbss_idx;
++
++ if (IS_5G_HIGH(channel))
++ {
++ DeviceEntry->band = band_5g_h;
++ DeviceEntry->second_band = band_5g;
++ DeviceEntry->third_band = band_2g;
++ }
++ else if (channel > 14)
++ {
++ DeviceEntry->band = band_5g;
++ DeviceEntry->second_band = band_5g_h;
++ DeviceEntry->third_band = band_2g;
++ }
++ else
++ {
++ DeviceEntry->band = band_2g;
++ DeviceEntry->second_band = band_5g;
++ DeviceEntry->third_band = band_5g_h;
++ }
++
++ DeviceEntry->partner = NULL;
++
++ for (i=0; i<WIFI_FWD_DEVICE_SIZE; i++)
++ {
++ DeviceEntryTmp = FwdDevice + i;
++
++ if (i == DeviceEntry->index)
++ continue;
++
++ if ((DeviceEntryTmp->band == DeviceEntry->band) &&
++ (DeviceEntryTmp->type != DeviceEntry->type))
++ {
++ DeviceEntry->partner = DeviceEntryTmp;
++ DeviceEntryTmp->partner = DeviceEntry;
++ }
++ }
++
++ DeviceEntry->valid = TRUE;
++
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++ return;
++}
++
++static char wifi_fwd_find_fastlane(struct DevInfo *src, struct DevInfo *dest)
++{
++ struct DevInfo *DeviceOthSrc = NULL;
++ int idx = 0, i = 0;
++
++ if (dest == NULL)
++ return -1;
++
++ for (i=0; i<WIFI_FWD_DEVICE_SIZE; i++)
++ {
++ DeviceOthSrc = FwdDevice+i;
++
++ if ((DeviceOthSrc == NULL) || (DeviceOthSrc == src))
++ continue;
++
++ if ((DeviceOthSrc->valid == TRUE) &&
++ IS_APCLI_DEV(DeviceOthSrc->type))
++ {
++ idx = wifi_fwd_find_entry(DeviceOthSrc->dev, dest->dev);
++
++ if (idx >= 0)
++ return idx;
++ }
++ }
++
++ return -1;
++
++}
++
++
++/*
++ return value:
++ 1: delete success
++ 0: delete failed
++*/
++static unsigned char wifi_fwd_delete_entry(struct net_device *src, struct net_device *dest, unsigned char link_down)
++{
++ int i, j;
++ int src_index, tmp_index, second_src_index, third_src_index;
++ bool need_flush = FALSE;
++ struct DevInfo *DeviceTmp = NULL, *DeviceSecondSrc = NULL, *DeviceThirdSrc = NULL;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return 0;
++
++ if (!src)
++ return 0;
++
++ FWD_IRQ_LOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++
++ /* if AP interface is down */
++ if (link_down == 0)
++ {
++ for (i = 0; i < WIFI_FWD_DEVICE_SIZE; i++)
++ {
++ DeviceTmp = FwdDevice+i;
++
++ if (DeviceTmp->valid != TRUE)
++ continue;
++
++ if (!IS_APCLI_DEV(DeviceTmp->type))
++ continue;
++
++ tmp_index = wifi_fwd_find_entry(DeviceTmp->dev, dest);
++
++ if (tmp_index>=0)
++ {
++ if (wifi_fwd_clear_entry(tmp_index)!=0)
++ {
++ cal_link_cnt_by_idx(i, 0);
++ need_flush = TRUE;
++ }
++ }
++ }
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++ goto done;
++ }
++
++ src_index = wifi_fwd_find_device(src);
++
++ if (src_index < 0)
++ {
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s][%d]Error!!!!!!!!!!!!src_index(%d) src(%d)\n", __FUNCTION__, __LINE__, src_index, (int)src));
++ return 0;
++ }
++ /*
++ Delete the src path
++ */
++ for (j = 0; j < WIFI_FWD_DEVICE_SIZE; j++)
++ {
++ DeviceTmp = FwdDevice+j;
++
++ if (DeviceTmp->valid != TRUE)
++ continue;
++
++ if (!IS_AP_DEV(DeviceTmp->type))
++ continue;
++
++ tmp_index = wifi_fwd_find_entry(src, DeviceTmp->dev);
++
++ if (tmp_index>=0)
++ {
++ if (wifi_fwd_clear_entry(tmp_index)!=0)
++ {
++ cal_link_cnt_by_idx(src_index, 0);
++ need_flush = TRUE;
++
++ second_src_index = wifi_fwd_find_device_by_info(DeviceTmp->second_band,INT_APCLI);
++ DeviceSecondSrc = FwdDevice+second_src_index;
++
++ if ((second_src_index>=0) && (second_src_index!=src_index))
++ wifi_fwd_establish_entry(DeviceSecondSrc->dev, DeviceTmp->dev);
++ else
++ {
++ third_src_index = wifi_fwd_find_device_by_info(DeviceTmp->third_band,INT_APCLI);
++
++ DeviceThirdSrc = FwdDevice+third_src_index;
++
++ if ((third_src_index>=0) && (third_src_index!=src_index))
++ wifi_fwd_establish_entry(DeviceThirdSrc->dev, DeviceTmp->dev);
++
++ }
++ }
++ }
++ }
++
++
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++
++done:
++
++ if (need_flush)
++ {
++ /* flush packet source table */
++ packet_source_delete_entry(WIFI_FWD_TBL_SIZE+1);
++
++ /* flush tx packet source table */
++ packet_tx_source_flush_all();
++ }
++
++ WIFI_FWD_CLEAR_FLAG(fOP_GET_NET_DEVICE_STATUS_DONE);
++ return 1;
++}
++
++
++/*
++ return value:
++ 1: insert success
++ 0: insert failed
++*/
++static unsigned char wifi_fwd_insert_entry(struct net_device *src, struct net_device *dest, void *adapter)
++{
++ struct DevInfo *DeviceEntrySrc = NULL, *DeviceEntryDest = NULL, *DeviceEntryOthDest = NULL, *DeviceEntryOthSrc = NULL;
++ int idx = 0, k = 0, index = 0, src_index = 0;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return 0;
++
++ if (!src || !dest)
++ return 0;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_GET_NET_DEVICE_STATUS_DONE)) {
++ dump_bridge_by_name();
++ WIFI_FWD_SET_FLAG(fOP_GET_NET_DEVICE_STATUS_DONE);
++ }
++
++ FWD_IRQ_LOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++
++ /*
++ src is the net device of ap client trying to insert the entry into WiFi forwarding table
++
++ according to forwarding device table, find the partner of src
++ */
++ for (src_index=0; src_index<WIFI_FWD_DEVICE_SIZE; src_index++)
++ {
++ DeviceEntrySrc = FwdDevice + src_index;
++
++ if ((DeviceEntrySrc != NULL) &&
++ (DeviceEntrySrc->valid == TRUE) &&
++ (DeviceEntrySrc->dev == src) &&
++ IS_APCLI_DEV(DeviceEntrySrc->type))
++ {
++ DeviceEntryDest = DeviceEntrySrc->partner;
++ break;
++ }
++ }
++
++ if (src_index >= WIFI_FWD_DEVICE_SIZE)
++ {
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s][%d]cannot find DevSrc(%x) in FwdDevice\n", __FUNCTION__, __LINE__, (int)src));
++ return 0;
++ }
++
++ /*
++ check if there exists fastlane path by searching forwarding device table
++ if yes, delete the cross path of fastlane
++ */
++
++ idx = wifi_fwd_find_fastlane(DeviceEntrySrc, DeviceEntryDest);
++
++ if (idx >= 0)
++ wifi_fwd_delete_entry_by_idx(idx);
++
++ if (DeviceEntryDest != NULL)
++ {
++ /* try to establish the path between src and dest */
++ if (wifi_fwd_establish_entry(DeviceEntrySrc->dev, DeviceEntryDest->dev) == 0)
++ {
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s][%d]forwarding path establishing failed\n", __FUNCTION__, __LINE__));
++ return 0;
++ }
++ }
++
++ /*
++ check if there exists higher priority forwarding path for other dest from ap view
++ if yes, change the link from original src to new one
++ */
++ for (k=0; k<WIFI_FWD_DEVICE_SIZE; k++)
++ {
++ DeviceEntryOthDest = FwdDevice + k;
++
++ if (DeviceEntryOthDest->valid != TRUE)
++ continue;
++
++ if (!IS_AP_DEV(DeviceEntryOthDest->type))
++ continue;
++
++ if (DeviceEntryOthDest != DeviceEntryDest)
++ {
++ index = -1;
++ DeviceEntryOthSrc = NULL;
++
++ if ((DeviceEntryOthDest->partner != NULL) &&
++ (DeviceEntryOthDest->partner->valid == TRUE))
++ DeviceEntryOthSrc = DeviceEntryOthDest->partner;
++
++ if ((DeviceEntryOthSrc != NULL) && IS_VALID(DeviceEntryOthSrc->valid))
++ index = wifi_fwd_find_entry(DeviceEntryOthSrc->dev,DeviceEntryOthDest->dev);
++
++ if (index >= 0)
++ continue;
++
++ index = wifi_fwd_find_fastlane(DeviceEntryOthSrc, DeviceEntryOthDest);
++
++ if (index < 0)
++ {
++ if (DeviceEntrySrc != NULL)
++ wifi_fwd_establish_entry(DeviceEntrySrc->dev, DeviceEntryOthDest->dev);
++
++ continue;
++ }
++
++ wifi_fwd_delete_entry_by_idx(index);
++ if (DeviceEntrySrc != NULL)
++ wifi_fwd_establish_entry(DeviceEntrySrc->dev, DeviceEntryOthDest->dev);
++
++#if 0
++ /*Increase 2G apcli link cannot change 5G's link*/
++ if (DeviceEntrySrc->band == band_5g)
++ {
++ if (((DeviceEntryOthDest->band == band_2g) && (next_2g_band == band_5g)) ||
++ (DeviceEntryOthDest->band == band_5g_h))
++ {
++ wifi_fwd_delete_entry_by_idx(index);
++
++ if (DeviceEntrySrc!=NULL)
++ wifi_fwd_establish_entry(DeviceEntrySrc->dev, DeviceEntryOthDest->dev);
++ }
++ }
++ else if (DeviceEntrySrc->band == band_5g_h)
++ {
++ if (((DeviceEntryOthDest->band == band_2g) && (next_2g_band == band_5g_h)) ||
++ (DeviceEntryOthDest->band == band_5g))
++ {
++
++ wifi_fwd_delete_entry_by_idx(index);
++
++ if (DeviceEntrySrc!=NULL)
++ wifi_fwd_establish_entry(DeviceEntrySrc->dev, DeviceEntryOthDest->dev);
++ }
++ }
++ else if (DeviceEntrySrc->band == band_2g)
++ {
++ if (DeviceEntryOthDest->band == band_2g)
++ {
++ wifi_fwd_delete_entry_by_idx(index);
++
++ if (DeviceEntrySrc != NULL)
++ wifi_fwd_establish_entry(DeviceEntrySrc->dev, DeviceEntryOthDest->dev);
++ }
++ }
++#endif
++ }
++
++ }
++
++ FWD_IRQ_UNLOCK(&priority_tbl_lock, priority_tbl_irq_flags);
++
++ /* flush packet source table */
++ packet_source_delete_entry(WIFI_FWD_TBL_SIZE+1);
++ return 1;
++}
++
++
++/*
++ return value:
++ 0: Success, skb is handled by wifi_fwd module.
++ 1: FAIL, do nothing
++*/
++static struct net_device * wifi_fwd_insert_packet_source(struct sk_buff *skb, struct net_device *dev, struct net_device *peer)
++{
++ struct ethhdr *mh = eth_hdr(skb);
++ struct PacketSource *entry = NULL;
++ bool need_flush = false;
++ int i = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 1) {
++ if (peer == entry->peer) {
++ if (MAC_ADDR_EQUAL(mh->h_source, entry->h_source)) {
++ if (dev == entry->src) {
++ return peer;
++ } else {
++ need_flush = true;
++ break;
++ }
++ }
++ }
++ }
++ }
++
++ if (need_flush == true)
++ packet_source_delete_entry(WIFI_FWD_TBL_SIZE+1);
++ else
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] packet source cannot be found\n", __FUNCTION__));
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 0) {
++ COPY_MAC_ADDR(entry->h_source, mh->h_source);
++ COPY_MAC_ADDR(entry->h_dest, mh->h_dest);
++ entry->valid = 1;
++ entry->peer = peer;
++ entry->src = dev;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s %d] valid=%d, from=0x%08X, will send to=0x%08X, "
++ "src_mac=%02X:%02X:%02X:%02X:%02X:%02X, dest_mac=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__, i, entry->valid, (int)entry->src, (int)entry->peer,
++ entry->h_source[0], entry->h_source[1], entry->h_source[2],
++ entry->h_source[3], entry->h_source[4], entry->h_source[5],
++ entry->h_dest[0], entry->h_dest[1], entry->h_dest[2],
++ entry->h_dest[3], entry->h_dest[4],entry->h_dest[5]));
++
++ return entry->peer;
++ }
++ }
++ return NULL;
++}
++
++static void wifi_fwd_tx_count_by_source_addr(
++ struct sk_buff *skb,
++ struct net_device *tx_dev,
++ unsigned char *found_path,
++ unsigned char *entry_cnt)
++{
++ struct PacketSource *ppkt_src = NULL;
++ struct FwdPair *entry = NULL;
++ int i = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ ppkt_src = pkt_src + i;
++
++ if (ppkt_src->valid == 1)
++ {
++ if (MAC_ADDR_EQUAL(&skb->data[6], ppkt_src->h_source))
++ {
++ *entry_cnt = *entry_cnt + 1;
++
++ if ((ppkt_src->src == tx_dev) || (ppkt_src->peer == tx_dev))
++ {
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = WiFiFwdBase + i;
++
++ if (entry->valid == 1)
++ {
++ if (((entry->src == ppkt_src->src) && (entry->dest == ppkt_src->peer)) ||
++ ((entry->src == ppkt_src->peer) && (entry->dest == ppkt_src->src)))
++ *found_path = *found_path + 1;
++ }
++ }
++ }
++ }
++ }
++ else
++ break;
++ }
++}
++
++
++/*
++ return value:
++ 1: find entry
++ 0: find nothing
++*/
++static unsigned char wifi_fwd_find_sa_entry_by_sa_and_nd(
++ struct sk_buff *skb,
++ struct net_device *sender_dev,
++ unsigned char idx)
++{
++ struct PacketSource *entry = NULL;
++ struct ethhdr *mh = eth_hdr(skb);
++ int i = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 1)
++ {
++ if (MAC_ADDR_EQUAL(mh->h_source, entry->h_source))
++ {
++ if (idx == 0)
++ {
++ if (entry->src == sender_dev)
++ return 1;
++ }
++ else
++ {
++ if (entry->peer== sender_dev)
++ return 1;
++ }
++ }
++ }
++ else
++ break;
++ }
++
++ return 0;
++}
++
++#if 0
++/*
++ return value: # of found entries
++*/
++static unsigned char wifi_fwd_sa_count_by_source_addr(
++ struct sk_buff *skb,
++ struct net_device *receive_dev)
++{
++ struct PacketSource *entry = NULL;
++ struct ethhdr *mh = eth_hdr(skb);
++ int i = 0, sa_cnt = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 1)
++ {
++ if ((mh->h_source[0] & 0x2) == 0x2)
++ {
++ if (memcmp(&mh->h_source[3], &entry->h_source[3], 3) == 0)
++ sa_cnt++;
++ }
++ else
++ {
++ if (MAC_ADDR_EQUAL(mh->h_source, entry->h_source))
++ sa_cnt++;
++ }
++ }
++ else
++ break;
++ }
++
++ return sa_cnt;
++}
++#endif
++
++/*
++ return value:
++ 0: suppose it is forwarded from bridge
++ 1: not forwarded from bridge
++ 2: should be forwarded to bridge
++*/
++static unsigned char wifi_fwd_check_from_bridge_by_dest_addr(
++ struct sk_buff *skb,
++ struct DevInfo *sender_dev_info)
++{
++ struct PacketSource *entry = NULL;
++ struct ethhdr *mh = eth_hdr(skb);
++ struct DevInfo *entry_dev_info = NULL;
++ int i = 0, idx = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 1)
++ {
++ idx = wifi_fwd_find_device(entry->src);
++
++ if (idx<0)
++ {
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] idx(%d)\n", __FUNCTION__, idx));
++ return 0;
++ }
++
++ entry_dev_info = FwdDevice+idx;
++
++ if ((sender_dev_info->valid == TRUE) && (entry_dev_info->valid == TRUE))
++ {
++ if ((IS_AP_DEV(sender_dev_info->type) && IS_AP_DEV(entry_dev_info->type)) &&
++ (sender_dev_info->index != entry_dev_info->index))
++ {
++ if (MAC_ADDR_EQUAL(mh->h_dest, entry->h_source))
++ return 2;
++ }
++ else
++ {
++ if (MAC_ADDR_EQUAL(mh->h_dest, entry->h_source))
++ return 1;
++ }
++ }
++ }
++ else
++ break;
++ }
++
++ return 0;
++}
++
++/*
++ return value:
++ 0: error
++ > 0: the band that the packet comes from
++*/
++static unsigned int band_pkt_from(struct sk_buff *skb)
++{
++ unsigned int band_from = WIFI_FWD_GET_PACKET_BAND(skb, band_cb_offset);
++
++ if (IS_PACKET_FROM_2G(band_from))
++ return band_2g;
++ else if (IS_PACKET_FROM_5G(band_from))
++ return band_5g;
++ else if (IS_PACKET_FROM_5G_H(band_from))
++ return band_5g_h;
++}
++
++static void wifi_fwd_clone_and_redirect_pkt(struct sk_buff *skb, BAND_INDEX band)
++{
++ struct sk_buff *clone_pkt = NULL;
++ struct DevInfo *rdt_dev = NULL;
++ int rdt_dev_idx = 0;
++
++ clone_pkt = skb_clone(skb, GFP_ATOMIC);
++ rdt_dev_idx = wifi_fwd_find_device_by_info(band, INT_APCLI);
++
++ if (rdt_dev_idx < 0)
++ goto error;
++
++ rdt_dev = FwdDevice + rdt_dev_idx;
++
++ if (rdt_dev == NULL)
++ goto error;
++
++ clone_pkt->dev = rdt_dev->dev;
++ dev_queue_xmit(clone_pkt);
++ return;
++
++
++error:
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s] cannnot find redirect device(%d)\n", __FUNCTION__, rdt_dev_idx));
++ return;
++}
++
++
++/*
++ return value:
++ 0: no need to drop this packet
++ 1: drop this packet
++*/
++static unsigned char wifi_fwd_tx_lookup_entry(struct net_device *tx_dev, struct sk_buff *skb)
++{
++ struct DevInfo *device_tx = NULL;
++ unsigned char found = 0, entry_cnt = 0, rep = 0;
++ unsigned int recv_from = 0, band_from = 0;
++ int idx = 0, need_redirect = 0;
++
++ band_from = WIFI_FWD_GET_PACKET_BAND(skb, band_cb_offset);
++ recv_from = WIFI_FWD_GET_PACKET_RECV_FROM(skb, recv_from_cb_offset);
++
++ /* drop the packet due to there's no forwarding links between ap-client and ap */
++ if (IS_PACKET_FROM_APCLI(recv_from) && !MAIN_SSID_OP(tx_dev))
++ return 1;
++
++ /*
++ in FastLane topology:
++ 1. forward the packet to driver and handle without any drop
++ 2. only for unicast
++ */
++ if (IS_UNICAST_PACKET(skb) && is_fastlane_mode(tx_dev))
++ return 0;
++
++ idx = wifi_fwd_find_device(tx_dev);
++ if (idx < 0)
++ return 1;
++
++ device_tx = FwdDevice + idx;
++
++ /*
++ drop the packet from other band if
++ 1. @ concurrent mode
++ 2. the tx device is valid
++ 3. the tx device is ap-client, not ap
++ */
++ if (is_concurrent_mode(tx_dev)
++ && IS_VALID(device_tx->valid)
++ && IS_APCLI_DEV(device_tx->type))
++ {
++ /* double check the rep setting due to tri-band topology */
++ rep = wifi_fwd_check_rep(rep_net_dev);
++
++ if (device_tx->band == band_5g_h)
++ {
++ if (IS_PACKET_FROM_2G(band_from) && (main_2g_link_cnt != 0))
++ {
++ if (IS_TAG_PACKET(band_from))
++ need_redirect = band_2g;
++ else
++ return 1;
++ }
++
++ if ((IS_PACKET_FROM_5G(band_from) && (main_5g_link_cnt != 0)) ||
++ ((IS_PACKET_FROM_2G(band_from) && (main_2g_link_cnt == 0)) && (next_2g_band == band_5g)))
++ {
++ if (IS_TAG_PACKET(band_from))
++ need_redirect = band_5g;
++ else
++ return 1;
++ }
++
++ if (IS_PACKET_FROM_ETHER(band_from))
++ {
++ if (IS_UNICAST_PACKET(skb))
++ {
++ if (rep == eth_traffic_band_2g)
++ {
++ eth_rep2g_wrg_uni_cnt++;
++ need_redirect = band_2g;
++ }
++ else if (rep == eth_traffic_band_5g)
++ {
++ eth_rep5g_wrg_uni_cnt++;
++ need_redirect = band_5g;
++ }
++ }
++ else
++ {
++ if (rep == eth_traffic_band_2g)
++ eth_rep2g_wrg_bct_cnt++;
++ else if (rep == eth_traffic_band_5g)
++ eth_rep5g_wrg_bct_cnt++;
++ }
++ }
++ }
++ else if (device_tx->band == band_5g)
++ {
++ if (IS_PACKET_FROM_2G(band_from) && (main_2g_link_cnt != 0))
++ {
++ if (IS_TAG_PACKET(band_from))
++ need_redirect = band_2g;
++ else
++ return 1;
++ }
++
++ if ((IS_PACKET_FROM_5G_H(band_from) && (main_5g_h_link_cnt != 0)) ||
++ ((IS_PACKET_FROM_2G(band_from) && (main_2g_link_cnt == 0)) && (next_2g_band == band_5g_h)))
++ {
++ if (IS_TAG_PACKET(band_from))
++ need_redirect = band_5g_h;
++ else
++ return 1;
++ }
++ if (IS_PACKET_FROM_ETHER(band_from))
++ {
++ if (IS_UNICAST_PACKET(skb))
++ {
++ if (rep == eth_traffic_band_2g)
++ {
++ eth_rep2g_wrg_uni_cnt++;
++ need_redirect = band_2g;
++ }
++ else if (rep == eth_traffic_band_5g_H)
++ {
++ eth_rep5g_h_wrg_uni_cnt++;
++ need_redirect = band_5g_h;
++ }
++ }
++ else
++ {
++ if (rep == eth_traffic_band_2g)
++ eth_rep2g_wrg_bct_cnt++;
++ else if (rep == eth_traffic_band_5g_H)
++ eth_rep5g_h_wrg_bct_cnt++;
++ }
++ }
++ }
++ else if (device_tx->band == band_2g)
++ {
++ if (IS_PACKET_FROM_5G(band_from) && (main_5g_link_cnt != 0))
++ {
++ if (IS_TAG_PACKET(band_from))
++ need_redirect = band_5g;
++ else
++ return 1;
++ }
++
++ if (IS_PACKET_FROM_5G_H(band_from) && (main_5g_h_link_cnt != 0))
++ {
++ if (IS_TAG_PACKET(band_from))
++ need_redirect = band_5g_h;
++ else
++ return 1;
++ }
++
++ if (IS_PACKET_FROM_ETHER(band_from))
++ {
++ if (IS_UNICAST_PACKET(skb))
++ {
++ if (rep == eth_traffic_band_5g)
++ {
++ eth_rep5g_wrg_uni_cnt++;
++ need_redirect = band_5g;
++ }
++ else if (rep == eth_traffic_band_5g_H)
++ {
++ eth_rep5g_h_wrg_uni_cnt++;
++ need_redirect = band_5g_h;
++ }
++ }
++ else
++ {
++ if (rep == eth_traffic_band_5g)
++ eth_rep5g_wrg_bct_cnt++;
++ else if (rep == eth_traffic_band_5g_H)
++ eth_rep5g_h_wrg_bct_cnt++;
++ }
++ }
++ }
++
++ if (need_redirect != 0)
++ {
++ wifi_fwd_clone_and_redirect_pkt(skb, need_redirect);
++ return 1;
++ }
++ }
++
++ wifi_fwd_tx_count_by_source_addr(skb, tx_dev, &found, &entry_cnt);
++
++ if (IS_BROADCAST_PACKET(skb))
++ {
++ if(wifi_fwd_insert_tx_source_entry(skb,device_tx) == 0)
++ return 0;
++
++ if (found == 0)
++ {
++ if (entry_cnt == 0)
++ {
++ if (is_concurrent_mode(tx_dev) &&
++ IS_PACKET_FROM_ETHER(band_from))
++ {
++ rep = wifi_fwd_check_rep(rep_net_dev);
++
++ if (IS_VALID(device_tx->valid) && IS_APCLI_DEV(device_tx->type))
++ {
++ if (device_tx->band == band_5g_h)
++ {
++ if (rep == eth_traffic_band_5g_H)
++ return 0;
++ else
++ return 1;
++ }
++ else if (device_tx->band == band_5g)
++ {
++ if (rep == eth_traffic_band_5g)
++ return 0;
++ else
++ return 1;
++ }
++ else if (device_tx->band == band_2g)
++ {
++ if (rep == eth_traffic_band_2g)
++ return 0;
++ else
++ return 1;
++ }
++ }
++ }
++ }
++
++ return 0;
++ }
++ else
++ {
++ if (entry_cnt >= 2)
++ return 0;
++ else
++ {
++ if (wifi_fwd_find_sa_entry_by_sa_and_nd(skb, tx_dev, 1) == 0)
++ return 1;
++ else
++ return 0;
++ }
++ }
++ }
++ else
++ {
++ if (found == 0)
++ {
++ if (entry_cnt == 0)
++ {
++ if (is_concurrent_mode(tx_dev) &&
++ IS_PACKET_FROM_ETHER(band_from))
++ {
++ rep = wifi_fwd_check_rep(rep_net_dev);
++
++ if (rep == eth_traffic_band_5g_H)
++ {
++ if (IS_VALID(device_tx->valid) && REP_IS_5G_H(device_tx->band))
++ return 0;
++ }
++ else if (rep == eth_traffic_band_5g)
++ {
++ if (IS_VALID(device_tx->valid) && REP_IS_5G(device_tx->band))
++ return 0;
++ }
++ else if (rep == eth_traffic_band_2g)
++ {
++ if (IS_VALID(device_tx->valid) && REP_IS_2G(device_tx->band))
++ return 0;
++ }
++ }
++ }
++ }
++
++ return 0;
++ }
++}
++
++/*
++ return value of struct net_device:
++ NULL: search failed
++ other: partner net_device
++*/
++static struct net_device * wifi_fwd_lookup_entry(struct net_device *dev, unsigned char *type, struct sk_buff *skb)
++{
++ struct FwdPair *entry = NULL;
++ struct net_device *src = NULL;
++ int i = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = WiFiFwdBase + i;
++
++ if (entry->valid == 1)
++ {
++ if (entry->src == dev)
++ {
++ *type = ENTRY_TYPE_SRC;
++ src = wifi_fwd_insert_packet_source(skb, dev, entry->dest);
++
++ if (src != NULL)
++ return src;
++ }
++ else if (entry->dest == dev)
++ {
++ *type = ENTRY_TYPE_DEST;
++ src = wifi_fwd_insert_packet_source(skb, dev, entry->src);
++
++ if (src != NULL)
++ return src;
++ }
++ }
++ }
++
++ *type = ENTRY_TYPE_INVALID;
++ return NULL;
++}
++
++void wifi_fwd_probe_adapter(void *adapter)
++{
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if ((adapter != NULL) && (adapter_tmp_1 == NULL)) {
++ adapter_tmp_1 = adapter;
++ } else if ((adapter != NULL) && (adapter_tmp_2 == NULL)) {
++ if (adapter != adapter_tmp_1)
++ adapter_tmp_2 = adapter;
++ } else if ((adapter != NULL) && (adapter_tmp_3 == NULL)) {
++ if ((adapter != adapter_tmp_1) && (adapter != adapter_tmp_2))
++ adapter_tmp_3 = adapter;
++
++ }
++}
++
++void wifi_fwd_feedback_map_table(void *adapter, void **peer, void **opp_peer, void **oth_peer)
++{
++ unsigned char index;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if(!adapter)
++ return;
++
++ FWD_IRQ_LOCK(&global_band_tbl_lock, global_band_tbl_irq_flags);
++
++ for (index=0; index<num_rep_tbl; index++)
++ {
++ if (adapter == *adapter_table[index])
++ {
++ if (rep_table[index]->Enabled == TRUE)
++ *peer = rep_table[index];
++ else
++ *peer = NULL;
++
++ }
++ else
++ {
++ if (*opp_peer == NULL)
++ {
++ if (rep_table[index] && rep_table[index]->Enabled == TRUE)
++ *opp_peer = rep_table[index];
++ else
++ *opp_peer = NULL;
++ }
++ else
++ {
++ if (rep_table[index] && rep_table[index]->Enabled == TRUE)
++ *oth_peer = rep_table[index];
++ else
++ *oth_peer = NULL;
++
++ }
++ }
++ }
++ FWD_IRQ_UNLOCK(&global_band_tbl_lock, global_band_tbl_irq_flags);
++
++}
++
++void wifi_fwd_insert_repeater_mapping(void *adapter, void *lock, void *cli_mapping, void *map_mapping, void *ifAddr_mapping)
++{
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ FWD_IRQ_LOCK(&global_band_tbl_lock, global_band_tbl_irq_flags);
++
++ if ((adapter == adapter_tmp_1)) {
++ global_map_tbl_1.EntryLock = lock;
++ global_map_tbl_1.CliHash = cli_mapping;
++ global_map_tbl_1.MapHash = map_mapping;
++ if ((global_map_tbl_1.Wdev_ifAddr ==NULL) ||
++ (global_map_tbl_1.Wdev_ifAddr == ifAddr_mapping))
++ global_map_tbl_1.Wdev_ifAddr = ifAddr_mapping;
++ else
++ global_map_tbl_1.Wdev_ifAddr_DBDC = ifAddr_mapping;
++ global_map_tbl_1.Enabled = TRUE;
++
++ } else if ((adapter == adapter_tmp_2)) {
++ global_map_tbl_2.EntryLock = lock;
++ global_map_tbl_2.CliHash = cli_mapping;
++ global_map_tbl_2.MapHash = map_mapping;
++ if ((global_map_tbl_2.Wdev_ifAddr ==NULL) ||
++ (global_map_tbl_2.Wdev_ifAddr == ifAddr_mapping))
++ global_map_tbl_2.Wdev_ifAddr = ifAddr_mapping;
++ else
++ global_map_tbl_2.Wdev_ifAddr_DBDC = ifAddr_mapping;
++ global_map_tbl_2.Enabled = TRUE;
++
++ } else if ((adapter == adapter_tmp_3)) {
++ global_map_tbl_3.EntryLock = lock;
++ global_map_tbl_3.CliHash = cli_mapping;
++ global_map_tbl_3.MapHash = map_mapping;
++ if ((global_map_tbl_3.Wdev_ifAddr ==NULL) ||
++ (global_map_tbl_3.Wdev_ifAddr == ifAddr_mapping))
++ global_map_tbl_3.Wdev_ifAddr = ifAddr_mapping;
++ else
++ global_map_tbl_3.Wdev_ifAddr_DBDC = ifAddr_mapping;
++
++ global_map_tbl_3.Enabled = TRUE;
++ }
++
++ FWD_IRQ_UNLOCK(&global_band_tbl_lock, global_band_tbl_irq_flags);
++}
++
++void wifi_fwd_insert_bridge_mapping(struct sk_buff *skb)
++{
++ struct net_device *net_dev = NULL;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *map_tbl_entry = NULL;
++ unsigned char pkt_from, tbl_size = 0;
++
++ net_dev = skb->dev;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if ((net_dev == ap_5g) || (net_dev == apcli_5g))
++ pkt_from = WIFI_FWD_PACKET_SPECIFIC_5G;
++ else
++ pkt_from = WIFI_FWD_PACKET_SPECIFIC_2G;
++
++ FWD_IRQ_LOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++
++ if (global_map_tbl.entry_num < 0) {
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] no entry found in the list\n", __FUNCTION__));
++ return;
++ }
++
++ tbl_size = sizeof(struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT);
++ map_tbl_entry = (struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *) kmalloc(tbl_size, GFP_ATOMIC);
++
++ if (map_tbl_entry) {
++ memset(map_tbl_entry, 0, tbl_size);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] size of map_tbl_entry = %dbytes\n", __FUNCTION__, tbl_size));
++ } else
++ return;
++
++ map_tbl_entry->rcvd_net_dev = net_dev;
++ map_tbl_entry->entry_from = pkt_from;
++ memcpy(map_tbl_entry->src_addr, ((unsigned char *)skb->data)[6], ETH_ALEN);
++
++ if (global_map_tbl.entry_num == 0) {
++ global_map_tbl.pHead = map_tbl_entry;
++ global_map_tbl.pTail = map_tbl_entry;
++ map_tbl_entry->pBefore = NULL;
++ map_tbl_entry->pNext = NULL;
++ }
++ else if (global_map_tbl.entry_num > 0) {
++ global_map_tbl.pTail->pNext = map_tbl_entry;
++ map_tbl_entry->pBefore = global_map_tbl.pTail;
++ global_map_tbl.pTail = map_tbl_entry;
++ }
++
++ global_map_tbl.entry_num++;
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++
++ if (0) {
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] %s\n", __FUNCTION__, (pkt_from == WIFI_FWD_PACKET_SPECIFIC_5G ? "5G" : "2.4G")));
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] inserting mac addr = %02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__, PRINT_MAC(map_tbl_entry->src_addr)));
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] rcvd from %s\n", __FUNCTION__, WIFI_FWD_NETDEV_GET_DEVNAME(map_tbl_entry->rcvd_net_dev)));
++ }
++
++ return;
++}
++
++
++/*
++ return value:
++ 1: success
++ 0: fail
++*/
++static int wifi_fwd_search_mapping_table(struct sk_buff *skb, struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT **tbl_entry)
++{
++ struct net_device *net_dev = NULL;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *map_tbl_entry = NULL;
++ unsigned char pkt_from, idx = 0;
++
++ net_dev = skb->dev;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return 0;
++
++ if ((net_dev == ap_5g) || (net_dev == apcli_5g))
++ pkt_from = WIFI_FWD_PACKET_SPECIFIC_5G;
++ else
++ pkt_from = WIFI_FWD_PACKET_SPECIFIC_2G;
++
++ FWD_IRQ_LOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++
++ if (global_map_tbl.entry_num <= 0) {
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] no entry found in the list\n", __FUNCTION__));
++ return 0;
++ }
++ else
++ {
++ if (global_map_tbl.pHead != NULL) {
++ map_tbl_entry = global_map_tbl.pHead;
++ }
++ else
++ {
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ return 0;
++ }
++
++ for (idx=0; idx<global_map_tbl.entry_num; idx++)
++ {
++ if (MAC_ADDR_EQUAL(map_tbl_entry->src_addr, ((unsigned char *)skb->data)[6]) &&
++ (net_dev != map_tbl_entry->rcvd_net_dev))
++ {
++ if (map_tbl_entry->entry_from == WIFI_FWD_PACKET_SPECIFIC_5G) {
++ /* indicate this entry exist in dual band. packets sending to this entry need to be monitored */
++ map_tbl_entry->entry_from |= pkt_from;
++ map_tbl_entry->rcvd_net_dev = net_dev;
++ }
++ else
++ {
++ /* make sure the net device reported in the packet is up */
++ if (dev_get_flags(map_tbl_entry->rcvd_net_dev) & IFF_UP) {
++ map_tbl_entry->entry_from |= pkt_from; /* indicate this entry exist in dual band. packet need to send to this */
++ SET_OS_PKT_NETDEV(RTPKT_TO_OSPKT(skb), map_tbl_entry->rcvd_net_dev); /* change net_device of packet to 2G */
++ }
++ }
++
++ *tbl_entry = map_tbl_entry;
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ return 1;
++ }
++ else if (MAC_ADDR_EQUAL(map_tbl_entry->src_addr, ((unsigned char *)skb->data)[6]) &&
++ (net_dev == map_tbl_entry->rcvd_net_dev))
++ {
++ *tbl_entry = map_tbl_entry;
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ return 1;
++ }
++
++ map_tbl_entry = map_tbl_entry->pNext;
++ }
++
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ }
++
++ return 0;
++}
++
++
++/*
++ return value:
++ 1: Allocate Success
++ 0: Allocate FAIL
++*/
++static int wifi_fwd_alloc_tbl(unsigned int NumOfEntry)
++{
++ unsigned int TblSize = 0;
++
++ TblSize = NumOfEntry * sizeof(struct FwdPair);
++ WiFiFwdBase = (struct FwdPair *) kmalloc(TblSize, GFP_ATOMIC);
++ if (WiFiFwdBase) {
++ memset(WiFiFwdBase, 0, TblSize);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] size of WiFiFwdBase = %dbytes\n", __FUNCTION__, TblSize));
++ }
++ else
++ return 0;
++
++ TblSize = NumOfEntry * sizeof(struct PacketSource);
++ pkt_src = (struct PacketSource *) kmalloc(TblSize, GFP_ATOMIC);
++ if (pkt_src) {
++ memset(pkt_src, 0, TblSize);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] size of pkt_src = %dbytes\n", __FUNCTION__, TblSize));
++ }
++ else
++ return 0;
++
++ TblSize = NumOfEntry * sizeof(struct TxSourceEntry);
++ tx_src_tbl = (struct TxSourceEntry *) kmalloc(TblSize, GFP_ATOMIC);
++ if (tx_src_tbl) {
++ memset(tx_src_tbl, 0, TblSize);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] size of tx_src_tbl = %dbytes\n", __FUNCTION__, TblSize));
++ }
++ else
++ return 0;
++
++ TblSize = sizeof(struct APCLI_BRIDGE_LEARNING_MAPPING_MAP);
++ memset(&global_map_tbl, 0, TblSize);
++
++ TblSize = sizeof(struct _REPEATER_ADAPTER_DATA_TABLE);
++ memset(&global_map_tbl_1, 0, TblSize);
++ memset(&global_map_tbl_2, 0, TblSize);
++ memset(&global_map_tbl_3, 0, TblSize);
++
++ return 1;
++}
++
++static int wifi_fwd_alloc_device_info(unsigned int NumOfDevice)
++{
++ unsigned int DeviceSize = 0;
++
++ DeviceSize = NumOfDevice * sizeof(struct DevInfo);
++ FwdDevice = (struct DevInfo *) kmalloc(DeviceSize, GFP_ATOMIC);
++ if (FwdDevice) {
++ memset(FwdDevice, 0, DeviceSize);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] size of FwdDevice = %dbytes\n", __FUNCTION__, DeviceSize));
++ }
++ else
++ return 0;
++
++ return 1;
++}
++
++
++
++static unsigned char wifi_fwd_check_and_forward(struct sk_buff *skb)
++{
++ struct ethhdr *mh = NULL;
++ struct iphdr *iph = NULL;
++ struct udphdr *uh = NULL;
++ struct tcphdr *th = NULL;
++ struct ipv6hdr *ipv6h = NULL;
++ void *ptr = NULL;
++ unsigned short type = 0;
++
++ mh = eth_hdr(skb);
++ if (mh)
++ {
++ type = ntohs(mh->h_proto);
++ switch(type)
++ {
++ /*
++ Forward LLTD EthType: 0x88D9
++ */
++ case LLTD_ETH_TYPE:
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("Forward - LLTD_ETH_TYPE: 0x%02x\n", type));
++#endif
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE))
++ return TRUE;
++ break;
++
++ /*
++ Forward ARP EthType: 0x0806
++ */
++ case ARP_ETH_TYPE:
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("Forward - ARP_ETH_TYPE: 0x%02x\n", type));
++#endif
++ if (WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE))
++ return TRUE;
++ break;
++
++ case ETH_P_IP:
++ iph = (struct iphdr *)(skb->data);
++#if 0
++ hex_dump_("Data", skb->data, (iph->ihl<<2));
++#endif
++ if (iph)
++ {
++ ptr = (void *)(skb->data+(iph->ihl<<2));
++ if (ptr)
++ {
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("wifi_fwd_check_and_forward - iph->protocol: 0x%02x\n", iph->protocol));
++#endif
++ switch(iph->protocol)
++ {
++ case UDP:
++ /*
++ Forward UDP port 53 and 67
++ */
++ uh = (struct udphdr*)(ptr);
++ if ((ntohs(uh->source) == 53) || (ntohs(uh->dest) == 53) ||
++ (ntohs(uh->source) == 67) || (ntohs(uh->dest) == 67))
++ {
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("Forward - udp source port: %d, dest port: %d\n", ntohs(uh->source), ntohs(uh->dest)));
++#endif
++ return TRUE;
++ }
++ break;
++
++ case TCP:
++ /*
++ Forward TCP port 80 and 5000
++ */
++ th = (struct tcphdr *)(ptr);
++ if ((ntohs(th->source) == 80) ||
++ (ntohs(th->dest) == 80) ||
++ (ntohs(th->source) == 5000) ||
++ (ntohs(th->dest) == 5000))
++ {
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("Forward - tcp source port: %d, dest port: %d\n", ntohs(th->source), ntohs(th->dest)));
++#endif
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE))
++ return TRUE;
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++ }
++ break;
++
++ case ETH_P_IPV6:
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("IPv6: IPV6_HDR_LEN = %d\n", IPV6_HDR_LEN));
++ hex_dump_("Data", skb->data, LENGTH_802_3+IPV6_HDR_LEN);
++#endif
++ ipv6h = (struct ipv6hdr *)(skb->data);
++ if (ipv6h)
++ {
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("ipv6h->version = 0x%x\n", ipv6h->version));
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("ipv6h->nexthdr = 0x%x\n", ipv6h->nexthdr));
++#endif
++ ptr = (void *)(skb->data+IPV6_HDR_LEN);
++ if (ptr)
++ {
++ switch(ipv6h->nexthdr)
++ {
++ /*
++ Forward IPv6 UDP port 53
++ */
++ case IPV6_NEXT_HEADER_UDP:
++ uh = (struct udphdr*)(ptr);
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("udp source port: %d, dest port: %d\n", ntohs(uh->source), ntohs(uh->dest)));
++#endif
++ if ((ntohs(uh->source) == 53) || (ntohs(uh->dest) == 53))
++ {
++ return TRUE;
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++
++ return FALSE;
++}
++
++/*
++ return value:
++ 0: return to driver and handle
++ 1: return to driver and release
++*/
++static int wifi_fwd_tx_handler(struct sk_buff *skb)
++{
++ struct net_device *net_dev = NULL;
++ unsigned char ret = 0;
++ net_dev = skb->dev;
++
++ /*
++ return this skb to driver and handle while:
++ 1. path of WiFi forwarding is inactive
++ 2. the skb does not exist
++ 3. no forwarding connection is established
++ */
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACTIVE)
++ || !WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED)
++ || !skb
++ || (fwd_counter == 0))
++ return 0;
++
++ ret = wifi_fwd_tx_lookup_entry(net_dev, skb);
++ return ret;
++}
++
++/*
++ return value:
++ 0: skb is handled by wifi_fwd module
++ 1: return to driver and handle
++ 2: return to driver and release
++*/
++static int wifi_fwd_rx_handler(struct sk_buff *skb)
++{
++ struct net_device *net_dev = NULL;
++ struct net_device *target = NULL;
++ struct ethhdr *mh = eth_hdr(skb);
++ struct DevInfo *device_info = NULL;
++ unsigned char rep = 0, type = ENTRY_TYPE_INVALID;
++ unsigned int recv_from = 0, band_from = 0, ret = 0;
++ int idx = 0;
++
++ net_dev = skb->dev;
++ recv_from = WIFI_FWD_GET_PACKET_RECV_FROM(skb, recv_from_cb_offset);
++ band_from = WIFI_FWD_GET_PACKET_BAND(skb, band_cb_offset);
++
++ /*
++ return this skb to driver and bridge while:
++ 1. path of WiFi forwarding is inactive
++ 2. the skb does not exist
++ 3. no forwarding connection is established
++ 4. the destination is to bridge or ethernet
++ 5. in FastLane topology
++ 6. handling multicast/broadcast Rx
++ 7. hit hijack
++ */
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACTIVE)
++ || !WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED)
++ || !skb
++ || (fwd_counter == 0))
++ return 1;
++
++ /* drop the packet due to there's no forwarding links between ap-client and ap */
++ if (IS_PACKET_FROM_APCLI(recv_from) && !MAIN_SSID_OP(net_dev))
++ return 2;
++
++ /* handle access schedule */
++ if (WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE))
++ {
++ if (IS_PACKET_FROM_APCLI(recv_from))
++ {
++ if (wifi_fwd_check_and_forward(skb) == FALSE)
++ return 2;
++ }
++ }
++
++ /* forward the packet to bridge no matter unicast or broadcast */
++ if (MAC_ADDR_EQUAL(mh->h_dest, br_lan->dev_addr))
++ return 1;
++
++ /*
++ in FastLane topology:
++ 1. forward Rx packets to bridge and let flooding work done by bridge
++ 2. no matter unicast or multicast/broadcast
++ or in MBSS support
++ */
++ if (is_fastlane_mode(net_dev) || is_mbss_mode(band_from))
++ return 1;
++
++ idx = wifi_fwd_find_device(net_dev);
++ if (idx < 0)
++ {
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s][%d] cannot find device information\n", __FUNCTION__, __LINE__));
++ return 1;
++ }
++
++ device_info = FwdDevice + idx;
++
++ /*
++ prevent from looping
++ 0: no looping
++ 1: looping found and need to drop the packet
++ 2: forward to bridge and handle
++ */
++ ret = wifi_fwd_check_looping(skb, device_info);
++ if (ret == 1)
++ return 2;
++ else if (ret == 2)
++ return 1;
++
++ /* after checking looping, handle multicast/broadcast Rx */
++ if ((mh->h_dest[0] & 0x1) == 0x1)
++ return 1;
++
++ /* handle hijack */
++ if (WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_HIJACK_ACTIVE))
++ {
++ if (wifi_fwd_check_and_forward(skb) == TRUE)
++ {
++ WIFI_FWD_SET_PACKET_BAND(skb,WIFI_FWD_PACKET_SPECIFIC_TAG, band_cb_offset);
++ return 1;
++ }
++ }
++
++ /* forward the packet to bridge by comparing destination address @ concurrent mode */
++ if (wifi_fwd_pkt_to_bridge_by_dest_addr(skb, net_dev))
++ return 1;
++
++ target = wifi_fwd_lookup_entry(net_dev, &type, skb);
++
++ /* handle unicast Rx for non-FastLane cases */
++ if (target != NULL)
++ {
++ /* prevent from looping */
++ if (wifi_fwd_find_sa_entry_by_sa_and_nd(skb, net_dev, 0) != 0)
++ {
++ unsigned char hit = 0;
++
++ /* forward to bridge if it is not a WiFi net device back-end packet */
++ hit = wifi_fwd_check_from_bridge_by_dest_addr(skb, device_info);
++
++ if (hit == 0)
++ {
++ /* this packet should be forwarded to bridge due to the destination is addessing to bridge or ethernet */
++ if (is_concurrent_mode(net_dev) && IS_PACKET_FROM_ETHER(band_from))
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s][%d] this case is not reasonable\n", __FUNCTION__, __LINE__));
++
++ return 1;
++ }
++ else if (hit == 2)
++ {
++ /* match inner communication case */
++ return 1;
++ }
++ else
++ {
++ /* this packet should be delivered to WiFi interface and transmitted directly */
++ skb_push(skb, ETH_HLEN);
++ skb->dev = target;
++ dev_queue_xmit(skb);
++ return 0;
++ }
++
++ return 1;
++ }
++ else
++ return 2;
++ }
++ else
++ return 1;
++}
++
++
++static int wifi_fwd_init_mod(void)
++{
++ if (!wifi_fwd_alloc_tbl(WIFI_FWD_TBL_SIZE)) {
++ return -ENOMEM; /* memory allocation failed */
++ }
++
++ if (!wifi_fwd_alloc_device_info(WIFI_FWD_DEVICE_SIZE)) {
++ return -ENOMEM; /* memory allocation failed */
++ }
++
++ WIFI_FWD_CLEAR_FLAG(fOP_GET_NET_DEVICE_STATUS_DONE);
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE);
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ENABLED);
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ACTIVE);
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_HIJACK_ACTIVE);
++
++ wifi_fwd_reset_link_count();
++
++ eth_rep5g_wrg_uni_cnt = 0;
++ eth_rep5g_wrg_bct_cnt = 0;
++ eth_rep2g_wrg_uni_cnt = 0;
++ eth_rep2g_wrg_bct_cnt = 0;
++ rep_net_dev = eth_traffic_band_5g;
++ next_2g_band = band_5g;
++ band_cb_offset = DEFAULT_BAND_CB_OFFSET;
++ recv_from_cb_offset = DEFAULT_RECV_FROM_CB_OFFSET;
++ wf_debug_level = WF_DEBUG_OFF;
++
++ wf_fwd_tx_hook = wifi_fwd_tx_handler;
++ wf_fwd_rx_hook = wifi_fwd_rx_handler;
++ wf_fwd_entry_insert_hook = wifi_fwd_insert_entry;
++ wf_fwd_entry_delete_hook = wifi_fwd_delete_entry;
++ wf_fwd_set_cb_num = wifi_fwd_set_cb_num;
++ wf_fwd_check_active_hook = wifi_fwd_check_active;
++ wf_fwd_get_rep_hook = wifi_fwd_get_rep;
++ wf_fwd_pro_active_hook = wifi_fwd_pro_active;
++ wf_fwd_pro_halt_hook = wifi_fwd_pro_halt;
++ wf_fwd_pro_enabled_hook = wifi_fwd_pro_enabled;
++ wf_fwd_pro_disabled_hook = wifi_fwd_pro_disabled;
++ wf_fwd_access_schedule_active_hook = wifi_fwd_access_schedule_active;
++ wf_fwd_access_schedule_halt_hook = wifi_fwd_access_schedule_halt;
++ wf_fwd_hijack_active_hook = wifi_fwd_hijack_active;
++ wf_fwd_hijack_halt_hook = wifi_fwd_hijack_halt;
++ wf_fwd_show_entry_hook = wifi_fwd_show_entry;
++ wf_fwd_needed_hook = wifi_fwd_needed;
++ wf_fwd_delete_entry_hook = wifi_fwd_delete_entry_by_idx;
++ packet_source_show_entry_hook = packet_source_show_entry;
++ packet_source_delete_entry_hook = packet_source_delete_entry;
++ wf_fwd_feedback_map_table = wifi_fwd_feedback_map_table;
++ wf_fwd_probe_adapter = wifi_fwd_probe_adapter;
++ wf_fwd_insert_bridge_mapping_hook = wifi_fwd_insert_bridge_mapping;
++ wf_fwd_insert_repeater_mapping_hook = wifi_fwd_insert_repeater_mapping;
++ wf_fwd_search_mapping_table_hook = wifi_fwd_search_mapping_table;
++ wf_fwd_delete_entry_inform_hook = wf_fwd_delete_entry_inform;
++ wf_fwd_check_device_hook = wifi_fwd_check_device;
++ wf_fwd_add_entry_inform_hook = wf_fwd_add_entry_inform;
++
++ return 0;
++}
++
++
++static void wifi_fwd_cleanup_mod(void)
++{
++ if (WiFiFwdBase)
++ kfree(WiFiFwdBase);
++ WiFiFwdBase = NULL;
++
++ if (FwdDevice)
++ kfree(FwdDevice);
++ FwdDevice = NULL;
++
++ if (pkt_src)
++ kfree(pkt_src);
++ pkt_src = NULL;
++
++ if(tx_src_tbl)
++ kfree(tx_src_tbl);
++ tx_src_tbl = NULL;
++
++ wf_fwd_tx_hook = NULL;
++ wf_fwd_rx_hook = NULL;
++ wf_fwd_entry_insert_hook = NULL;
++ wf_fwd_entry_delete_hook = NULL;
++ wf_fwd_set_cb_num = NULL;
++ wf_fwd_check_active_hook = NULL;
++ wf_fwd_get_rep_hook = NULL;
++ wf_fwd_pro_active_hook = NULL;
++ wf_fwd_pro_halt_hook = NULL;
++ wf_fwd_pro_enabled_hook = NULL;
++ wf_fwd_pro_disabled_hook = NULL;
++ wf_fwd_access_schedule_active_hook = NULL;
++ wf_fwd_access_schedule_halt_hook = NULL;
++ wf_fwd_hijack_active_hook = NULL;
++ wf_fwd_hijack_halt_hook = NULL;
++ wf_fwd_show_entry_hook = NULL;
++ wf_fwd_needed_hook = NULL;
++ wf_fwd_delete_entry_hook = NULL;
++ packet_source_show_entry_hook = NULL;
++ packet_source_delete_entry_hook = NULL;
++ wf_fwd_feedback_map_table = NULL;
++ wf_fwd_probe_adapter = NULL;
++ wf_fwd_insert_bridge_mapping_hook = NULL;
++ wf_fwd_insert_repeater_mapping_hook = NULL;
++ wf_fwd_search_mapping_table_hook = NULL;
++ wf_fwd_delete_entry_inform_hook = NULL;
++ wf_fwd_check_device_hook = NULL;
++ wf_fwd_add_entry_inform_hook = NULL;
++ wf_fwd_debug_level_hook = NULL;
++
++ return;
++}
++
++
++module_init(wifi_fwd_init_mod);
++module_exit(wifi_fwd_cleanup_mod);
++
++MODULE_AUTHOR("MediaTek Inc");
++MODULE_LICENSE("Proprietary");
++MODULE_DESCRIPTION("MediaTek WiFi Packet Forwarding Module\n");
++
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd/wifi_fwd.h
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd/wifi_fwd.h
+@@ -0,0 +1,319 @@
++/****************************************************************************
++ * Mediatek Inc.
++ * 5F., No.5, Taiyuan 1st St., Zhubei City,
++ * Hsinchu County 302, Taiwan, R.O.C.
++ * (c) Copyright 2014, Mediatek, Inc.
++ *
++ * All rights reserved. Ralink's source code is an unpublished work and the
++ * use of a copyright notice does not imply otherwise. This source code
++ * contains confidential trade secret material of Ralink Tech. Any attemp
++ * or participation in deciphering, decoding, reverse engineering or in any
++ * way altering the source code is stricitly prohibited, unless the prior
++ * written consent of Ralink Technology, Inc. is obtained.
++ ****************************************************************************
++
++ Module Name:
++ wifi_fwd.h
++
++ Abstract:
++
++ Revision History:
++ Who When What
++ -------- ---------- ----------------------------------------------
++ Annie Lu 2014-06-30 Initial version
++*/
++
++#ifndef __WF_FWD_H__
++#define __WF_FWD_H__
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/kernel.h>
++#include <linux/net.h>
++#include <linux/netdevice.h>
++#include <linux/ip.h>
++#include <linux/ipv6.h>
++#include <linux/udp.h>
++#include <linux/tcp.h>
++
++#ifndef TRUE
++#define TRUE 1
++#endif
++
++#ifndef FALSE
++#define FALSE 0
++#endif
++
++/* enternal symbol */
++extern int (*wf_fwd_rx_hook) (struct sk_buff *skb);
++extern int (*wf_fwd_tx_hook) (struct sk_buff *skb);
++
++extern unsigned char (*wf_fwd_entry_insert_hook) (struct net_device *src, struct net_device *dest, void *adapter);
++extern unsigned char (*wf_fwd_entry_delete_hook) (struct net_device *src, struct net_device *dest, unsigned char link_down);
++extern void (*wf_fwd_set_cb_num) (unsigned int band_cb_num, unsigned int receive_cb_num);
++extern bool (*wf_fwd_check_active_hook) (void);
++extern void (*wf_fwd_get_rep_hook) (unsigned char idx);
++extern void (*wf_fwd_pro_active_hook) (void);
++extern void (*wf_fwd_pro_halt_hook) (void);
++extern void (*wf_fwd_pro_enabled_hook) (void);
++extern void (*wf_fwd_pro_disabled_hook) (void);
++extern void (*wf_fwd_access_schedule_active_hook) (void);
++extern void (*wf_fwd_access_schedule_halt_hook) (void);
++extern void (*wf_fwd_hijack_active_hook) (void);
++extern void (*wf_fwd_hijack_halt_hook) (void);
++extern void (*wf_fwd_show_entry_hook) (void);
++extern bool (*wf_fwd_needed_hook) (void);
++extern void (*wf_fwd_delete_entry_hook) (unsigned char idx);
++extern void (*packet_source_show_entry_hook) (void);
++extern void (*packet_source_delete_entry_hook) (unsigned char idx);
++extern void (*wf_fwd_feedback_map_table) (void *adapter, void *peer, void *opp_peer, void *oth_peer);
++extern void (*wf_fwd_probe_adapter) (void *adapter);
++extern void (*wf_fwd_insert_repeater_mapping_hook)(void *adapter, void *lock, void *cli_mapping, void *map_mapping, void *ifAddr_mapping);
++extern void (*wf_fwd_insert_bridge_mapping_hook) (struct sk_buff *skb);
++extern int (*wf_fwd_search_mapping_table_hook) (struct sk_buff *skb, struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT **tbl_entry);
++extern void (*wf_fwd_delete_entry_inform_hook) (unsigned char *addr);
++extern void (*wf_fwd_check_device_hook) ( struct net_device *net_dev, signed int type, signed int mbss_idx, unsigned char channel, unsigned char link);
++extern void (*wf_fwd_add_entry_inform_hook) (unsigned char *addr);
++extern void (*wf_fwd_debug_level_hook) (unsigned char level);
++
++#ifndef ETH_ALEN
++#define ETH_ALEN 6
++#endif
++#define MAC_ADDR_LEN 6
++
++#define H_CHANNEL_BIGGER_THAN 144
++#define fOP_GET_NET_DEVICE_STATUS_DONE 0x00000001
++#define fOP_WIFI_FWD_ENABLED 0x00000002
++#define fOP_WIFI_FWD_ACTIVE 0x00000010
++#define fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE 0x00000100
++#define fOP_WIFI_FWD_HIJACK_ACTIVE 0x00001000
++
++#define WIFI_FWD_SET_FLAG(_F) (wifi_fwd_op_flag |= (_F))
++#define WIFI_FWD_CLEAR_FLAG(_F) (wifi_fwd_op_flag &= ~(_F))
++#define WIFI_FWD_CLEAR_FLAGS (wifi_fwd_op_flag = 0)
++#define WIFI_FWD_TEST_FLAG(_F) ((wifi_fwd_op_flag & (_F)) != 0)
++#define WIFI_FWD_TEST_FLAGS(_F) ((wifi_fwd_op_flag & (_F)) == (_F))
++
++#define CMP_TO_REP(_p, _x) (REP_IS_##_x##G(_p))
++
++/* sanity check for apidx */
++#define REP_IS_5G_H(_p) ((_p) == band_5g_h)
++#define REP_IS_5G(_p) ((_p) == band_5g)
++#define REP_IS_2G(_p) ((_p) == band_2g)
++
++#define IS_UNICAST_PACKET(_p) ((((unsigned char *)(_p)->data)[0] & 0x1) == 0x0)
++#define IS_BROADCAST_PACKET(_p) ((((unsigned char *)(_p)->data)[0] & 0x1) == 0x1)
++
++#define IS_AP_DEV(_p) ((_p) & (INT_MAIN | INT_MBSSID))
++#define IS_APCLI_DEV(_p) ((_p) & INT_APCLI)
++#define IS_5G_HIGH(_p) ((_p) > H_CHANNEL_BIGGER_THAN)
++#define IS_VALID(_p) ((_p) == TRUE)
++
++#define MAIN_SSID_OP(_p) ((main_2g_link_cnt != 0) || (main_5g_link_cnt != 0) || (main_5g_h_link_cnt != 0))
++
++#define WIFI_FWD_TBL_SIZE 50
++#define WIFI_FWD_DEVICE_SIZE 34 /* currently max number of MBSS support */
++
++#define ENTRY_TYPE_INVALID 0
++#define ENTRY_TYPE_SRC 1
++#define ENTRY_TYPE_DEST 2
++
++/* debug macro */
++#define dbg_print printk
++#define WF_DEBUG_OFF 0
++#define WF_DEBUG_TRACE 1
++#define WF_DEBUG_ON 2
++
++extern unsigned char wf_debug_level;
++
++/* sync from WiFi driver */
++#define INT_MAIN 0x0100
++#define INT_MBSSID 0x0200
++#define INT_APCLI 0x0400
++
++#define WF_FWD_PRINT(Level, Fmt) \
++ do{ \
++ unsigned char __gLevel = (Level); \
++ if (__gLevel <= wf_debug_level) \
++ { \
++ printk Fmt; \
++ } \
++ }while(0)
++
++/* CB related */
++#define CB_OFF 10
++#define RTPKT_TO_OSPKT(_p) ((struct sk_buff *)(_p))
++#define GET_OS_PKT_CB(_p) (RTPKT_TO_OSPKT(_p)->cb)
++#define PACKET_CB(_p, _offset) ((RTPKT_TO_OSPKT(_p)->cb[CB_OFF + (_offset)]))
++
++/* [CB_OFF + 34]: tag the packet is sent by which band */
++#define DEFAULT_BAND_CB_OFFSET 34
++#define WIFI_FWD_PACKET_SPECIFIC_2G 0x1
++#define WIFI_FWD_PACKET_SPECIFIC_5G 0x2
++#define WIFI_FWD_PACKET_SPECIFIC_ETHER 0x4
++#define WIFI_FWD_PACKET_SPECIFIC_TAG 0x8
++#define WIFI_FWD_PACKET_SPECIFIC_5G_H 0x10
++
++#define WIFI_FWD_SET_PACKET_BAND(_p, _offset, _flg) \
++ do{ \
++ if (_flg) \
++ PACKET_CB(_p, _offset) |= (_flg); \
++ else \
++ PACKET_CB(_p, _offset) &= (~_flg); \
++ }while(0)
++
++#define WIFI_FWD_GET_PACKET_BAND(_p, _offset) (PACKET_CB(_p, _offset))
++
++/* [CB_OFF + 35]: tag the packet received from which net device */
++#define DEFAULT_RECV_FROM_CB_OFFSET 35
++#define WIFI_FWD_PACKET_RECV_FROM_2G_CLIENT 0x01
++#define WIFI_FWD_PACKET_RECV_FROM_5G_CLIENT 0x02
++#define WIFI_FWD_PACKET_RECV_FROM_2G_AP 0x04
++#define WIFI_FWD_PACKET_RECV_FROM_5G_AP 0x08
++#define WIFI_FWD_PACKET_RECV_FROM_5G_H_CLIENT 0x10
++#define WIFI_FWD_PACKET_RECV_FROM_5G_H_AP 0x20
++
++#if 0
++#define WIFI_FWD_PACKET_RECV_FROM_2G_GUEST_CLIENT 0x10
++#define WIFI_FWD_PACKET_RECV_FROM_5G_GUEST_CLIENT 0x20
++#define WIFI_FWD_PACKET_RECV_FROM_2G_GUEST_AP 0x40
++#define WIFI_FWD_PACKET_RECV_FROM_5G_GUEST_AP 0x80
++#endif
++
++#define WIFI_FWD_SET_PACKET_RECV_FROM(_p, _offset, _flg) \
++ do{ \
++ if (_flg) \
++ PACKET_CB(_p, _offset) |= (_flg); \
++ else \
++ PACKET_CB(_p, _offset) &= (~_flg); \
++ }while(0)
++
++#define WIFI_FWD_GET_PACKET_RECV_FROM(_p, _offset) (PACKET_CB(_p, _offset))
++
++#define IS_PACKET_FROM_APCLI(_x) \
++ ((((_x) & WIFI_FWD_PACKET_RECV_FROM_2G_CLIENT) == WIFI_FWD_PACKET_RECV_FROM_2G_CLIENT) ||\
++ (((_x) & WIFI_FWD_PACKET_RECV_FROM_5G_CLIENT) == WIFI_FWD_PACKET_RECV_FROM_5G_CLIENT) ||\
++ (((_x) & WIFI_FWD_PACKET_RECV_FROM_5G_H_CLIENT) == WIFI_FWD_PACKET_RECV_FROM_5G_H_CLIENT))
++
++#define IS_PACKET_FROM_2G(_x) \
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_2G) == WIFI_FWD_PACKET_SPECIFIC_2G)
++
++#define IS_PACKET_FROM_5G(_x) \
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_5G) == WIFI_FWD_PACKET_SPECIFIC_5G)
++
++#define IS_PACKET_FROM_5G_H(_x) \
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_5G_H) == WIFI_FWD_PACKET_SPECIFIC_5G_H)
++
++#define IS_TAG_PACKET(_x) \
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_TAG) == WIFI_FWD_PACKET_SPECIFIC_TAG)
++
++#define IS_PACKET_FROM_ETHER(_x) \
++ ((((_x) & WIFI_FWD_PACKET_SPECIFIC_2G) != WIFI_FWD_PACKET_SPECIFIC_2G) &&\
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_5G) != WIFI_FWD_PACKET_SPECIFIC_5G) &&\
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_5G_H) != WIFI_FWD_PACKET_SPECIFIC_5G_H))
++
++#define IS_PACKET_FROM_MAIN_SSID(_x) \
++ (((_x) & 0x0F) != 0x0)
++
++#define IS_PACKET_FROM_GUEST_SSID(_x) \
++ (((_x) & 0x0F) == 0x0)
++
++
++typedef enum _ETHER_BAND_BINDDING {
++ eth_traffic_band_2g = 2,
++ eth_traffic_band_5g = 5,
++ eth_traffic_band_5g_H = 6,
++} ETHER_BAND_BINDDING, *PETHER_BAND_BINDDING;
++
++typedef enum _BAND_INDEX {
++ band_2g = 2,
++ band_5g = 5,
++ band_5g_h = 6,
++} BAND_INDEX;
++
++/* data structure */
++struct FwdPair {
++ unsigned char valid; /* 1: valid, 0: invalid */
++ struct net_device *src;
++ struct net_device *dest;
++};
++
++struct PacketSource {
++ unsigned char valid; /* 1: valid, 0: invalid */
++ struct net_device *peer;
++ struct net_device *src;
++ unsigned char h_source[ETH_ALEN];
++ unsigned char h_dest[ETH_ALEN];
++};
++
++/*
++ mainly to avoid looping caused by lan stations
++ 1. insert entries while (1) mac table insert entry @ WiFi driver (2) wifi_fwd_tx_handler @ forwarding module
++ 2. check while receiving packets
++*/
++struct TxSourceEntry {
++ unsigned char valid; /* 1: valid, 0: invalid */
++ unsigned char bfrom_wireless; /* 1: from wireless, 0: from ethernet */
++ unsigned char h_source[ETH_ALEN];
++};
++
++struct DevInfo {
++ unsigned char valid; /* 1: valid, 0: invalid */
++ unsigned char index;
++ unsigned int type; /* ap or ap-client */
++ int mbss_idx; /* for ap mode */
++ BAND_INDEX band;
++ BAND_INDEX second_band;
++ BAND_INDEX third_band;
++ struct net_device *dev;
++ struct DevInfo *partner;
++};
++
++struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT {
++ struct net_device *rcvd_net_dev;
++ unsigned char src_addr[ETH_ALEN];
++ unsigned char entry_from;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *pBefore;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *pNext;
++};
++
++struct APCLI_BRIDGE_LEARNING_MAPPING_MAP {
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *pHead;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *pTail;
++ unsigned int entry_num;
++};
++
++typedef struct _REPEATER_ADAPTER_DATA_TABLE {
++ bool Enabled;
++ void *EntryLock;
++ void **CliHash;
++ void **MapHash;
++ void *Wdev_ifAddr;
++ void *Wdev_ifAddr_DBDC;
++} REPEATER_ADAPTER_DATA_TABLE;
++
++
++#define FWD_IRQ_LOCK(__lock, __irqflags) \
++{ \
++ __irqflags = 0; \
++ spin_lock_irqsave((spinlock_t *)(__lock), __irqflags); \
++}
++
++#define FWD_IRQ_UNLOCK(__lock, __irqflag) \
++{ \
++ spin_unlock_irqrestore((spinlock_t *)(__lock), __irqflag); \
++}
++
++#define PRINT_MAC(addr) \
++ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
++
++#define MAC_ADDR_EQUAL(addr1,addr2) (!memcmp((void *)(addr1), (void *)(addr2), ETH_ALEN))
++#define COPY_MAC_ADDR(addr1, addr2) (memcpy((addr1), (addr2), ETH_ALEN))
++
++#define WIFI_FWD_NETDEV_GET_DEVNAME(_pNetDev) ((_pNetDev)->name)
++#define WIFI_FWD_NETDEV_GET_PHYADDR(_pNetDev) ((_pNetDev)->dev_addr)
++
++#define SET_OS_PKT_NETDEV(_pkt, _pNetDev) \
++ (RTPKT_TO_OSPKT(_pkt)->dev) = (_pNetDev)
++#endif
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd_v1/Kconfig
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd_v1/Kconfig
+@@ -0,0 +1 @@
++
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd_v1/Makefile
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd_v1/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_WIFI_PKT_FWD) += wifi_fwd.o
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd_v1/wifi_fwd.c
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd_v1/wifi_fwd.c
+@@ -0,0 +1,2174 @@
++/****************************************************************************
++ * Mediatek Inc.
++ * 5F., No.5, Taiyuan 1st St., Zhubei City,
++ * Hsinchu County 302, Taiwan, R.O.C.
++ * (c) Copyright 2014, Mediatek, Inc.
++ *
++ * All rights reserved. Ralink's source code is an unpublished work and the
++ * use of a copyright notice does not imply otherwise. This source code
++ * contains confidential trade secret material of Ralink Tech. Any attemp
++ * or participation in deciphering, decoding, reverse engineering or in any
++ * way altering the source code is stricitly prohibited, unless the prior
++ * written consent of Ralink Technology, Inc. is obtained.
++ ****************************************************************************
++
++ Module Name:
++ wifi_fwd.c
++
++ Abstract:
++
++ Revision History:
++ Who When What
++ -------- ---------- ----------------------------------------------
++ Annie Lu 2014-06-30 Initial version
++ */
++
++#include "wifi_fwd.h"
++
++struct net_device *ap_2g = NULL, *apcli_2g = NULL, *ap_5g = NULL, *apcli_5g = NULL, *br_lan = NULL;
++struct net_device *ap1_2g = NULL, *apcli1_2g = NULL, *ap1_5g = NULL, *apcli1_5g = NULL;
++struct FwdPair *WiFiFwdBase = NULL;
++struct PacketSource *pkt_src = NULL;
++struct TxSourceEntry *tx_src_tbl = NULL;
++unsigned int tx_src_count = 0;
++
++void * adapter_2g = NULL;
++void * adapter_5g = NULL;
++void * adapter_tmp_1 = NULL;
++void * adapter_tmp_2 = NULL;
++
++unsigned char wf_debug_level;
++static unsigned long wifi_fwd_op_flag;
++static unsigned char rep_net_dev;
++static unsigned char fwd_counter;
++static signed char main_5g_link_cnt;
++static signed char main_2g_link_cnt;
++static signed char guest_5g_link_cnt;
++static signed char guest_2g_link_cnt;
++
++static unsigned int eth_rep5g_wrg_uni_cnt;
++static unsigned int eth_rep5g_wrg_bct_cnt;
++static unsigned int eth_rep2g_wrg_uni_cnt;
++static unsigned int eth_rep2g_wrg_bct_cnt;
++
++static unsigned int band_cb_offset;
++static unsigned int recv_from_cb_offset;
++
++REPEATER_ADAPTER_DATA_TABLE global_map_2g_tbl , global_map_5g_tbl;
++
++struct APCLI_BRIDGE_LEARNING_MAPPING_MAP global_map_tbl;
++spinlock_t global_map_tbl_lock;
++unsigned long global_map_tbl_irq_flags;
++spinlock_t global_band_tbl_lock;
++unsigned long global_band_tbl_irq_flags;
++
++
++#define ARP_ETH_TYPE 0x0806
++#define LLTD_ETH_TYPE 0x88D9
++
++#ifndef TCP
++#define TCP 0x06
++#endif /* !TCP */
++
++#ifndef UDP
++#define UDP 0x11
++#endif /* !UDP */
++
++#ifndef ETH_P_IPV6
++#define ETH_P_IPV6 0x86DD
++#endif /* !ETH_P_IPV6 */
++
++#ifndef ETH_P_IP
++#define ETH_P_IP 0x0800 /* Internet Protocol packet */
++#endif /* ETH_P_IP */
++
++#ifndef LENGTH_802_3
++#define LENGTH_802_3 14
++#endif /* !LENGTH_802_3 */
++
++#define IPV6_NEXT_HEADER_UDP 0x11
++#define IPV6_HDR_LEN 40
++
++struct net_device *ra_dev_get_by_name(const char *name)
++{
++ struct net_device *dev;
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
++ dev = dev_get_by_name(&init_net, name);
++#else
++ dev = dev_get_by_name(name);
++#endif
++
++ if (dev)
++ dev_put(dev);
++
++ return dev;
++}
++
++
++static unsigned char is_wifi_fastlane_mode(struct net_device *dev)
++{
++ if (IS_MAIN_SSID_DEV(dev)) {
++ if (((main_2g_link_cnt >= 2) && (main_5g_link_cnt == 0)) ||
++ ((main_5g_link_cnt >= 2) && (main_2g_link_cnt == 0)))
++ return 1;
++ else
++ return 0;
++ } else if (IS_GUEST_SSID_DEV(dev)) {
++ if ((guest_2g_link_cnt >= 2) || (guest_5g_link_cnt >= 2))
++ return 1;
++ else
++ return 0;
++ }
++}
++
++
++static unsigned char is_wifi_concurrent_mode(struct net_device *dev)
++{
++ if (IS_MAIN_SSID_DEV(dev)) {
++ if (((main_2g_link_cnt == 1) && (main_5g_link_cnt == 1)) ||
++ ((main_2g_link_cnt == 2) && (main_5g_link_cnt == 2)))
++ return 1;
++ else
++ return 0;
++ } else if (IS_GUEST_SSID_DEV(dev)) {
++ if ((guest_2g_link_cnt == 1) && (guest_5g_link_cnt == 1))
++ return 1;
++ else
++ return 0;
++ }
++}
++
++
++static void cal_link_count_by_net_device(struct net_device *dev, unsigned char policy)
++{
++ if (dev == apcli_2g)
++ {
++ if (policy > 0)
++ main_2g_link_cnt++;
++ else
++ main_2g_link_cnt--;
++
++ if (main_2g_link_cnt < 0)
++ main_2g_link_cnt = 0;
++ }
++ else if (dev == apcli_5g)
++ {
++ if (policy > 0)
++ main_5g_link_cnt++;
++ else
++ main_5g_link_cnt--;
++
++ if (main_5g_link_cnt < 0)
++ main_5g_link_cnt = 0;
++ }
++ else if (dev == apcli1_2g)
++ {
++ if (policy > 0)
++ guest_2g_link_cnt++;
++ else
++ guest_2g_link_cnt--;
++
++ if (guest_2g_link_cnt < 0)
++ guest_2g_link_cnt = 0;
++ }
++ else if (dev == apcli1_5g)
++ {
++ if (policy > 0)
++ guest_5g_link_cnt++;
++ else
++ guest_5g_link_cnt--;
++
++ if (guest_5g_link_cnt < 0)
++ guest_5g_link_cnt = 0;
++ }
++
++ if (policy > 0)
++ fwd_counter++;
++ else
++ fwd_counter--;
++}
++
++
++static void dump_net_device_by_name(void)
++{
++ ap_2g = ra_dev_get_by_name("ra0");
++ apcli_2g = ra_dev_get_by_name("apcli0");
++ ap_5g = ra_dev_get_by_name("rai0");
++ apcli_5g = ra_dev_get_by_name("apclii0");
++
++ /* for Guest SSID */
++ ap1_2g = ra_dev_get_by_name("ra1");
++ apcli1_2g = ra_dev_get_by_name("apcli1");
++ ap1_5g = ra_dev_get_by_name("rai1");
++ apcli1_5g = ra_dev_get_by_name("apclii1");
++
++ br_lan = ra_dev_get_by_name("br-lan");
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[dump]ap_2g=0x%08X, apcli_2g=0x%08X, ap_5g=0x%08X, apcli_5g=0x%08X, br_lan=0x%08X\n",
++ (int)ap_2g, (int)apcli_2g, (int)ap_5g, (int)apcli_5g, (int)br_lan));
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[dump]ap1_2g=0x%08X, apcli1_2g=0x%08X, ap1_5g=0x%08X, apcli1_5g=0x%08X\n",
++ (int)ap1_2g, (int)apcli1_2g, (int)ap1_5g, (int)apcli1_5g));
++
++
++}
++
++
++static void hex_dump_(char *str, unsigned char *pSrcBufVA, unsigned int SrcBufLen)
++{
++ unsigned char *pt;
++ unsigned int x;
++
++ pt = pSrcBufVA;
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("%s: %p, len = %d\n", str, pSrcBufVA, SrcBufLen));
++ for (x=0; x<SrcBufLen; x++) {
++ if (x % 16 == 0)
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("0x%04x : ", x));
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("%02x ", ((unsigned char)pt[x])));
++ if (x % 16 == 15)
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("\n"));
++ }
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("\n"));
++}
++
++
++static void wifi_fwd_reset_link_count(void)
++{
++ fwd_counter = 0;
++ main_5g_link_cnt = 0;
++ main_2g_link_cnt = 0;
++ guest_5g_link_cnt = 0;
++ guest_2g_link_cnt = 0;
++}
++
++static bool wifi_fwd_needed(void)
++{
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACTIVE) || !WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED) || (fwd_counter == 0))
++ return FALSE;
++
++ return TRUE;
++}
++
++
++static void wifi_fwd_show_entry(void)
++{
++ int idx = 0;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("\n[%s]fwd_link=%d, [5g main]=%d, [2g main]=%d, [5g guest]=%d, [2g guest]=%d\n",
++ __FUNCTION__, fwd_counter, main_5g_link_cnt, main_2g_link_cnt,
++ guest_5g_link_cnt, guest_2g_link_cnt));
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s][5g main: 0x%08X <--> 0x%08X] : %d\n",
++ __FUNCTION__, (int)apcli_5g, (int)ap_5g, main_5g_link_cnt));
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s][2g main: 0x%08X <--> 0x%08X] : %d\n",
++ __FUNCTION__, (int)apcli_2g, (int)ap_2g, main_2g_link_cnt));
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s][5g guest: 0x%08X <--> 0x%08X] : %d\n",
++ __FUNCTION__, (int)apcli1_5g, (int)ap1_5g, guest_5g_link_cnt));
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s][2g guest: 0x%08X <--> 0x%08X] : %d\n",
++ __FUNCTION__, (int)apcli1_2g, (int)ap1_2g, guest_2g_link_cnt));
++
++ for (idx=0; idx<WIFI_FWD_TBL_SIZE; idx++)
++ {
++ if (WiFiFwdBase[idx].valid)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]index=%d, valid=%d, src_dev=0x%08X, dest_dev=0x%08X\n",
++ __FUNCTION__, idx, WiFiFwdBase[idx].valid, (int)WiFiFwdBase[idx].src, (int)WiFiFwdBase[idx].dest));
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]eth_rep5g_wrg_uni_cnt=%d, eth_rep5g_wrg_bct_cnt=%d\n",
++ __FUNCTION__, eth_rep5g_wrg_uni_cnt, eth_rep5g_wrg_bct_cnt));
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]eth_rep2g_wrg_uni_cnt=%d, eth_rep2g_wrg_bct_cnt=%d\n",
++ __FUNCTION__, eth_rep2g_wrg_uni_cnt, eth_rep2g_wrg_bct_cnt));
++
++}
++
++
++static void wifi_fwd_delete_entry_by_idx(unsigned char idx)
++{
++ int i = 0;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("\n[%s]--------------------------------------------\n", __FUNCTION__));
++
++ if (idx < WIFI_FWD_TBL_SIZE)
++ {
++ if (WiFiFwdBase[idx].valid)
++ {
++ memset(&WiFiFwdBase[idx], 0, sizeof(struct FwdPair));
++ cal_link_count_by_net_device(WiFiFwdBase[idx].src, 0);
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]index=%d, valid=%d, src dev=0x%08X, dest dev=0x%08X\n",
++ __FUNCTION__, idx, WiFiFwdBase[idx].valid, (int)WiFiFwdBase[idx].src, (int)WiFiFwdBase[idx].dest));
++ } else
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]index=%d is void originally\n", __FUNCTION__, idx));
++ }
++ else
++ {
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ memset(&WiFiFwdBase[i], 0, sizeof(struct FwdPair));
++
++ wifi_fwd_reset_link_count();
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s] flush all entries\n", __FUNCTION__));
++ }
++}
++
++
++static void packet_source_show_entry(void)
++{
++ int idx = 0;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("\n[%s]--------------------------------------------\n", __FUNCTION__));
++
++ for (idx=0; idx<WIFI_FWD_TBL_SIZE; idx++)
++ {
++ if (pkt_src[idx].valid)
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]index=%d, valid=%d, src=0x%08X, peer=0x%08X, "
++ "h_source=%02X:%02X:%02X:%02X:%02X:%02X, h_dest=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__, idx, pkt_src[idx].valid, (int)pkt_src[idx].src, (int)pkt_src[idx].peer,
++ pkt_src[idx].h_source[0], pkt_src[idx].h_source[1], pkt_src[idx].h_source[2],
++ pkt_src[idx].h_source[3], pkt_src[idx].h_source[4], pkt_src[idx].h_source[5],
++ pkt_src[idx].h_dest[0], pkt_src[idx].h_dest[1], pkt_src[idx].h_dest[2],
++ pkt_src[idx].h_dest[3], pkt_src[idx].h_dest[4], pkt_src[idx].h_dest[5]));
++ }
++}
++
++
++static void packet_source_delete_entry(unsigned char idx)
++{
++ int i = 0;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("\n[%s]--------------------------------------------\n", __FUNCTION__));
++
++ if (idx < WIFI_FWD_TBL_SIZE)
++ {
++ if (pkt_src[idx].valid)
++ {
++ memset(&pkt_src[idx], 0, sizeof(struct PacketSource));
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]index=%d, valid=%d, src=0x%08X, peer=0x%08X, "
++ "h_source=%02X:%02X:%02X:%02X:%02X:%02X, h_dest=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__, idx, pkt_src[idx].valid, (int)pkt_src[idx].src, (int)pkt_src[idx].peer,
++ pkt_src[idx].h_source[0], pkt_src[idx].h_source[1], pkt_src[idx].h_source[2],
++ pkt_src[idx].h_source[3], pkt_src[idx].h_source[4], pkt_src[idx].h_source[5],
++ pkt_src[idx].h_dest[0], pkt_src[idx].h_dest[1], pkt_src[idx].h_dest[2],
++ pkt_src[idx].h_dest[3], pkt_src[idx].h_dest[4], pkt_src[idx].h_dest[5]));
++ } else
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]index=%d is void originally\n", __FUNCTION__, idx));
++ }
++ else if (idx == WIFI_FWD_TBL_SIZE)
++ {
++ eth_rep5g_wrg_uni_cnt = 0;
++ eth_rep5g_wrg_bct_cnt = 0;
++ eth_rep2g_wrg_uni_cnt = 0;
++ eth_rep2g_wrg_bct_cnt = 0;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] reset counters\n", __FUNCTION__));
++ }
++ else
++ {
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ memset(&pkt_src[i], 0, sizeof(struct PacketSource));
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] flush all entries\n", __FUNCTION__));
++ }
++}
++
++
++/*
++ return value:
++ >=0: array index of WiFiFwdBase
++ -1: search failed
++*/
++static int wifi_fwd_find_empty_entry(void)
++{
++ int idx = 0;
++
++ for (idx=0; idx<WIFI_FWD_TBL_SIZE; idx++)
++ {
++ if (WiFiFwdBase[idx].valid == 0)
++ return idx;
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] table full\n", __FUNCTION__));
++ return -1;
++}
++
++
++/*
++ return value:
++ >=0: array index of WiFiFwdBase
++ -1: search failed
++*/
++static int wifi_fwd_find_entry(struct net_device *src, struct net_device *dest)
++{
++ int idx = 0;
++
++ for (idx=0; idx<WIFI_FWD_TBL_SIZE; idx++)
++ {
++ if ((WiFiFwdBase[idx].valid) &&
++ (WiFiFwdBase[idx].src == src) &&
++ (WiFiFwdBase[idx].dest == dest))
++ return idx;
++ }
++
++ //WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] no entry found\n", __FUNCTION__));
++ return -1;
++}
++
++
++/*
++ return value:
++ 1: clear OK
++ 0: clear failed (wrong input index)
++*/
++static int wifi_fwd_clear_entry(int index)
++{
++ struct FwdPair *entry = NULL;
++ entry = WiFiFwdBase + index;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[wifi_fwd_clear_entry] original: index=%d, valid=%d, src_dev=0x%08X, dest_dev=0x%08X\n",
++ index, entry->valid, (int)entry->src, (int)entry->dest));
++
++ if (entry->valid) {
++ memset(entry, 0, sizeof(struct FwdPair));
++ return 1;
++ }
++
++ return 0;
++}
++
++static void wifi_fwd_set_cb_num(unsigned int band_cb_num, unsigned int receive_cb_num)
++{
++ band_cb_offset = band_cb_num;
++ recv_from_cb_offset = receive_cb_num;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] band_cb_offset=%d, recv_from_cb_offset=%d\n", __FUNCTION__, band_cb_offset, recv_from_cb_offset));
++}
++
++static void wifi_fwd_get_rep(unsigned char rep)
++{
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if (rep == 0)
++ rep_net_dev = eth_traffic_band_2g;
++ else
++ rep_net_dev = eth_traffic_band_5g;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s] rep=%d\n", __FUNCTION__, rep_net_dev));
++}
++
++static void wifi_fwd_set_debug_level(unsigned char level)
++{
++
++ if (level == 0)
++ wf_debug_level = WF_DEBUG_OFF;
++ else
++ wf_debug_level = WF_DEBUG_ON;
++
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s] level=%d\n", __FUNCTION__, wf_debug_level));
++}
++
++
++
++static void wifi_fwd_pro_active(void)
++{
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++
++static void wifi_fwd_pro_halt(void)
++{
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void wifi_fwd_pro_enabled(void)
++{
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ENABLED);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++
++static void wifi_fwd_pro_disabled(void)
++{
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_ENABLED);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++static void wifi_fwd_access_schedule_active(void)
++{
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++
++static void wifi_fwd_access_schedule_halt(void)
++{
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++
++static void wifi_fwd_hijack_active(void)
++{
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_HIJACK_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++
++static void wifi_fwd_hijack_halt(void)
++{
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_HIJACK_ACTIVE);
++ WF_FWD_PRINT(WF_DEBUG_OFF, ("[%s]\n", __FUNCTION__));
++}
++
++
++static void packet_tx_source_flush_all()
++{
++ if(!tx_src_tbl)
++ return;
++
++ memset(tx_src_tbl, 0, sizeof(struct TxSourceEntry)*WIFI_FWD_TBL_SIZE);
++ tx_src_count = 0;
++}
++
++
++static void wf_fwd_delete_entry_inform(unsigned char *addr)
++{
++ int i = 0,count = 0;
++ struct TxSourceEntry *entry = NULL;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if(!addr || (tx_src_count == 0))
++ return;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++ if (entry->valid == 1)
++ {
++ count++;
++ if(MAC_ADDR_EQUAL(addr, entry->h_source))
++ {
++ entry->valid = 0;
++ tx_src_count--;
++ if(tx_src_count < 0)
++ tx_src_count = 0;
++
++ return;
++ }
++
++ if(count >= tx_src_count)
++ return;
++ }
++ }
++}
++
++
++/*
++ return value:
++ 0: insert failed
++ 1: insert success
++ 2: do nothing
++*/
++static int wifi_fwd_insert_tx_source_entry(struct sk_buff *skb,struct net_device *tx_dev)
++{
++ struct ethhdr *mh = eth_hdr(skb);
++ unsigned int recv_from = WIFI_FWD_GET_PACKET_RECV_FROM(skb, recv_from_cb_offset);
++ struct TxSourceEntry *entry = NULL;
++ int i = 0, count = 0;
++ unsigned char bInsert = 0;
++
++ if(!is_wifi_concurrent_mode(tx_dev) || !mh)
++ return 2;
++
++ if(tx_dev == apcli_2g || tx_dev == apcli_5g)
++ {
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++ if ((entry->valid == 1) &&
++ MAC_ADDR_EQUAL(mh->h_source, entry->h_source))
++ return 2;
++ }
++ bInsert = 1;
++ }
++
++ if(!bInsert)
++ return 2;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++ if (entry->valid == 0)
++ {
++ tx_src_count++;
++ entry->valid = 1;
++ COPY_MAC_ADDR(entry->h_source, mh->h_source);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s %d] valid=%d, src_mac=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__,i, entry->valid,
++ entry->h_source[0], entry->h_source[1], entry->h_source[2],
++ entry->h_source[3], entry->h_source[4], entry->h_source[5]));
++ return 1;
++ }
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] No free space for insert tx source entry.\n",__FUNCTION__));
++ return 0;
++}
++
++/*
++ return value:
++ 0: no looping
++ 1: looping found
++ 2: forward to bridge
++*/
++static unsigned char wifi_fwd_check_looping(
++ struct sk_buff *skb,
++ struct net_device *dev)
++{
++ struct ethhdr *mh = eth_hdr(skb);
++ unsigned int recv_from = WIFI_FWD_GET_PACKET_RECV_FROM(skb, recv_from_cb_offset);
++ struct TxSourceEntry *entry = NULL;
++ int i = 0,count = 0;
++ unsigned char bMulticast = 0;
++
++ if (!is_wifi_concurrent_mode(dev) ||
++ !IS_PACKET_FROM_APCLI(recv_from) ||
++ (tx_src_count == 0) ||
++ !mh)
++ return 0;
++
++ if (dev == apcli_2g || dev == apcli_5g || dev == apcli1_2g || dev == apcli1_5g)
++ {
++ if ((mh->h_dest[0] & 0x1) == 0x1)
++ bMulticast = 1;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = tx_src_tbl + i;
++ if ((entry->valid == 1))
++ {
++ count++;
++ if(MAC_ADDR_EQUAL(mh->h_source, entry->h_source))
++ {
++ // Mean the souce has changed to other side.
++ if(!bMulticast)
++ {
++ wf_fwd_delete_entry_inform(entry->h_source);
++ return 0;
++ }
++
++ return 1;
++ }
++
++ if(count >= tx_src_count)
++ break;;
++ }
++ }
++ }
++
++ return 0;
++}
++
++
++/*
++ return value:
++ 1: insert success
++ 0: insert failed
++*/
++static unsigned char wifi_fwd_establish_entry(struct net_device *src, struct net_device *dest)
++{
++ struct FwdPair *entry = NULL;
++ int idx = 0;
++
++ if (!src || !dest)
++ return 0;
++
++ /* check if it is an existed entry */
++ idx = wifi_fwd_find_entry(src, dest);
++ if (idx >= 0)
++ return 0;
++
++ /* to establish the path between src and dest */
++ idx = wifi_fwd_find_empty_entry();
++ if (idx == -1)
++ return 0;
++
++ entry = WiFiFwdBase + idx;
++ entry->valid = 1;
++ entry->src = src;
++ entry->dest = dest;
++ cal_link_count_by_net_device(entry->src, 1);
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s %d] valid=%d, src=0x%08X, dest=0x%08X\n",
++ __FUNCTION__, idx, entry->valid, (unsigned int)entry->src, (unsigned int)entry->dest));
++}
++
++
++static void main_ssid_cover_guest_ssid(struct net_device *src)
++{
++ struct net_device *second_src = NULL, *second_dest = NULL, *first_src = NULL, *first_dest = NULL;
++ int idx = 0;
++
++ idx = wifi_fwd_find_entry(apcli_2g, ap1_2g);
++ if (idx >= 0) {
++ wifi_fwd_clear_entry(idx);
++ cal_link_count_by_net_device(apcli_2g, 0);
++ }
++
++ idx = wifi_fwd_find_entry(apcli_2g, ap1_5g);
++ if (idx >= 0) {
++ wifi_fwd_clear_entry(idx);
++ cal_link_count_by_net_device(apcli_2g, 0);
++ }
++
++ idx = wifi_fwd_find_entry(apcli_5g, ap1_2g);
++ if (idx >= 0) {
++ wifi_fwd_clear_entry(idx);
++ cal_link_count_by_net_device(apcli_5g, 0);
++ }
++
++ idx = wifi_fwd_find_entry(apcli_5g, ap1_5g);
++ if (idx >= 0) {
++ wifi_fwd_clear_entry(idx);
++ cal_link_count_by_net_device(apcli_5g, 0);
++ }
++
++ if (GUEST_SSID_OP(src)) {
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]no need to cover guest ssid\n", __FUNCTION__));
++ return;
++ }
++
++ if (is_wifi_concurrent_mode(apcli_2g))
++ {
++ idx = wifi_fwd_find_entry(apcli_2g, ap1_2g);
++ if (idx < 0)
++ wifi_fwd_establish_entry(apcli_2g, ap1_2g);
++
++ idx = wifi_fwd_find_entry(apcli_5g, ap1_5g);
++ if (idx < 0)
++ wifi_fwd_establish_entry(apcli_5g, ap1_5g);
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]need to cover guest ssid under concurrent mode\n", __FUNCTION__));
++ }
++ else if (is_wifi_fastlane_mode(apcli_2g))
++ {
++ idx = wifi_fwd_find_entry(apcli_2g, ap_2g);
++ if (idx >= 0) {
++ src = apcli_2g;
++ first_dest = ap1_2g;
++ second_dest = ap1_5g;
++ } else {
++ src = apcli_5g;
++ first_dest = ap1_5g;
++ second_dest = ap1_2g;
++ }
++
++ idx = wifi_fwd_find_entry(src, first_dest);
++ if (idx < 0)
++ wifi_fwd_establish_entry(src, first_dest);
++
++ idx = wifi_fwd_find_entry(src, second_dest);
++ if (idx < 0)
++ wifi_fwd_establish_entry(src, second_dest);
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]need to cover guest ssid under fastLane mode\n", __FUNCTION__));
++ }
++ else
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s]no need to cover guest ssid under AP mode\n", __FUNCTION__));
++}
++
++
++/*
++ return value:
++ 1: delete success
++ 0: delete failed
++*/
++static unsigned char wifi_fwd_delete_entry(struct net_device *src, struct net_device *dest, unsigned char link_down)
++{
++ struct net_device *second_src = NULL, *second_dest = NULL, *first_dest = NULL;
++ int idx = 0;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return 1;
++
++ if (src == apcli_2g) {
++ first_dest = ap_2g;
++ second_src = apcli_5g;
++ second_dest = ap_5g;
++ } else if (src == apcli_5g) {
++ first_dest = ap_5g;
++ second_src = apcli_2g;
++ second_dest = ap_2g;
++ } else if (src == apcli1_2g) {
++ first_dest = ap1_2g;
++ second_src = apcli1_5g;
++ second_dest = ap1_5g;
++ } else if (src == apcli1_5g) {
++ first_dest = ap1_5g;
++ second_src = apcli1_2g;
++ second_dest = ap1_2g;
++ }
++
++ if (link_down == 0)
++ first_dest = dest;
++
++ idx = wifi_fwd_find_entry(src, first_dest);
++ if (idx == -1)
++ return 0;
++
++ if (wifi_fwd_clear_entry(idx) == 0)
++ return 0;
++
++ cal_link_count_by_net_device(src, 0);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] index=%d, src=0x%08X, dest=0x%08X\n",
++ __FUNCTION__, idx, (int)src, (int)first_dest));
++
++ /*
++ check if there exists FastLane case
++ if yes, delete the cross path of FastLane as well
++ */
++ idx = wifi_fwd_find_entry(src, second_dest);
++ if (idx >= 0) {
++ wifi_fwd_clear_entry(idx);
++ cal_link_count_by_net_device(src, 0);
++ }
++
++ /*
++ check if there exists the connection of the other band
++ if yes, need to do FastLane case
++ */
++ idx = wifi_fwd_find_entry(second_src, second_dest);
++ if (idx == -1)
++ goto done;
++
++ /* try to establish the FastLane path between second_src and dest */
++ if (wifi_fwd_establish_entry(second_src, first_dest) == 0)
++ return 0;
++
++done:
++ main_ssid_cover_guest_ssid(src);
++
++ /* flush packet source table */
++ packet_source_delete_entry(WIFI_FWD_TBL_SIZE+1);
++
++ /* flush tx packet source table */
++ packet_tx_source_flush_all();
++
++ WIFI_FWD_CLEAR_FLAG(fOP_GET_NET_DEVICE_STATUS_DONE);
++ return 1;
++}
++
++
++/*
++ return value:
++ 1: insert success
++ 0: insert failed
++*/
++static unsigned char wifi_fwd_insert_entry(struct net_device *src, struct net_device *dest, void *adapter)
++{
++ struct net_device *second_src = NULL, *second_dest = NULL, *first_dest = NULL;
++ struct FwdPair *entry = NULL;
++ int idx = 0;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return 0;
++
++ if (!src || !dest)
++ return 0;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_GET_NET_DEVICE_STATUS_DONE)) {
++ dump_net_device_by_name();
++ WIFI_FWD_SET_FLAG(fOP_GET_NET_DEVICE_STATUS_DONE);
++ }
++
++ if (src == apcli_2g) {
++ first_dest = ap_2g;
++ second_src = apcli_5g;
++ second_dest = ap_5g;
++ adapter_2g = adapter;
++
++ if ((adapter_tmp_1 == adapter_2g) && (adapter_tmp_2 != NULL))
++ adapter_5g = adapter_tmp_2;
++ else if ((adapter_tmp_2 == adapter_2g) && (adapter_tmp_1 != NULL))
++ adapter_5g = adapter_tmp_1;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] input adapter=0x%08X, adapter_2g=0x%08X, adapter_5g=0x%08X\n",
++ __FUNCTION__, (int)adapter, (int)adapter_2g, (int)adapter_5g));
++
++ } else if (src == apcli_5g) {
++ first_dest = ap_5g;
++ second_src = apcli_2g;
++ second_dest = ap_2g;
++ adapter_5g = adapter;
++
++ if ((adapter_tmp_1 == adapter_5g) && (adapter_tmp_2 != NULL))
++ adapter_2g = adapter_tmp_2;
++ else if ((adapter_tmp_2 == adapter_5g) && (adapter_tmp_1 != NULL))
++ adapter_2g = adapter_tmp_1;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] input adapter=0x%08X, adapter_5g=0x%08X, adapter_2g=0x%08X\n",
++ __FUNCTION__, (int)adapter, (int)adapter_5g, (int)adapter_2g));
++
++ } else if (src == apcli1_2g) {
++ first_dest = ap1_2g;
++ second_src = apcli1_5g;
++ second_dest = ap1_5g;
++
++ if (adapter_2g == NULL) {
++ adapter_2g = adapter;
++
++ if ((adapter_tmp_1 == adapter_2g) && (adapter_tmp_2 != NULL))
++ adapter_5g = adapter_tmp_2;
++ else if ((adapter_tmp_2 == adapter_2g) && (adapter_tmp_1 != NULL))
++ adapter_5g = adapter_tmp_1;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] input adapter=0x%08X, adapter_2g=0x%08X, adapter_5g=0x%08X\n",
++ __FUNCTION__, (int)adapter, (int)adapter_2g, (int)adapter_5g));
++
++ }
++ } else if (src == apcli1_5g) {
++ first_dest = ap1_5g;
++ second_src = apcli1_2g;
++ second_dest = ap1_2g;
++
++ if (adapter_5g == NULL) {
++ adapter_5g = adapter;
++
++ if ((adapter_tmp_1 == adapter_5g) && (adapter_tmp_2 != NULL))
++ adapter_2g = adapter_tmp_2;
++ else if ((adapter_tmp_2 == adapter_5g) && (adapter_tmp_1 != NULL))
++ adapter_2g = adapter_tmp_1;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] input adapter=0x%08X, adapter_5g=0x%08X, adapter_2g=0x%08X\n",
++ __FUNCTION__, (int)adapter, (int)adapter_5g, (int)adapter_2g));
++
++ }
++ }
++
++ /*
++ check if there exists FastLane case
++ if yes, delete the cross path of FastLane
++ */
++ idx = wifi_fwd_find_entry(second_src, first_dest);
++ if (idx >= 0)
++ wifi_fwd_delete_entry(second_src, first_dest, 0);
++
++ /* try to establish the path between src and dest */
++ if (wifi_fwd_establish_entry(src, first_dest) == 0)
++ return 0;
++
++ /*
++ check if there exists the connection of the other band
++ if yes, no need to do FastLane case
++ */
++ idx = wifi_fwd_find_entry(second_src, second_dest);
++ if (idx >= 0)
++ goto done;
++
++ /* try to establish the FastLane path between src and second_dest */
++ if (wifi_fwd_establish_entry(src, second_dest) == 0)
++ goto done;
++
++done:
++ main_ssid_cover_guest_ssid(src);
++
++ /* flush packet source table */
++ packet_source_delete_entry(WIFI_FWD_TBL_SIZE+1);
++
++ return 1;
++}
++
++
++/*
++ return value:
++ 0: Success, skb is handled by wifi_fwd module.
++ 1: FAIL, do nothing
++*/
++static struct net_device * wifi_fwd_insert_packet_source(struct sk_buff *skb, struct net_device *dev, struct net_device *peer)
++{
++ struct ethhdr *mh = eth_hdr(skb);
++ struct PacketSource *entry = NULL;
++ bool need_flush = false;
++ int i = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 1) {
++ if (peer == entry->peer) {
++ if (MAC_ADDR_EQUAL(mh->h_source, entry->h_source)) {
++ if (dev == entry->src) {
++ return peer;
++ } else {
++ need_flush = true;
++ break;
++ }
++ }
++ }
++ }
++ }
++
++ if (need_flush == true)
++ packet_source_delete_entry(WIFI_FWD_TBL_SIZE+1);
++ else
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] packet source cannot be found\n", __FUNCTION__));
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 0) {
++ COPY_MAC_ADDR(entry->h_source, mh->h_source);
++ COPY_MAC_ADDR(entry->h_dest, mh->h_dest);
++ entry->valid = 1;
++ entry->peer = peer;
++ entry->src = dev;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s %d] valid=%d, from=0x%08X, will send to=0x%08X, "
++ "src_mac=%02X:%02X:%02X:%02X:%02X:%02X, dest_mac=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__, i, entry->valid, entry->src, entry->peer,
++ entry->h_source[0], entry->h_source[1], entry->h_source[2],
++ entry->h_source[3], entry->h_source[4], entry->h_source[5],
++ entry->h_dest[0], entry->h_dest[1], entry->h_dest[2],
++ entry->h_dest[3], entry->h_dest[4],entry->h_dest[5]));
++
++ return entry->peer;
++ }
++ }
++}
++
++static void wifi_fwd_tx_count_by_source_addr(
++ struct sk_buff *skb,
++ struct net_device *tx_dev,
++ unsigned char *found_path,
++ unsigned char *entry_cnt)
++{
++ struct PacketSource *ppkt_src = NULL;
++ struct FwdPair *entry = NULL;
++ int i = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ ppkt_src = pkt_src + i;
++
++ if (ppkt_src->valid == 1)
++ {
++ if (MAC_ADDR_EQUAL(&skb->data[6], ppkt_src->h_source))
++ {
++ *entry_cnt = *entry_cnt + 1;
++
++ if ((ppkt_src->src == tx_dev) || (ppkt_src->peer == tx_dev))
++ {
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = WiFiFwdBase + i;
++
++ if (entry->valid == 1)
++ {
++ if (((entry->src == ppkt_src->src) && (entry->dest == ppkt_src->peer)) ||
++ ((entry->src == ppkt_src->peer) && (entry->dest == ppkt_src->src)))
++ *found_path = *found_path + 1;
++ }
++ }
++ }
++ }
++ }
++ else
++ break;
++ }
++}
++
++
++/*
++ return value:
++ 1: find entry
++ 0: find nothing
++*/
++static unsigned char wifi_fwd_find_sa_entry_by_sa_and_nd(
++ struct sk_buff *skb,
++ struct net_device *sender_dev,
++ unsigned char idx)
++{
++ struct PacketSource *entry = NULL;
++ struct ethhdr *mh = eth_hdr(skb);
++ int i = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 1)
++ {
++ if (MAC_ADDR_EQUAL(mh->h_source, entry->h_source))
++ {
++ if (idx == 0)
++ {
++ if (entry->src == sender_dev)
++ return 1;
++ }
++ else
++ {
++ if (entry->peer== sender_dev)
++ return 1;
++ }
++ }
++ }
++ else
++ break;
++ }
++
++ return 0;
++}
++
++
++/*
++ return value: # of found entries
++*/
++static unsigned char wifi_fwd_sa_count_by_source_addr(
++ struct sk_buff *skb,
++ struct net_device *receive_dev)
++{
++ struct PacketSource *entry = NULL;
++ struct ethhdr *mh = eth_hdr(skb);
++ int i = 0, sa_cnt = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 1)
++ {
++ if ((mh->h_source[0] & 0x2) == 0x2)
++ {
++ if (memcmp(&mh->h_source[3], &entry->h_source[3], 3) == 0)
++ sa_cnt++;
++ }
++ else
++ {
++ if (MAC_ADDR_EQUAL(mh->h_source, entry->h_source))
++ sa_cnt++;
++ }
++ }
++ else
++ break;
++ }
++
++ return sa_cnt;
++}
++
++
++/*
++ return value:
++ 2: should be forwarded to bridge
++ 1: not forwarded from bridge
++ 0: suppose it is forwarded from bridge
++*/
++static unsigned char wifi_fwd_check_from_bridge_by_dest_addr(
++ struct sk_buff *skb,
++ struct net_device *sender_dev)
++{
++ struct PacketSource *entry = NULL;
++ struct ethhdr *mh = eth_hdr(skb);
++ int i = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = pkt_src + i;
++
++ if (entry->valid == 1)
++ {
++ if (((sender_dev == ap_2g) && (entry->src== ap_5g)) ||
++ ((sender_dev == ap_5g) && (entry->src == ap_2g)) ||
++ ((sender_dev == ap1_5g) && (entry->src == ap1_2g)) ||
++ ((sender_dev == ap1_5g) && (entry->src == ap1_2g)))
++ {
++ if (MAC_ADDR_EQUAL(mh->h_dest, entry->h_source))
++ return 2;
++ }
++ else
++ {
++ if (MAC_ADDR_EQUAL(mh->h_dest, entry->h_source))
++ return 1;
++ }
++ }
++ else
++ break;
++ }
++
++ return 0;
++}
++
++/*
++ return value:
++ 0: no need to drop this packet
++ 1: drop this packet
++*/
++static unsigned char wifi_fwd_tx_lookup_entry(struct net_device *tx_dev, struct sk_buff *skb)
++{
++ unsigned char found = 0, entry_cnt = 0;
++ unsigned int recv_from = 0, band_from = 0;
++ struct sk_buff *clone_pkt = NULL;
++ bool need_redirect = false;
++
++ band_from = WIFI_FWD_GET_PACKET_BAND(skb, band_cb_offset);
++ recv_from = WIFI_FWD_GET_PACKET_RECV_FROM(skb, recv_from_cb_offset);
++
++ if (IS_PACKET_FROM_APCLI(recv_from) && !MAIN_SSID_OP(tx_dev))
++ return 1;
++
++ /* drop the packet from guest ssid to main ssid, and vice versa */
++ if (!IS_PACKET_FROM_ETHER(band_from) &&
++ ((guest_2g_link_cnt != 0) || (guest_5g_link_cnt != 0)))
++ {
++ if (IS_MAIN_SSID_DEV(tx_dev)) {
++ if (IS_PACKET_FROM_GUEST_SSID(recv_from))
++ return 1;
++ } else if (IS_GUEST_SSID_DEV(tx_dev)) {
++ if (IS_PACKET_FROM_MAIN_SSID(recv_from))
++ return 1;
++ }
++ }
++
++ /* drop the packet from the other band */
++ if (is_wifi_concurrent_mode(tx_dev))
++ {
++ if ((tx_dev == apcli_5g) || (tx_dev == apcli1_5g))
++ {
++ if (IS_PACKET_FROM_2G(band_from)) {
++ if(IS_TAG_PACKET(band_from))
++ need_redirect = true;
++ else
++ return 1;
++ }
++
++ if ((rep_net_dev == eth_traffic_band_2g) && IS_PACKET_FROM_ETHER(band_from)) {
++ if ((((unsigned char *)skb->data)[0] & 0x1) == 0x0) {
++ eth_rep2g_wrg_uni_cnt++;
++ need_redirect = true;
++ } else
++ eth_rep2g_wrg_bct_cnt++;
++ }
++
++ if (need_redirect == true) {
++ clone_pkt = skb_clone(skb, GFP_ATOMIC);
++ if (tx_dev == apcli_5g)
++ clone_pkt->dev = apcli_2g;
++ else
++ clone_pkt->dev = apcli1_2g;
++ dev_queue_xmit(clone_pkt);
++ return 1;
++ }
++ }
++ else if ((tx_dev == apcli_2g) || (tx_dev == apcli1_2g))
++ {
++ if (IS_PACKET_FROM_5G(band_from)) {
++ if(IS_TAG_PACKET(band_from))
++ need_redirect = true;
++ else
++ return 1;
++ }
++
++ if ((rep_net_dev == eth_traffic_band_5g) && IS_PACKET_FROM_ETHER(band_from)) {
++ if ((((unsigned char *)skb->data)[0] & 0x1) == 0x0) {
++ eth_rep5g_wrg_uni_cnt++;
++ need_redirect = true;
++ } else
++ eth_rep5g_wrg_bct_cnt++;
++ }
++
++ if (need_redirect == true) {
++ clone_pkt = skb_clone(skb, GFP_ATOMIC);
++ if (tx_dev == apcli_2g)
++ clone_pkt->dev = apcli_5g;
++ else
++ clone_pkt->dev = apcli1_5g;
++ dev_queue_xmit(clone_pkt);
++ return 1;
++ }
++ }
++ }
++
++ /*
++ in FastLane topology:
++ 1. forward Tx packets to driver and handle without any drop
++ 2. only for unicast
++ */
++ if (((((unsigned char *)skb->data)[0] & 0x1) == 0x0) && (is_wifi_fastlane_mode(tx_dev) && !GUEST_SSID_OP(tx_dev)))
++ return 0;
++
++ wifi_fwd_tx_count_by_source_addr(skb, tx_dev, &found, &entry_cnt);
++
++ if ((((unsigned char *)skb->data)[0] & 0x1) == 0x1)
++ {
++ if(wifi_fwd_insert_tx_source_entry(skb,tx_dev) == 0)
++ return 0;
++
++ if (found == 0)
++ {
++ if (entry_cnt == 0)
++ {
++ if (is_wifi_concurrent_mode(tx_dev) &&
++ IS_PACKET_FROM_ETHER(band_from))
++ {
++ if ((tx_dev == apcli_5g) || (tx_dev == apcli1_5g))
++ {
++ if (rep_net_dev == eth_traffic_band_5g)
++ return 0;
++ else
++ return 1;
++ }
++ else if ((tx_dev == apcli_2g) || (tx_dev == apcli1_2g))
++ {
++ if (rep_net_dev == eth_traffic_band_2g)
++ return 0;
++ else
++ return 1;
++ }
++ }
++ }
++
++ return 0;
++ }
++ else
++ {
++ if (entry_cnt >= 2)
++ return 0;
++ else
++ {
++ if (wifi_fwd_find_sa_entry_by_sa_and_nd(skb, tx_dev, 1) == 0)
++ return 1;
++ else
++ return 0;
++ }
++ }
++ }
++ else
++ {
++#if 0 /* move to earlier and handle it */
++ /* in FastLane case, forward Rx packets to bridge and let flooding work done by bridge */
++ if (is_wifi_fastlane_mode(tx_dev))
++ return 0;
++#endif
++ if (found == 0)
++ {
++ if (entry_cnt == 0)
++ {
++ if (is_wifi_concurrent_mode(tx_dev) &&
++ IS_PACKET_FROM_ETHER(band_from))
++ {
++ if (rep_net_dev == eth_traffic_band_5g)
++ {
++ if (REP_IS_5G(tx_dev))
++ return 0;
++ }
++ else if (rep_net_dev == eth_traffic_band_2g)
++ {
++ if (REP_IS_2G(tx_dev))
++ return 0;
++ }
++ }
++ }
++ }
++
++ return 0;
++ }
++}
++
++
++/*
++ return value of struct net_device:
++ NULL: search fail
++ other: partner net_device
++*/
++static struct net_device * wifi_fwd_lookup_entry(struct net_device *dev, unsigned char *type, struct sk_buff *skb)
++{
++ struct FwdPair *entry = NULL;
++ struct net_device *src = NULL;
++ int i = 0;
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = WiFiFwdBase + i;
++
++ if (entry->valid == 1)
++ {
++ if (entry->src == dev)
++ {
++ *type = ENTRY_TYPE_SRC;
++ src = wifi_fwd_insert_packet_source(skb, dev, entry->dest);
++
++ if (src != NULL)
++ return src;
++ }
++ else if (entry->dest == dev)
++ {
++ *type = ENTRY_TYPE_DEST;
++ src = wifi_fwd_insert_packet_source(skb, dev, entry->src);
++
++ if (src != NULL)
++ return src;
++ }
++ }
++ }
++
++ *type = ENTRY_TYPE_INVALID;
++ return NULL;
++}
++
++
++static void wifi_fwd_entry_dump(void)
++{
++ struct FwdPair *entry = NULL;
++ int i =0;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("\ndump wifi fwd table\n"));
++
++ for (i=0; i<WIFI_FWD_TBL_SIZE; i++)
++ {
++ entry = WiFiFwdBase + i;
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%d] valid=%d, src=0x%08X, dest=0x%08X\n",
++ i, entry->valid, (int)entry->src, (int)entry->dest));
++ }
++
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("\n"));
++}
++
++
++void wifi_fwd_probe_adapter(void *adapter)
++{
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if ((adapter != NULL) && (adapter_tmp_1 == NULL)) {
++ adapter_tmp_1 = adapter;
++ } else if ((adapter != NULL) && (adapter_tmp_2 == NULL)) {
++ if (adapter != adapter_tmp_1)
++ adapter_tmp_2 = adapter;
++ }
++}
++
++
++void wifi_fwd_feedback_peer_adapter(void *adapter, void **peer)
++{
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if ((adapter == adapter_2g) && (adapter_5g != NULL)) {
++ *peer = adapter_5g;
++ } else if ((adapter == adapter_5g) && (adapter_2g != NULL)) {
++ *peer = adapter_2g;
++ } else {
++ *peer = NULL;
++ }
++}
++
++void wifi_fwd_feedback_map_table(void *adapter, void **peer, void **opp_peer)
++{
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++
++ FWD_IRQ_LOCK(&global_band_tbl_lock, global_band_tbl_irq_flags);
++
++ if (adapter == adapter_2g) {
++
++ if (global_map_2g_tbl.Enabled == TRUE)
++ *peer = &global_map_2g_tbl;
++ else
++ *peer = NULL;
++
++ if (global_map_5g_tbl.Enabled == TRUE)
++ *opp_peer = &global_map_5g_tbl;
++ else
++ *opp_peer = NULL;
++
++
++ } else if (adapter == adapter_5g) {
++
++ if (global_map_5g_tbl.Enabled == TRUE)
++ *peer = &global_map_5g_tbl;
++ else
++ *peer = NULL;
++
++ if (global_map_2g_tbl.Enabled == TRUE)
++ *opp_peer = &global_map_2g_tbl;
++ else
++ *opp_peer = NULL;
++
++
++ } else {
++ *peer = NULL;
++ *opp_peer = NULL;
++ }
++
++ FWD_IRQ_UNLOCK(&global_band_tbl_lock, global_band_tbl_irq_flags);
++
++}
++
++void wifi_fwd_insert_repeater_mapping(void *adapter, void *lock, void *cli_mapping, void *map_mapping, void *ifAddr_mapping)
++{
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ FWD_IRQ_LOCK(&global_band_tbl_lock, global_band_tbl_irq_flags);
++
++ if ( adapter == adapter_2g ) {
++ global_map_2g_tbl.EntryLock = lock;
++ global_map_2g_tbl.CliHash = cli_mapping;
++ global_map_2g_tbl.MapHash = map_mapping;
++ global_map_2g_tbl.Wdev_ifAddr = ifAddr_mapping;
++ global_map_2g_tbl.Enabled = TRUE;
++
++ } else if ( adapter == adapter_5g ) {
++ global_map_5g_tbl.EntryLock = lock;
++ global_map_5g_tbl.CliHash = cli_mapping;
++ global_map_5g_tbl.MapHash = map_mapping;
++ global_map_5g_tbl.Wdev_ifAddr = ifAddr_mapping;
++ global_map_5g_tbl.Enabled = TRUE;
++ }
++
++ if (0) {
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] global_map_2g_tbl address %p\n", __FUNCTION__, &global_map_2g_tbl));
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] global_map_5g_tbl address %p\n", __FUNCTION__, &global_map_5g_tbl));
++ }
++
++
++ FWD_IRQ_UNLOCK(&global_band_tbl_lock, global_band_tbl_irq_flags);
++
++}
++
++void wifi_fwd_insert_bridge_mapping(struct sk_buff *skb)
++{
++ struct net_device *net_dev = NULL;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *map_tbl_entry = NULL;
++ unsigned char pkt_from, tbl_size = 0;
++
++ net_dev = skb->dev;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return;
++
++ if ((net_dev == ap_5g) || (net_dev == apcli_5g))
++ pkt_from = WIFI_FWD_PACKET_SPECIFIC_5G;
++ else
++ pkt_from = WIFI_FWD_PACKET_SPECIFIC_2G;
++
++ FWD_IRQ_LOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++
++ if (global_map_tbl.entry_num < 0) {
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] no entry found in the list\n", __FUNCTION__));
++ return;
++ }
++
++ tbl_size = sizeof(struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT);
++ map_tbl_entry = (struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *) kmalloc(tbl_size, GFP_ATOMIC);
++
++ if (map_tbl_entry) {
++ memset(map_tbl_entry, 0, tbl_size);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] size of map_tbl_entry = %dbytes\n", __FUNCTION__, tbl_size));
++ } else
++ return;
++
++ map_tbl_entry->rcvd_net_dev = net_dev;
++ map_tbl_entry->entry_from = pkt_from;
++ memcpy(map_tbl_entry->src_addr, ((unsigned char *)skb->data)[6], ETH_ALEN);
++
++ if (global_map_tbl.entry_num == 0) {
++ global_map_tbl.pHead = map_tbl_entry;
++ global_map_tbl.pTail = map_tbl_entry;
++ map_tbl_entry->pBefore = NULL;
++ map_tbl_entry->pNext = NULL;
++ }
++ else if (global_map_tbl.entry_num > 0) {
++ global_map_tbl.pTail->pNext = map_tbl_entry;
++ map_tbl_entry->pBefore = global_map_tbl.pTail;
++ global_map_tbl.pTail = map_tbl_entry;
++ }
++
++ global_map_tbl.entry_num++;
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++
++ if (0) {
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] %s\n", __FUNCTION__, (pkt_from == WIFI_FWD_PACKET_SPECIFIC_5G ? "5G" : "2.4G")));
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] inserting mac addr = %02X:%02X:%02X:%02X:%02X:%02X\n",
++ __FUNCTION__, PRINT_MAC(map_tbl_entry->src_addr)));
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] rcvd from %s\n", __FUNCTION__, WIFI_FWD_NETDEV_GET_DEVNAME(map_tbl_entry->rcvd_net_dev)));
++ }
++
++ return;
++}
++
++
++/*
++ return value:
++ 1: success
++ 0: fail
++*/
++static int wifi_fwd_search_mapping_table(struct sk_buff *skb, struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT **tbl_entry)
++{
++ struct net_device *net_dev = NULL;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *map_tbl_entry = NULL;
++ unsigned char pkt_from, idx = 0;
++
++ net_dev = skb->dev;
++
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED))
++ return 0;
++
++ if ((net_dev == ap_5g) || (net_dev == apcli_5g))
++ pkt_from = WIFI_FWD_PACKET_SPECIFIC_5G;
++ else
++ pkt_from = WIFI_FWD_PACKET_SPECIFIC_2G;
++
++ FWD_IRQ_LOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++
++ if (global_map_tbl.entry_num <= 0) {
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] no entry found in the list\n", __FUNCTION__));
++ return 0;
++ }
++ else
++ {
++ if (global_map_tbl.pHead != NULL) {
++ map_tbl_entry = global_map_tbl.pHead;
++ }
++ else
++ {
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ return 0;
++ }
++
++ for (idx=0; idx<global_map_tbl.entry_num; idx++)
++ {
++ if (MAC_ADDR_EQUAL(map_tbl_entry->src_addr, ((unsigned char *)skb->data)[6]) &&
++ (net_dev != map_tbl_entry->rcvd_net_dev))
++ {
++ if (map_tbl_entry->entry_from == WIFI_FWD_PACKET_SPECIFIC_5G) {
++ /* indicate this entry exist in dual band. packets sending to this entry need to be monitored */
++ map_tbl_entry->entry_from |= pkt_from;
++ map_tbl_entry->rcvd_net_dev = net_dev;
++ }
++ else
++ {
++ /* make sure the net device reported in the packet is up */
++ if (dev_get_flags(map_tbl_entry->rcvd_net_dev) & IFF_UP) {
++ map_tbl_entry->entry_from |= pkt_from; /* indicate this entry exist in dual band. packet need to send to this */
++ SET_OS_PKT_NETDEV(RTPKT_TO_OSPKT(skb), map_tbl_entry->rcvd_net_dev); /* change net_device of packet to 2G */
++ }
++ }
++
++ *tbl_entry = map_tbl_entry;
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ return 1;
++ }
++ else if (MAC_ADDR_EQUAL(map_tbl_entry->src_addr, ((unsigned char *)skb->data)[6]) &&
++ (net_dev == map_tbl_entry->rcvd_net_dev))
++ {
++ *tbl_entry = map_tbl_entry;
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ return 1;
++ }
++
++ map_tbl_entry = map_tbl_entry->pNext;
++ }
++
++ FWD_IRQ_UNLOCK(&global_map_tbl_lock, global_map_tbl_irq_flags);
++ }
++
++ return 0;
++}
++
++
++/*
++ return value:
++ 1: Allocate Success
++ 0: Allocate FAIL
++*/
++static int wifi_fwd_alloc_tbl(unsigned int NumOfEntry)
++{
++ unsigned int TblSize = 0;
++
++ TblSize = NumOfEntry * sizeof(struct FwdPair);
++ WiFiFwdBase = (struct FwdPair *) kmalloc(TblSize, GFP_ATOMIC);
++ if (WiFiFwdBase) {
++ memset(WiFiFwdBase, 0, TblSize);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] size of WiFiFwdBase = %dbytes\n", __FUNCTION__, TblSize));
++ }
++ else
++ return 0;
++
++ TblSize = NumOfEntry * sizeof(struct PacketSource);
++ pkt_src = (struct PacketSource *) kmalloc(TblSize, GFP_ATOMIC);
++ if (pkt_src) {
++ memset(pkt_src, 0, TblSize);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] size of pkt_src = %dbytes\n", __FUNCTION__, TblSize));
++ }
++ else
++ return 0;
++
++ TblSize = NumOfEntry * sizeof(struct TxSourceEntry);
++ tx_src_tbl = (struct TxSourceEntry *) kmalloc(TblSize, GFP_ATOMIC);
++ if (tx_src_tbl) {
++ memset(tx_src_tbl, 0, TblSize);
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("[%s] size of tx_src_tbl = %dbytes\n", __FUNCTION__, TblSize));
++ }
++ else
++ return 0;
++
++ TblSize = sizeof(struct APCLI_BRIDGE_LEARNING_MAPPING_MAP);
++ memset(&global_map_tbl, 0, TblSize);
++
++ TblSize = sizeof(struct _REPEATER_ADAPTER_DATA_TABLE);
++ memset(&global_map_2g_tbl, 0, TblSize);
++ memset(&global_map_5g_tbl, 0, TblSize);
++
++ return 1;
++}
++
++
++static unsigned char wifi_fwd_check_and_forward(struct sk_buff *skb)
++{
++ struct ethhdr *mh = NULL;
++ struct iphdr *iph = NULL;
++ struct udphdr *uh = NULL;
++ struct tcphdr *th = NULL;
++ struct ipv6hdr *ipv6h = NULL;
++ void *ptr = NULL;
++ unsigned short type = 0;
++
++ mh = eth_hdr(skb);
++ if (mh)
++ {
++ type = ntohs(mh->h_proto);
++ switch(type)
++ {
++ /*
++ Forward LLTD EthType: 0x88D9
++ */
++ case LLTD_ETH_TYPE:
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("Forward - LLTD_ETH_TYPE: 0x%02x\n", type));
++#endif
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE))
++ return TRUE;
++ break;
++
++ /*
++ Forward ARP EthType: 0x0806
++ */
++ case ARP_ETH_TYPE:
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("Forward - ARP_ETH_TYPE: 0x%02x\n", type));
++#endif
++ if (WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE))
++ return TRUE;
++ break;
++
++ case ETH_P_IP:
++ iph = (struct iphdr *)(skb->data);
++#if 0
++ hex_dump_("Data", skb->data, (iph->ihl<<2));
++#endif
++ if (iph)
++ {
++ ptr = (void *)(skb->data+(iph->ihl<<2));
++ if (ptr)
++ {
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("wifi_fwd_check_and_forward - iph->protocol: 0x%02x\n", iph->protocol));
++#endif
++ switch(iph->protocol)
++ {
++ case UDP:
++ /*
++ Forward UDP port 53 and 67
++ */
++ uh = (struct udphdr*)(ptr);
++ if ((ntohs(uh->source) == 53) || (ntohs(uh->dest) == 53) ||
++ (ntohs(uh->source) == 67) || (ntohs(uh->dest) == 67))
++ {
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("Forward - udp source port: %d, dest port: %d\n", ntohs(uh->source), ntohs(uh->dest)));
++#endif
++ return TRUE;
++ }
++ break;
++
++ case TCP:
++ /*
++ Forward TCP port 80 and 5000
++ */
++ th = (struct tcphdr *)(ptr);
++ if ((ntohs(th->source) == 80) ||
++ (ntohs(th->dest) == 80) ||
++ (ntohs(th->source) == 5000) ||
++ (ntohs(th->dest) == 5000))
++ {
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("Forward - tcp source port: %d, dest port: %d\n", ntohs(th->source), ntohs(th->dest)));
++#endif
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE))
++ return TRUE;
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++ }
++ break;
++
++ case ETH_P_IPV6:
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("IPv6: IPV6_HDR_LEN = %d\n", IPV6_HDR_LEN));
++ hex_dump_("Data", skb->data, LENGTH_802_3+IPV6_HDR_LEN);
++#endif
++ ipv6h = (struct ipv6hdr *)(skb->data);
++ if (ipv6h)
++ {
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("ipv6h->version = 0x%x\n", ipv6h->version));
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("ipv6h->nexthdr = 0x%x\n", ipv6h->nexthdr));
++#endif
++ ptr = (void *)(skb->data+IPV6_HDR_LEN);
++ if (ptr)
++ {
++ switch(ipv6h->nexthdr)
++ {
++ /*
++ Forward IPv6 UDP port 53
++ */
++ case IPV6_NEXT_HEADER_UDP:
++ uh = (struct udphdr*)(ptr);
++#if 0
++ WF_FWD_PRINT(WF_DEBUG_TRACE, ("udp source port: %d, dest port: %d\n", ntohs(uh->source), ntohs(uh->dest)));
++#endif
++ if ((ntohs(uh->source) == 53) || (ntohs(uh->dest) == 53))
++ {
++ return TRUE;
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++
++ return FALSE;
++}
++
++/*
++ return value:
++ 0: return to driver and handle
++ 1: return to driver and release
++*/
++static int wifi_fwd_tx_handler(struct sk_buff *skb)
++{
++ struct net_device *net_dev = NULL;
++ net_dev = skb->dev;
++ unsigned char ret = 0;
++
++ /*
++ return this skb to driver and handle while:
++ 1. path of WiFi forwarding is inactive
++ 2. the skb does not exist
++ 3. no forwarding connection is established
++ */
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACTIVE) || !WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED) || !skb || (fwd_counter == 0))
++ return 0;
++
++ ret = wifi_fwd_tx_lookup_entry(net_dev, skb);
++ return ret;
++}
++
++
++/*
++ return value:
++ 0: skb is handled by wifi_fwd module
++ 1: return to driver and bridge
++ 2: return to driver and release
++*/
++static int wifi_fwd_rx_handler(struct sk_buff *skb)
++{
++ struct net_device *net_dev = NULL;
++ struct net_device *target = NULL;
++ struct ethhdr *mh = eth_hdr(skb);
++
++ unsigned char type = ENTRY_TYPE_INVALID;
++ net_dev = skb->dev;
++
++ unsigned int recv_from = 0, band_from = 0, ret = 0;
++ recv_from = WIFI_FWD_GET_PACKET_RECV_FROM(skb, recv_from_cb_offset);
++ band_from = WIFI_FWD_GET_PACKET_BAND(skb, band_cb_offset);
++
++ /*
++ return this skb to driver and bridge while:
++ 1. path of WiFi forwarding is inactive
++ 2. the skb does not exist
++ 3. no forwarding connection is established
++ 4. the destination is bridge
++ 5. in FastLane topology
++ 6. handling multicast/broadcast Rx
++ 7. hit hijack
++ */
++ if (!WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACTIVE) || !WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ENABLED) || !skb || (fwd_counter == 0))
++ return 1;
++
++ if (IS_PACKET_FROM_APCLI(recv_from) && !MAIN_SSID_OP(net_dev))
++ return 2;
++
++ /* handle access schedule */
++ if (WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE))
++ {
++ if (IS_PACKET_FROM_APCLI(recv_from))
++ {
++ if (wifi_fwd_check_and_forward(skb) == FALSE)
++ return 2;
++ }
++ }
++
++ /* handle packets from bridge no matter unicast or broadcast */
++ if (MAC_ADDR_EQUAL(mh->h_dest, br_lan->dev_addr))
++ return 1;
++
++#if 0
++ if (((mh->h_dest[0] & 0x1) == 0x1) && ((mh->h_source[0] & 0x2) == 0x2))
++ {
++ void * adapter = NULL;
++
++ if (IS_PACKET_FROM_2G(band_from) && (adapter_2g != NULL))
++ adapter = adapter_2g;
++ else if (IS_PACKET_FROM_5G(band_from) && (adapter_5g != NULL))
++ adapter = adapter_5g;
++
++ if (IS_PACKET_FROM_APCLI(recv_from) && (adapter != NULL) &&
++ RTMPQueryLookupRepeaterCliEntry(adapter, mh->h_source) == TRUE)
++ return 2;
++ }
++#endif
++
++ /*
++ in FastLane topology:
++ 1. forward Rx packets to bridge and let flooding work done by bridge
++ 2. no matter unicast or multicast/broadcast
++ */
++ if (is_wifi_fastlane_mode(net_dev) && !GUEST_SSID_OP(net_dev))
++ return 1;
++
++ /* handle looping */
++ ret = wifi_fwd_check_looping(skb, net_dev);
++ if(ret == 1)
++ return 2;
++ else if(ret == 2)
++ return 1;
++
++ /* handle multicast/broadcast Rx */
++ if ((mh->h_dest[0] & 0x1) == 0x1)
++ return 1;
++
++ /* handle hijack */
++ if (WIFI_FWD_TEST_FLAG(fOP_WIFI_FWD_HIJACK_ACTIVE))
++ {
++ if (wifi_fwd_check_and_forward(skb) == TRUE)
++ {
++ WIFI_FWD_SET_PACKET_BAND(skb,WIFI_FWD_PACKET_SPECIFIC_TAG, band_cb_offset);
++ return 1;
++ }
++ }
++
++ target = wifi_fwd_lookup_entry(net_dev, &type, skb);
++
++ /* handle unicast Rx for non-FastLane cases */
++ if (target != NULL)
++ {
++#if 0 /* move to earlier and handle it */
++ /* in FastLane case, forward Rx packets to bridge and let flooding work done by bridge */
++ if (is_wifi_fastlane_mode(net_dev))
++ return 1;
++#endif
++ /* prevent from looping */
++ if (wifi_fwd_find_sa_entry_by_sa_and_nd(skb, net_dev, 0) == 0)
++ {
++ return 2;
++ }
++ else
++ {
++ unsigned char hit = 0;
++
++ /* forward to bridge if it is not a WiFi net device back-end packet */
++ hit = wifi_fwd_check_from_bridge_by_dest_addr(skb, net_dev);
++
++ if (hit == 0)
++ {
++ if (is_wifi_concurrent_mode(net_dev) &&
++ IS_PACKET_FROM_ETHER(band_from))
++ {
++ if (rep_net_dev == eth_traffic_band_5g)
++ {
++ if (REP_IS_5G(net_dev))
++ return 1;
++ else
++ return 2;
++ }
++ else if (rep_net_dev == eth_traffic_band_2g)
++ {
++ if (REP_IS_2G(net_dev))
++ return 1;
++ else
++ return 2;
++ }
++ }
++ else
++ return 1;
++ }
++ else if (hit == 2)
++ {
++ /* match inner communication case */
++ return 1;
++ }
++ else
++ {
++ skb_push(skb, ETH_HLEN);
++ skb->dev = target;
++ dev_queue_xmit(skb);
++ return 0;
++ }
++ }
++ }
++ else
++ return 1;
++}
++
++
++static int wifi_fwd_init_mod(void)
++{
++ if (!wifi_fwd_alloc_tbl(WIFI_FWD_TBL_SIZE)) {
++ return -ENOMEM; /* memory allocation failed */
++ }
++
++ WIFI_FWD_CLEAR_FLAG(fOP_GET_NET_DEVICE_STATUS_DONE);
++ WIFI_FWD_CLEAR_FLAG(fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE);
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ENABLED);
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_ACTIVE);
++ WIFI_FWD_SET_FLAG(fOP_WIFI_FWD_HIJACK_ACTIVE);
++
++ wifi_fwd_reset_link_count();
++
++ eth_rep5g_wrg_uni_cnt = 0;
++ eth_rep5g_wrg_bct_cnt = 0;
++ eth_rep2g_wrg_uni_cnt = 0;
++ eth_rep2g_wrg_bct_cnt = 0;
++ rep_net_dev = eth_traffic_band_5g;
++ band_cb_offset = DEFAULT_BAND_CB_OFFSET;
++ recv_from_cb_offset = DEFAULT_RECV_FROM_CB_OFFSET;
++ wf_debug_level = WF_DEBUG_OFF;
++
++ wf_fwd_tx_hook = wifi_fwd_tx_handler;
++ wf_fwd_rx_hook = wifi_fwd_rx_handler;
++ wf_fwd_entry_insert_hook = wifi_fwd_insert_entry;
++ wf_fwd_entry_delete_hook = wifi_fwd_delete_entry;
++ wf_fwd_set_cb_num = wifi_fwd_set_cb_num;
++ wf_fwd_get_rep_hook = wifi_fwd_get_rep;
++ wf_fwd_pro_active_hook = wifi_fwd_pro_active;
++ wf_fwd_pro_halt_hook = wifi_fwd_pro_halt;
++ wf_fwd_pro_enabled_hook = wifi_fwd_pro_enabled;
++ wf_fwd_pro_disabled_hook = wifi_fwd_pro_disabled;
++ wf_fwd_access_schedule_active_hook = wifi_fwd_access_schedule_active;
++ wf_fwd_access_schedule_halt_hook = wifi_fwd_access_schedule_halt;
++ wf_fwd_hijack_active_hook = wifi_fwd_hijack_active;
++ wf_fwd_hijack_halt_hook = wifi_fwd_hijack_halt;
++ wf_fwd_show_entry_hook = wifi_fwd_show_entry;
++ wf_fwd_needed_hook = wifi_fwd_needed;
++ wf_fwd_delete_entry_hook = wifi_fwd_delete_entry_by_idx;
++ packet_source_show_entry_hook = packet_source_show_entry;
++ packet_source_delete_entry_hook = packet_source_delete_entry;
++ wf_fwd_feedback_peer_adapter = wifi_fwd_feedback_peer_adapter;
++ wf_fwd_feedback_map_table = wifi_fwd_feedback_map_table;
++ wf_fwd_probe_adapter = wifi_fwd_probe_adapter;
++ wf_fwd_insert_bridge_mapping_hook = wifi_fwd_insert_bridge_mapping;
++ wf_fwd_insert_repeater_mapping_hook = wifi_fwd_insert_repeater_mapping;
++ wf_fwd_search_mapping_table_hook = wifi_fwd_search_mapping_table;
++ wf_fwd_delete_entry_inform_hook = wf_fwd_delete_entry_inform;
++ wf_fwd_debug_level_hook = wifi_fwd_set_debug_level;
++
++ return 0;
++}
++
++
++static void wifi_fwd_cleanup_mod(void)
++{
++ if (WiFiFwdBase)
++ kfree(WiFiFwdBase);
++ WiFiFwdBase = NULL;
++
++ if (pkt_src)
++ kfree(pkt_src);
++ pkt_src = NULL;
++
++ if(tx_src_tbl)
++ kfree(tx_src_tbl);
++ tx_src_tbl = NULL;
++
++ wf_fwd_tx_hook = NULL;
++ wf_fwd_rx_hook = NULL;
++ wf_fwd_entry_insert_hook = NULL;
++ wf_fwd_entry_delete_hook = NULL;
++ wf_fwd_set_cb_num = NULL;
++ wf_fwd_get_rep_hook = NULL;
++ wf_fwd_pro_active_hook = NULL;
++ wf_fwd_pro_halt_hook = NULL;
++ wf_fwd_pro_enabled_hook = NULL;
++ wf_fwd_pro_disabled_hook = NULL;
++ wf_fwd_access_schedule_active_hook = NULL;
++ wf_fwd_access_schedule_halt_hook = NULL;
++ wf_fwd_hijack_active_hook = NULL;
++ wf_fwd_hijack_halt_hook = NULL;
++ wf_fwd_show_entry_hook = NULL;
++ wf_fwd_needed_hook = NULL;
++ wf_fwd_delete_entry_hook = NULL;
++ packet_source_show_entry_hook = NULL;
++ packet_source_delete_entry_hook = NULL;
++ wf_fwd_feedback_peer_adapter = NULL;
++ wf_fwd_feedback_map_table = NULL;
++ wf_fwd_probe_adapter = NULL;
++ wf_fwd_insert_bridge_mapping_hook = NULL;
++ wf_fwd_insert_repeater_mapping_hook = NULL;
++ wf_fwd_search_mapping_table_hook = NULL;
++ wf_fwd_delete_entry_inform_hook = NULL;
++ wf_fwd_debug_level_hook = NULL;
++
++ return;
++}
++
++
++module_init(wifi_fwd_init_mod);
++module_exit(wifi_fwd_cleanup_mod);
++
++MODULE_AUTHOR("MediaTek Inc");
++MODULE_LICENSE("Proprietary");
++MODULE_DESCRIPTION("MediaTek WiFi Packet Forwarding Module\n");
++
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd_v1/wifi_fwd.h
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_fwd_v1/wifi_fwd.h
+@@ -0,0 +1,276 @@
++/****************************************************************************
++ * Mediatek Inc.
++ * 5F., No.5, Taiyuan 1st St., Zhubei City,
++ * Hsinchu County 302, Taiwan, R.O.C.
++ * (c) Copyright 2014, Mediatek, Inc.
++ *
++ * All rights reserved. Ralink's source code is an unpublished work and the
++ * use of a copyright notice does not imply otherwise. This source code
++ * contains confidential trade secret material of Ralink Tech. Any attemp
++ * or participation in deciphering, decoding, reverse engineering or in any
++ * way altering the source code is stricitly prohibited, unless the prior
++ * written consent of Ralink Technology, Inc. is obtained.
++ ****************************************************************************
++
++ Module Name:
++ wifi_fwd.h
++
++ Abstract:
++
++ Revision History:
++ Who When What
++ -------- ---------- ----------------------------------------------
++ Annie Lu 2014-06-30 Initial version
++*/
++
++#ifndef __WF_FWD_H__
++#define __WF_FWD_H__
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/kernel.h>
++#include <linux/net.h>
++#include <linux/netdevice.h>
++#include <linux/ip.h>
++#include <linux/ipv6.h>
++#include <linux/udp.h>
++#include <linux/tcp.h>
++
++
++#ifndef TRUE
++#define TRUE 1
++#endif
++
++#ifndef FALSE
++#define FALSE 0
++#endif
++
++/* enternal symbol */
++extern int (*wf_fwd_rx_hook) (struct sk_buff *skb);
++extern int (*wf_fwd_tx_hook) (struct sk_buff *skb);
++
++extern unsigned char (*wf_fwd_entry_insert_hook) (struct net_device *src, struct net_device *dest, void *adapter);
++extern unsigned char (*wf_fwd_entry_delete_hook) (struct net_device *src, struct net_device *dest, unsigned char link_down);
++extern void (*wf_fwd_set_cb_num) (unsigned int band_cb_num, unsigned int receive_cb_num);
++
++extern void (*wf_fwd_get_rep_hook) (unsigned char idx);
++extern void (*wf_fwd_pro_active_hook) (void);
++extern void (*wf_fwd_pro_halt_hook) (void);
++extern void (*wf_fwd_pro_enabled_hook) (void);
++extern void (*wf_fwd_pro_disabled_hook) (void);
++extern void (*wf_fwd_access_schedule_active_hook) (void);
++extern void (*wf_fwd_access_schedule_halt_hook) (void);
++extern void (*wf_fwd_hijack_active_hook) (void);
++extern void (*wf_fwd_hijack_halt_hook) (void);
++
++extern void (*wf_fwd_show_entry_hook) (void);
++extern bool (*wf_fwd_needed_hook) (void);
++extern void (*wf_fwd_delete_entry_hook) (unsigned char idx);
++extern void (*packet_source_show_entry_hook) (void);
++extern void (*packet_source_delete_entry_hook) (unsigned char idx);
++
++extern void (*wf_fwd_feedback_peer_adapter) (void *adapter, void *peer);
++extern void (*wf_fwd_feedback_map_table) (void *adapter, void *peer, void *opp_peer);
++extern void (*wf_fwd_probe_adapter) (void *adapter);
++extern void (*wf_fwd_insert_repeater_mapping_hook)(void *adapter, void *lock, void *cli_mapping, void *map_mapping, void *ifAddr_mapping);
++extern void (*wf_fwd_insert_bridge_mapping_hook) (struct sk_buff *skb);
++extern int (*wf_fwd_search_mapping_table_hook) (struct sk_buff *skb, struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT **tbl_entry);
++extern void (*wf_fwd_delete_entry_inform_hook) (unsigned char *addr);
++extern void (*wf_fwd_debug_level_hook) (unsigned char level);
++
++#ifndef ETH_ALEN
++#define ETH_ALEN 6
++#endif
++#define MAC_ADDR_LEN 6
++
++#define fOP_GET_NET_DEVICE_STATUS_DONE 0x00000001
++#define fOP_WIFI_FWD_ENABLED 0x00000002
++#define fOP_WIFI_FWD_ACTIVE 0x00000010
++#define fOP_WIFI_FWD_ACCESS_SCHED_ACTIVE 0x00000100
++#define fOP_WIFI_FWD_HIJACK_ACTIVE 0x00001000
++
++#define WIFI_FWD_SET_FLAG(_F) (wifi_fwd_op_flag |= (_F))
++#define WIFI_FWD_CLEAR_FLAG(_F) (wifi_fwd_op_flag &= ~(_F))
++#define WIFI_FWD_CLEAR_FLAGS (wifi_fwd_op_flag = 0)
++#define WIFI_FWD_TEST_FLAG(_F) ((wifi_fwd_op_flag & (_F)) != 0)
++#define WIFI_FWD_TEST_FLAGS(_F) ((wifi_fwd_op_flag & (_F)) == (_F))
++
++#define REP_IS_5G(_p) (((_p) == apcli_5g) || ((_p) == ap_5g) || ((_p) == apcli1_5g) || ((_p) == ap1_5g))
++#define REP_IS_2G(_p) (((_p) == apcli_2g) || ((_p) == ap_2g) || ((_p) == apcli1_2g) || ((_p) == ap1_2g))
++#define CMP_TO_REP(_p, _x) (REP_IS_##_x##G(_p))
++
++#define IS_MAIN_SSID_DEV(_p) (((_p) == ap_2g) || ((_p) == ap_5g) || ((_p) == apcli_2g) || ((_p) == apcli_5g))
++#define IS_GUEST_SSID_DEV(_p) (((_p) == ap1_2g) || ((_p) == ap1_5g) || ((_p) == apcli1_2g) || ((_p) == apcli1_5g))
++
++#define MAIN_SSID_OP(_p) ((main_2g_link_cnt != 0) || (main_5g_link_cnt != 0))
++#define GUEST_SSID_OP(_p) ((guest_2g_link_cnt != 0) || (guest_5g_link_cnt != 0))
++
++#define WIFI_FWD_TBL_SIZE 50
++
++#define ENTRY_TYPE_INVALID 0
++#define ENTRY_TYPE_SRC 1
++#define ENTRY_TYPE_DEST 2
++
++/* macro */
++#define dbg_print printk
++#define WF_DEBUG_OFF 0
++#define WF_DEBUG_TRACE 1
++#define WF_DEBUG_ON 2
++
++extern unsigned char wf_debug_level;
++
++#define WF_FWD_PRINT(Level, Fmt) \
++do{ \
++ unsigned char __gLevel = (Level);\
++ if (__gLevel <= wf_debug_level) \
++ { \
++ printk Fmt; \
++ } \
++}while(0)
++
++/* CB related */
++#define CB_OFF 10
++#define RTPKT_TO_OSPKT(_p) ((struct sk_buff *)(_p))
++#define GET_OS_PKT_CB(_p) (RTPKT_TO_OSPKT(_p)->cb)
++#define PACKET_CB(_p, _offset) ((RTPKT_TO_OSPKT(_p)->cb[CB_OFF + (_offset)]))
++
++/* [CB_OFF + 34]: tag the packet is sent by which band */
++#define DEFAULT_BAND_CB_OFFSET 34
++#define WIFI_FWD_PACKET_SPECIFIC_2G 0x1
++#define WIFI_FWD_PACKET_SPECIFIC_5G 0x2
++#define WIFI_FWD_PACKET_SPECIFIC_ETHER 0x4
++#define WIFI_FWD_PACKET_SPECIFIC_TAG 0x8
++
++
++#define WIFI_FWD_SET_PACKET_BAND(_p, _offset, _flg) \
++ do{ \
++ if (_flg) \
++ PACKET_CB(_p, _offset) |= (_flg); \
++ else \
++ PACKET_CB(_p, _offset) &= (~_flg); \
++ }while(0)
++
++#define WIFI_FWD_GET_PACKET_BAND(_p, _offset) (PACKET_CB(_p, _offset))
++
++/* [CB_OFF + 35]: tag the packet received from which net device */
++#define DEFAULT_RECV_FROM_CB_OFFSET 35
++#define WIFI_FWD_PACKET_RECV_FROM_2G_CLIENT 0x01
++#define WIFI_FWD_PACKET_RECV_FROM_5G_CLIENT 0x02
++#define WIFI_FWD_PACKET_RECV_FROM_2G_AP 0x04
++#define WIFI_FWD_PACKET_RECV_FROM_5G_AP 0x08
++#define WIFI_FWD_PACKET_RECV_FROM_2G_GUEST_CLIENT 0x10
++#define WIFI_FWD_PACKET_RECV_FROM_5G_GUEST_CLIENT 0x20
++#define WIFI_FWD_PACKET_RECV_FROM_2G_GUEST_AP 0x40
++#define WIFI_FWD_PACKET_RECV_FROM_5G_GUEST_AP 0x80
++
++
++#define WIFI_FWD_SET_PACKET_RECV_FROM(_p, _offset, _flg) \
++ do{ \
++ if (_flg) \
++ PACKET_CB(_p, _offset) |= (_flg); \
++ else \
++ PACKET_CB(_p, _offset) &= (~_flg); \
++ }while(0)
++
++#define WIFI_FWD_GET_PACKET_RECV_FROM(_p, _offset) (PACKET_CB(_p, _offset))
++
++#define IS_PACKET_FROM_APCLI(_x) \
++ ((((_x) & WIFI_FWD_PACKET_RECV_FROM_2G_CLIENT) == WIFI_FWD_PACKET_RECV_FROM_2G_CLIENT) ||\
++ (((_x) & WIFI_FWD_PACKET_RECV_FROM_5G_CLIENT) == WIFI_FWD_PACKET_RECV_FROM_5G_CLIENT) ||\
++ (((_x) & WIFI_FWD_PACKET_RECV_FROM_2G_GUEST_CLIENT) == WIFI_FWD_PACKET_RECV_FROM_2G_GUEST_CLIENT) ||\
++ (((_x) & WIFI_FWD_PACKET_RECV_FROM_5G_GUEST_CLIENT) == WIFI_FWD_PACKET_RECV_FROM_5G_GUEST_CLIENT))
++
++#define IS_PACKET_FROM_2G(_x) \
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_2G) == WIFI_FWD_PACKET_SPECIFIC_2G)
++
++#define IS_PACKET_FROM_5G(_x) \
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_5G) == WIFI_FWD_PACKET_SPECIFIC_5G)
++
++#define IS_TAG_PACKET(_x) \
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_TAG) == WIFI_FWD_PACKET_SPECIFIC_TAG)
++
++#define IS_PACKET_FROM_ETHER(_x) \
++ ((((_x) & WIFI_FWD_PACKET_SPECIFIC_2G) != WIFI_FWD_PACKET_SPECIFIC_2G) &&\
++ (((_x) & WIFI_FWD_PACKET_SPECIFIC_5G) != WIFI_FWD_PACKET_SPECIFIC_5G))
++
++#define IS_PACKET_FROM_MAIN_SSID(_x) \
++ (((_x) & 0x0F) != 0x0)
++
++#define IS_PACKET_FROM_GUEST_SSID(_x) \
++ (((_x) & 0x0F) == 0x0)
++
++
++
++typedef enum _ETHER_BAND_BINDDING {
++ eth_traffic_band_2g = 2,
++ eth_traffic_band_5g = 5,
++} ETHER_BAND_BINDDING, *PETHER_BAND_BINDDING;
++
++/* data structure */
++struct FwdPair {
++ unsigned char valid; /* 1: valid, 0: invalid */
++ struct net_device *src;
++ struct net_device *dest;
++};
++
++struct PacketSource {
++ unsigned char valid; /* 1: valid, 0: invalid */
++ struct net_device *peer;
++ struct net_device *src;
++ unsigned char h_source[ETH_ALEN];
++ unsigned char h_dest[ETH_ALEN];
++};
++
++struct TxSourceEntry {
++ unsigned char valid; /* 1: valid, 0: invalid */
++ unsigned char h_source[ETH_ALEN];
++};
++
++struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT {
++ struct net_device *rcvd_net_dev;
++ unsigned char src_addr[ETH_ALEN];
++ unsigned char entry_from;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *pBefore;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *pNext;
++};
++
++struct APCLI_BRIDGE_LEARNING_MAPPING_MAP {
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *pHead;
++ struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT *pTail;
++ unsigned int entry_num;
++};
++
++
++typedef struct _REPEATER_ADAPTER_DATA_TABLE {
++ bool Enabled;
++ void *EntryLock;
++ void **CliHash;
++ void **MapHash;
++ void *Wdev_ifAddr;
++} REPEATER_ADAPTER_DATA_TABLE;
++
++
++#define FWD_IRQ_LOCK(__lock, __irqflags) \
++{ \
++ __irqflags = 0; \
++ spin_lock_irqsave((spinlock_t *)(__lock), __irqflags); \
++}
++
++#define FWD_IRQ_UNLOCK(__lock, __irqflag) \
++{ \
++ spin_unlock_irqrestore((spinlock_t *)(__lock), __irqflag); \
++}
++
++#define PRINT_MAC(addr) \
++ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
++
++#define MAC_ADDR_EQUAL(addr1,addr2) (!memcmp((void *)(addr1), (void *)(addr2), ETH_ALEN))
++#define COPY_MAC_ADDR(addr1, addr2) (memcpy((addr1), (addr2), ETH_ALEN))
++
++#define WIFI_FWD_NETDEV_GET_DEVNAME(_pNetDev) ((_pNetDev)->name)
++#define WIFI_FWD_NETDEV_GET_PHYADDR(_pNetDev) ((_pNetDev)->dev_addr)
++
++#define SET_OS_PKT_NETDEV(_pkt, _pNetDev) \
++ (RTPKT_TO_OSPKT(_pkt)->dev) = (_pNetDev)
++#endif
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_hook_v1/Makefile
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_hook_v1/Makefile
+@@ -0,0 +1,2 @@
++obj-y += wifi_hook.o
++wifi_hook-objs := hook.o
+Index: linux-3.10.20/drivers/misc/mediatek/Kconfig.wcn
+===================================================================
+--- linux-3.10.20.orig/drivers/misc/mediatek/Kconfig.wcn
++++ linux-3.10.20/drivers/misc/mediatek/Kconfig.wcn
+@@ -198,6 +198,17 @@ source "drivers/net/wireless/mt_wifi/emb
+
+ endif # WIFI_DRIVER
+
++source "drivers/net/wireless/wifi_forward/wifi_fwd/Kconfig"
++
++config RTDEV
++ bool
++ default y if WIFI_DRIVER && !SECOND_IF_NONE || RTDEV_MII
++ default y if RTDEV_PLC
++
++config WIFI_PKT_FWD
++ bool "WiFi packet forwarding"
++ depends on WIFI_DRIVER
++ default n
+
+ config RTDEV
+ bool
+Index: linux-3.10.20/drivers/net/wireless/Makefile
+===================================================================
+--- linux-3.10.20.orig/drivers/net/wireless/Makefile
++++ linux-3.10.20/drivers/net/wireless/Makefile
+@@ -62,6 +62,20 @@ obj-$(CONFIG_MTK_CONNSYS_WIFI) += mt_soc
+ ifneq ($(CONFIG_ARCH_MT6595), y)
+ #obj-$(CONFIG_MTK_COMBO_WIFI) += mt66xx/
+ endif
++
++ifneq ($(CONFIG_WIFI_PKT_FWD),)
++ifneq ($(CONFIG_WIFI_PKT_FWD_V1),)
++obj-y += wifi_forward/wifi_hook_v1/
++else
++obj-y += wifi_forward/wifi_hook/
++endif
++endif
++
++ifneq ($(CONFIG_WIFI_PKT_FWD_V1),)
++obj-$(CONFIG_WIFI_PKT_FWD) += wifi_forward/wifi_fwd_v1/
++else
++obj-$(CONFIG_WIFI_PKT_FWD) += wifi_forward/wifi_fwd/
++endif
+ ifneq ($(CONFIG_RLT_AP_SUPPORT),)
+ obj-$(CONFIG_RLT_AP_SUPPORT) += rlt_wifi_ap/
+ endif
+@@ -74,3 +88,8 @@ endif
+ ifneq ($(CONFIG_MT_STA_SUPPORT),)
+ obj-$(CONFIG_MT_STA_SUPPORT) += mt_wifi_sta/
+ endif
++
++#ifneq ($(CONFIG_WIFI_PKT_FWD),)
++#obj-y += wifi_forward/wifi_hook/
++#endif
++#obj-$(CONFIG_WIFI_PKT_FWD) += wifi_forward/wifi_fwd/
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_hook/Makefile
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_hook/Makefile
+@@ -0,0 +1,2 @@
++obj-y += wifi_hook.o
++wifi_hook-objs := hook.o
+Index: linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_hook/hook.c
+===================================================================
+--- /dev/null
++++ linux-3.10.20/drivers/net/wireless/wifi_forward/wifi_hook/hook.c
+@@ -0,0 +1,101 @@
++/****************************************************************************
++ * Mediatek Inc.
++ * 5F., No.5, Taiyuan 1st St., Zhubei City,
++ * Hsinchu County 302, Taiwan, R.O.C.
++ * (c) Copyright 2014, Mediatek, Inc.
++ *
++ * All rights reserved. Ralink's source code is an unpublished work and the
++ * use of a copyright notice does not imply otherwise. This source code
++ * contains confidential trade secret material of Ralink Tech. Any attemp
++ * or participation in deciphering, decoding, reverse engineering or in any
++ * way altering the source code is stricitly prohibited, unless the prior
++ * written consent of Ralink Technology, Inc. is obtained.
++ ****************************************************************************
++
++ Module Name:
++ hook.c
++
++ Abstract:
++
++ Revision History:
++ Who When What
++ -------- ---------- ----------------------------------------------
++ Name Date Modification logs
++ Annie Lu 2014-06-30 Initial version
++*/
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/skbuff.h>
++#include <linux/net.h>
++#include <linux/netdevice.h>
++
++
++int (*wf_fwd_rx_hook) (struct sk_buff * skb) = NULL;
++int (*wf_fwd_tx_hook) (struct sk_buff * skb) = NULL;
++
++unsigned char (*wf_fwd_entry_insert_hook) (struct net_device *src, struct net_device *dest, void *adapter) = NULL;
++unsigned char (*wf_fwd_entry_delete_hook) (struct net_device *src, struct net_device *dest, unsigned char link_down) = NULL;
++void (*wf_fwd_set_cb_num) (unsigned int band_cb_num, unsigned int receive_cb_num) = NULL;
++bool (*wf_fwd_check_active_hook) (void) = NULL;
++
++void (*wf_fwd_get_rep_hook) (unsigned char idx) = NULL;
++void (*wf_fwd_pro_active_hook) (void) = NULL;
++void (*wf_fwd_pro_halt_hook) (void) = NULL;
++void (*wf_fwd_pro_enabled_hook) (void) = NULL;
++void (*wf_fwd_pro_disabled_hook) (void) = NULL;
++
++void (*wf_fwd_access_schedule_active_hook) (void) = NULL;
++void (*wf_fwd_access_schedule_halt_hook) (void) = NULL;
++void (*wf_fwd_hijack_active_hook) (void) = NULL;
++void (*wf_fwd_hijack_halt_hook) (void) = NULL;
++
++void (*wf_fwd_show_entry_hook) (void) = NULL;
++bool (*wf_fwd_needed_hook) (void) = NULL;
++void (*packet_source_show_entry_hook) (void) = NULL;
++void (*packet_source_delete_entry_hook) (unsigned char idx) = NULL;
++void (*wf_fwd_delete_entry_hook) (unsigned char idx) = NULL;
++
++void (*wf_fwd_feedback_map_table) (void *adapter, void *peer, void *opp_peer, void *oth_peer) = NULL;
++void (*wf_fwd_probe_adapter)(void *adapter) = NULL;
++void (*wf_fwd_insert_repeater_mapping_hook)(void *adapter, void *lock, void *cli_mapping, void *map_mapping, void *ifAddr_mapping) = NULL;
++void (*wf_fwd_insert_bridge_mapping_hook) (struct sk_buff *skb) = NULL;
++int (*wf_fwd_search_mapping_table_hook) (struct sk_buff *skb, struct APCLI_BRIDGE_LEARNING_MAPPING_STRUCT **tbl_entry) = NULL;
++void (*wf_fwd_delete_entry_inform_hook) (unsigned char *addr) = NULL;
++void (*wf_fwd_check_device_hook) ( struct net_device *net_dev, signed int type, signed int mbss_idx, unsigned char channel, unsigned char link) = NULL;
++void (*wf_fwd_add_entry_inform_hook) (unsigned char *addr) = NULL;
++void (*wf_fwd_debug_level_hook) (unsigned char level) = NULL;
++
++
++EXPORT_SYMBOL(wf_fwd_rx_hook);
++EXPORT_SYMBOL(wf_fwd_tx_hook);
++EXPORT_SYMBOL(wf_fwd_entry_insert_hook);
++EXPORT_SYMBOL(wf_fwd_entry_delete_hook);
++EXPORT_SYMBOL(wf_fwd_set_cb_num);
++EXPORT_SYMBOL(wf_fwd_check_active_hook);
++EXPORT_SYMBOL(wf_fwd_get_rep_hook);
++EXPORT_SYMBOL(wf_fwd_pro_active_hook);
++EXPORT_SYMBOL(wf_fwd_pro_halt_hook);
++EXPORT_SYMBOL(wf_fwd_pro_enabled_hook);
++EXPORT_SYMBOL(wf_fwd_pro_disabled_hook);
++EXPORT_SYMBOL(wf_fwd_access_schedule_active_hook);
++EXPORT_SYMBOL(wf_fwd_access_schedule_halt_hook);
++EXPORT_SYMBOL(wf_fwd_hijack_active_hook);
++EXPORT_SYMBOL(wf_fwd_hijack_halt_hook);
++EXPORT_SYMBOL(wf_fwd_show_entry_hook);
++EXPORT_SYMBOL(wf_fwd_needed_hook);
++EXPORT_SYMBOL(wf_fwd_delete_entry_hook);
++EXPORT_SYMBOL(packet_source_show_entry_hook);
++EXPORT_SYMBOL(packet_source_delete_entry_hook);
++EXPORT_SYMBOL(wf_fwd_feedback_map_table);
++EXPORT_SYMBOL(wf_fwd_insert_repeater_mapping_hook);
++EXPORT_SYMBOL(wf_fwd_probe_adapter);
++EXPORT_SYMBOL(wf_fwd_insert_bridge_mapping_hook);
++EXPORT_SYMBOL(wf_fwd_search_mapping_table_hook);
++EXPORT_SYMBOL(wf_fwd_delete_entry_inform_hook);
++EXPORT_SYMBOL(wf_fwd_check_device_hook);
++EXPORT_SYMBOL(wf_fwd_add_entry_inform_hook);
++EXPORT_SYMBOL(wf_fwd_debug_level_hook);
++