phonepad: commit phonepad(i30) rda code
authorlinjh <linjh@rock-chips.com>
Thu, 27 Sep 2012 09:39:53 +0000 (17:39 +0800)
committerlinjh <linjh@rock-chips.com>
Thu, 27 Sep 2012 10:04:00 +0000 (18:04 +0800)
[reference file]
modified:
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
new file:
drivers/net/wireless/rda5990/Makefile
drivers/net/wireless/rda5990/drv_fm_rda/Makefile
drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv.c
drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv_gpio.c
drivers/net/wireless/rda5990/rda_5990_power_ctrl/Makefile
drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl.c
drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl_by_gpio.c
drivers/net/wireless/rda5990/rda_gpio_i2c/Makefile
drivers/net/wireless/rda5990/rda_gpio_i2c/rda_gpio_i2c.c
drivers/net/wireless/rda5990/rda_wlan/Makefile
drivers/net/wireless/rda5990/rda_wlan/Makefile_gwl
drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.c
drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.h
drivers/net/wireless/rda5990/rda_wlan/rda5890_defs.h
drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.c
drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.h
drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.c
drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.h
drivers/net/wireless/rda5990/rda_wlan/rda5890_ioctl.h
drivers/net/wireless/rda5990/rda_wlan/rda5890_nvram.c
drivers/net/wireless/rda5990/rda_wlan/rda5890_scan.c
drivers/net/wireless/rda5990/rda_wlan/rda5890_sdio_patch.c
drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.c
drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.h
drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.c
drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.h
drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.c
drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.h

30 files changed:
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/rda5990/Makefile [new file with mode: 0755]
drivers/net/wireless/rda5990/drv_fm_rda/Makefile [new file with mode: 0755]
drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv.c [new file with mode: 0755]
drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv_gpio.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_5990_power_ctrl/Makefile [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl_by_gpio.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_gpio_i2c/Makefile [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_gpio_i2c/rda_gpio_i2c.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/Makefile [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/Makefile_gwl [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.h [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_defs.h [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.h [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.h [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_ioctl.h [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_nvram.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_scan.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_sdio_patch.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.h [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.h [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.c [new file with mode: 0755]
drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.h [new file with mode: 0755]

index 792268c67c2ec3551464f5a44d1398f63cf7c577..2acbb3f3e75b9023d5e5d34cf2bd58ca88b1d35a 100755 (executable)
@@ -64,7 +64,15 @@ choice
           bool "MTK MT6620 WiFi SDIO"
           ---help---
             A library for MTK_wcn_combo SDIO WLAN devices.
-
+       config RDA5990
+         depends on WLAN_80211 && MMC
+         select WIRELESS_EXT
+         select WEXT_PRIV
+         select IEEE80211
+         bool "rda 5990p"
+         ---help---
+           rda5990P fm bt wifi
+           
        config MV8686
          depends on WLAN_80211 && MMC
          select WIRELESS_EXT
index 669b28b90009934b25c29e3f4074d98705012647..08143cc16a8b88f39af43d04f1b4f63b11401868 100644 (file)
@@ -9,4 +9,5 @@ obj-$(CONFIG_RTL8192CU) += rkusbwifi/
 obj-$(CONFIG_RTL8188EU)        += rkusbwifi/
 obj-$(CONFIG_AR6003)   += ar6003/
 obj-$(CONFIG_RKWIFI)   += rkwifi/
+obj-$(CONFIG_RDA5990) += rda5990/
 #obj-m += wlan/
diff --git a/drivers/net/wireless/rda5990/Makefile b/drivers/net/wireless/rda5990/Makefile
new file mode 100755 (executable)
index 0000000..0b0494d
--- /dev/null
@@ -0,0 +1,5 @@
+#obj-y += rda_gpio_i2c/ # 模拟I2C开此功能
+#obj-y += drv_fm_rda/
+
+obj-y += rda_wlan/
+obj-y += rda_5990_power_ctrl/
diff --git a/drivers/net/wireless/rda5990/drv_fm_rda/Makefile b/drivers/net/wireless/rda5990/drv_fm_rda/Makefile
new file mode 100755 (executable)
index 0000000..4eaffa4
--- /dev/null
@@ -0,0 +1,49 @@
+# Copyright Statement:
+#
+# This software/firmware and related documentation ("MediaTek Software") are
+# protected under relevant copyright laws. The information contained herein
+# is confidential and proprietary to MediaTek Inc. and/or its licensors.
+# Without the prior written permission of MediaTek inc. and/or its licensors,
+# any reproduction, modification, use or disclosure of MediaTek Software,
+# and information contained herein, in whole or in part, shall be strictly prohibited.
+#
+# MediaTek Inc. (C) 2010. All rights reserved.
+#
+# BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+# THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+# RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+# AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+# NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+# SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+# SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+# THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+# THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+# CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+# SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+# STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+# CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+# AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+# OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+# MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+#
+# The following software/firmware and/or related documentation ("MediaTek Software")
+# have been modified by MediaTek Inc. All revisions are subject to any receiver's
+# applicable license agreements with MediaTek Inc.
+
+
+# Makefile generated by Mediatek
+# rda fm support
+
+ifeq ($(MTK_FM_SUPPORT), yes)
+ifeq ($(CUSTOM_KERNEL_FM), mt6620)
+
+#obj-$(CONFIG_MTK_COMBO_FM) += RDA5990_FM_drv.o
+# for 模拟I2C
+obj-$(CONFIG_MTK_COMBO_FM) += RDA5990_FM_drv_gpio.o
+
+endif
+endif
+
+
diff --git a/drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv.c b/drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv.c
new file mode 100755 (executable)
index 0000000..cfd9f1b
--- /dev/null
@@ -0,0 +1,2142 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h> // udelay()
+#include <linux/device.h> // device_create()
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/version.h>      /* constant of kernel version */
+#include <asm/uaccess.h> // get_user()
+
+#include <linux/fm.h>
+#include <mach/mt6575_gpio.h>
+#include <mach/mtk_rtc.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+// if need debug, define FMDEBUG
+//#define FMDEBUG
+
+// if your platform is MT6515/6575, define MTK_MT6515 
+#define MTK_MT6515
+
+// if your platform is MT6515/6575 and MTK FM is MT6626, define MT6626 
+//#define MT6626
+
+#define FM_ALERT(f, s...) \
+       do { \
+               printk(KERN_ALERT "RDAFM " f, ## s); \
+       } while(0)
+
+#ifdef FMDEBUG
+#define FM_DEBUG(f, s...) \
+       do { \
+               printk("RDAFM " f, ## s); \
+       } while(0)
+#else
+#define FM_DEBUG(f, s...)
+#endif
+
+#define RDA599X_SCANTBL_SIZE  16 //16*uinit16_t
+#define RDA599X_FM_SCAN_UP       0x0
+#define RDA599X_FM_SCAN_DOWN     0x01
+
+extern int rda_fm_power_off();
+extern int rda_fm_power_on();
+
+/******************************************************************************
+ * CONSTANT DEFINITIONS
+ *****************************************************************************/
+#define RDAFM_SLAVE_ADDR        (0x11 << 1)    //RDA FM Chip address
+
+#define RDAFM_MASK_RSSI         0X7F // RSSI
+#define RDAFM_DEV               "RDA599x"
+
+//customer need customize the I2C port
+#define RDAFM_I2C_PORT          0
+
+
+#define ID_RDA5802E             0x5804
+#define ID_RDA5802H             0x5801
+#define ID_RDA5802N             0x5808
+#define ID_RDA5820              0x5805
+#define ID_RDA5820NS            0x5820
+
+
+static struct proc_dir_entry *g_fm_proc = NULL;
+static struct fm *g_fm_struct = NULL;
+static atomic_t scan_complete_flag;
+
+#define FM_PROC_FILE "fm"
+
+/******************************************************************************
+ * STRUCTURE DEFINITIONS
+ *****************************************************************************/
+
+enum RDAFM_CHIP_TYPE {
+       CHIP_TYPE_RDA5802E = 0,
+       CHIP_TYPE_RDA5802H,
+       CHIP_TYPE_RDA5802N,
+       CHIP_TYPE_RDA5820,
+       CHIP_TYPE_RDA5820NS,
+};
+
+
+typedef struct
+{
+       uint8_t         address;
+       uint16_t        value;
+}RDA_FM_REG_T;   
+
+typedef struct
+{
+       bool            byPowerUp;
+       struct fm_tune_parm parm
+}FM_TUNE_T;
+static FM_TUNE_T fm_tune_data = {false, {}};
+
+typedef enum
+{
+       FM_RECEIVER,                            //5800,5802,5804
+       FM_TRANSMITTER,                 //5820
+}RDA_RADIO_WORK_E;
+
+typedef enum
+{
+       OFF,
+       ON,
+}RDA_FM_POWER_STATE_T;
+
+struct fm {
+       uint32_t ref;
+       bool powerup;
+       uint16_t chip_id;
+       uint16_t device_id;
+       dev_t dev_t;
+       uint16_t min_freq; // KHz
+       uint16_t max_freq; // KHz
+       uint8_t band;   // TODO
+       struct class *cls;
+       struct device *dev;
+       struct cdev cdev;
+       struct i2c_client *i2c_client;
+};
+
+
+
+
+/******************************************************************************
+ * FUNCTION PROTOTYPES
+ *****************************************************************************/
+
+static int RDAFM_clear_hmute(struct i2c_client *client);
+static int RDAFM_enable_hmute(struct i2c_client *client);
+static int RDAFM_clear_tune(struct i2c_client *client);
+static int RDAFM_enable_tune(struct i2c_client *client);
+static int RDAFM_clear_seek(struct i2c_client *client);
+static int RDAFM_enable_seek(struct i2c_client *client);
+static int RDAFM_SetStereo(struct i2c_client *client,uint8_t b);
+static int RDAFM_SetRSSI_Threshold(struct i2c_client *client,uint8_t RssiThreshold);
+static int RDAFM_SetDe_Emphasis(struct i2c_client *client,uint8_t index);
+static bool RDAFM_Scan(struct i2c_client *client,
+               uint16_t min_freq, uint16_t max_freq,
+               uint16_t *pFreq, //get the valid freq after scan
+               uint16_t *pScanTBL,
+               uint16_t *ScanTBLsize,
+               uint16_t scandir,
+               uint16_t space);
+
+
+static int RDAFM_read(struct i2c_client *client, uint8_t addr, uint16_t *val);
+static int RDAFM_write(struct i2c_client *client, uint8_t addr, uint16_t val);
+static void RDAFM_em_test(struct i2c_client *client, uint16_t group_idx, uint16_t item_idx, uint32_t item_value);
+static int fm_setup_cdev(struct fm *fm);
+static int fm_ops_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+static loff_t fm_ops_lseek(struct file *filp, loff_t off, int whence);
+static int fm_ops_open(struct inode *inode, struct file *filp);
+static int fm_ops_release(struct inode *inode, struct file *filp);
+
+static int fm_init(struct i2c_client *client);
+static int fm_destroy(struct fm *fm);
+static int fm_powerup(struct fm *fm, struct fm_tune_parm *parm);
+static int fm_powerdown(struct fm *fm);
+
+static int fm_tune(struct fm *fm, struct fm_tune_parm *parm);
+static int fm_seek(struct fm *fm, struct fm_seek_parm *parm);
+static int fm_scan(struct fm *fm, struct fm_scan_parm *parm);
+static int fm_setvol(struct fm *fm, uint32_t vol);
+static int fm_getvol(struct fm *fm, uint32_t *vol);
+static int fm_getrssi(struct fm *fm, uint32_t *rssi);
+static int fm_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+static int fm_i2c_attach_adapter(struct i2c_adapter *adapter);
+static int fm_i2c_detect(struct i2c_adapter *adapter, int addr, int kind);
+static int fm_i2c_detach_client(struct i2c_client *client);
+#else
+static int fm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id);
+static int fm_i2c_detect(struct i2c_client *client, int kind, struct i2c_board_info *info);
+static int fm_i2c_remove(struct i2c_client *client);
+#endif
+
+/******************************************************************************
+ * GLOBAL DATA
+ *****************************************************************************/
+/* Addresses to scan */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+static unsigned short normal_i2c[] = {RDAFM_SLAVE_ADDR, I2C_CLIENT_END};
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data RDAFM_addr_data = {
+       .normal_i2c = normal_i2c,
+       .probe = &ignore,
+       .ignore = &ignore,
+};
+#else
+static const struct i2c_device_id fm_i2c_id = {RDAFM_DEV, 0};
+static unsigned short force[] = {RDAFM_I2C_PORT, RDAFM_SLAVE_ADDR, I2C_CLIENT_END, I2C_CLIENT_END};
+static const unsigned short * const forces[] = {force, NULL};
+//static struct i2c_client_address_data addr_data = {.forces = forces};
+static struct i2c_board_info __initdata i2c_rdafm={ I2C_BOARD_INFO(RDAFM_DEV, (RDAFM_SLAVE_ADDR>>1))};
+#endif
+
+       static struct i2c_driver RDAFM_driver = {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+               .driver = {
+                       .owner = THIS_MODULE,
+                       .name = RDAFM_DEV,
+               },
+               .attach_adapter = fm_i2c_attach_adapter,
+               .detach_client = fm_i2c_detach_client,
+#else
+               .probe = fm_i2c_probe,
+               .remove = fm_i2c_remove,
+               .detect = fm_i2c_detect,
+               .driver.name = RDAFM_DEV,
+               .id_table = &fm_i2c_id,
+       //      .address_data = &addr_data,
+               .address_list = (const unsigned short*) forces,
+#endif
+       };
+
+static uint16_t RDAFM_CHIP_ID = 0x5808;
+static RDA_RADIO_WORK_E RDA_RADIO_WorkType = FM_RECEIVER;
+
+
+
+#if 1
+static uint16_t RDA5802N_initialization_reg[]={
+       0xC005, //02h 
+       0x0000,
+       0x0400,
+       0xC6ED, //0x86AD, //05h
+       0x6000,
+       0x721A, //0x42C6
+       0x0000,
+       0x0000,
+       0x0000,  //0x0ah
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,  //0x10h
+       0x0019,
+       0x2A11,
+       0xB042,  
+       0x2A11,  
+       0xB831,  //0x15h 
+       0xC000,
+       0x2A91,
+       0x9400,
+       0x00A8,
+       0xc400,  //0x1ah
+       0xF7CF,  //Ìá¸ßÔ¶¶ËÔëÉùÒÖÖÆ  
+       0x2414, //0x2ADC,  //0x1ch ÌáÉýVIO VDDÖ®¼äѹ²îÒýÆðµÄ²»Á¼
+       0x806F, 
+       0x4608,
+       0x0086,
+       0x0661, //0x20H
+       0x0000,
+       0x109E,
+       0x23C8,
+       0x0406,
+       0x0E1C, //0x25H
+};
+#else
+static uint16_t RDA5802N_initialization_reg[]={
+       0xc401, //02h
+       0x0000,
+       0x0400,
+       0x86ad, //05h//
+       0x0000,
+       0x42c6,
+       0x0000,
+       0x0000,
+       0x0000,  //0x0ah
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,  //0x10h
+       0x0019,  
+       0x2a11,
+       0xa053,//0x80,0x53,
+       0x3e11,//0x22,0x11,     
+       0xfc7d,  //0x15h 
+       0xc000,
+       0x2a91,
+       0x9400,
+       0x00a8,
+       0xc400,  //0x1ah
+       0xe000,
+       0x2b1d, //0x23,0x14
+       0x816a,
+       0x4608,
+       0x0086,
+       0x0661,  //0x20h
+       0x0000,  
+       0x109e,
+       0x2244,
+       0x0408,  //0x24
+       0x0408,  //0x25
+};
+#endif
+
+static RDA_FM_REG_T RDA5820NS_TX_initialization_reg[]={
+       {0x02, 0xE003},
+       {0xFF, 100},    // if address is 0xFF, sleep value ms
+       {0x02, 0xE001},
+       {0x19, 0x88A8},
+       {0x1A, 0x4290},
+       {0x68, 0x0AF0},
+       {0x40, 0x0001},
+       {0x41, 0x41FF},
+       {0xFF, 500},
+       {0x03, 0x1B90},
+};
+
+static RDA_FM_REG_T RDA5820NS_RX_initialization_reg[]={
+       {0x02, 0x0002}, //Soft reset
+       {0xFF, 100},    // wait
+       {0x02, 0xC001},  //Power Up 
+       {0x05, 0x888F},  //LNAP  0x884F --LNAN
+       {0x06, 0x6000},
+       {0x13, 0x80E1},
+       {0x14, 0x2A11},
+       {0x1C, 0x22DE},
+       {0x21, 0x0020},
+       {0x03, 0x1B90},
+};
+
+
+
+static struct file_operations fm_ops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = fm_ops_ioctl,
+       .llseek = fm_ops_lseek,
+       .open = fm_ops_open,
+       .release = fm_ops_release,
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+static DECLARE_MUTEX(fm_ops_mutex);
+#else
+DEFINE_SEMAPHORE(fm_ops_mutex);
+#endif
+
+/******************************************************************************
+ *****************************************************************************/
+
+/******************************************************************************
+ *****************************************************************************/
+
+
+
+static int RDAFM_GetChipID(struct i2c_client *client, uint16_t *pChipID)
+{
+       int err;
+       int ret = -1;
+       uint16_t val = 0x0002;
+
+       //Reset RDA FM
+       err = RDAFM_write(client, 0x02, val);
+       if(err < 0){
+#ifdef FMDEBUG
+               FM_DEBUG("RDAFM_GetChipID: reset FM chip failed!\n");
+#endif
+               ret = -1;
+               return ret;
+       }
+       msleep(80);
+
+       val = 0;
+       err = RDAFM_read(client, 0x0C, &val);
+       if (err == 0)
+       {
+               if ((0x5802 == val) || (0x5803 == val))
+               {
+                       err = RDAFM_read(client, 0x0E, &val);
+
+                       if (err == 0)
+                               *pChipID = val;
+                       else
+                               *pChipID = 0x5802;
+
+#ifdef FMDEBUG
+                       FM_DEBUG("RDAFM_GetChipID: Chip ID = %04X\n", val);
+#endif
+
+                       ret = 0;
+
+               }
+               else if ((0x5805 == val) || (0x5820 == val))
+               {
+                       *pChipID = val;
+                       ret = 0;
+               }
+               else
+               {
+#ifdef FMDEBUG
+                       FM_DEBUG("RDAFM_GetChipID: get chip ID failed! get value = %04X\n", val);
+#endif
+                       ret = -1;
+               }
+
+       }
+       else
+       {
+#ifdef FMDEBUG
+               FM_DEBUG("RDAFM_GetChipID: get chip ID failed!\n");
+#endif
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+/*
+ *  RDAFM_read
+ */
+static int RDAFM_read(struct i2c_client *client, uint8_t addr, uint16_t *val)
+{
+       int n;
+       char b[2] = {0};
+
+       // first, send addr to RDAFM
+       n = i2c_master_send(client, (char*)&addr, 1);
+       if (n < 0)
+       {
+               FM_ALERT("RDAFM_read send:0x%X err:%d\n", addr, n);
+               return -1;
+       }
+
+       // second, receive two byte from RDAFM
+       n = i2c_master_recv(client, b, 2);
+       if (n < 0)
+       {
+               FM_ALERT("RDAFM_read recv:0x%X err:%d\n", addr, n);
+               return -1;
+       }
+
+       *val = (uint16_t)(b[0] << 8 | b[1]);
+
+       return 0;
+}
+
+/*
+ *  RDAFM_write
+ */
+static int RDAFM_write(struct i2c_client *client, uint8_t addr, uint16_t val)
+{
+       int n;
+       char b[3];
+
+       b[0] = addr;
+       b[1] = (char)(val >> 8);
+       b[2] = (char)(val & 0xFF);
+
+       n = i2c_master_send(client, b, 3);
+       if (n < 0)
+       {
+               FM_ALERT("RDAFM_write send:0x%X err:%d\n", addr, n);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int RDAFM_clear_hmute(struct i2c_client *client)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+
+       FM_DEBUG("RDAFM_clear_hmute\n");
+
+       ret = RDAFM_read(client, 0x02, &tRegValue);
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_clear_hmute  read register failed!\n"); 
+               return -1;
+       }
+
+       tRegValue |= (1 << 14);
+
+       ret = RDAFM_write(client, 0x02, tRegValue);
+
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_clear_hmute  write register failed!\n"); 
+               return -1;
+       }
+
+       if(fm_tune_data.byPowerUp){
+               if (fm_tune(g_fm_struct, &(fm_tune_data.parm)) < 0)
+               {
+                       fm_tune_data.byPowerUp = false;
+                       memset(&fm_tune_data.parm, 0, sizeof(fm_tune_data.parm));
+                       return -EPERM;
+               }
+               fm_tune_data.byPowerUp = false;
+               memset(&fm_tune_data.parm, 0, sizeof(fm_tune_data.parm));
+       }
+
+       return 0;
+}
+
+
+
+static int RDAFM_enable_hmute(struct i2c_client *client)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+
+       FM_DEBUG("RDAFM_enable_hmute\n");
+
+       ret = RDAFM_read(client, 0x02, &tRegValue);
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_enable_hmute  read register failed!\n"); 
+               return -1;
+       }
+
+       tRegValue &= (~(1 << 14));
+
+       ret = RDAFM_write(client, 0x02, tRegValue);
+
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_enable_hmute  write register failed!\n"); 
+               return -1;
+       }
+
+       return 0;
+}
+
+
+
+static int RDAFM_clear_tune(struct i2c_client *client)
+{
+       //Don't need it
+       return 0;
+}
+
+
+
+static int RDAFM_enable_tune(struct i2c_client *client)
+{
+       //Don't need it
+       return 0;
+}
+
+
+
+static int RDAFM_clear_seek(struct i2c_client *client)
+{
+       //Don't need it
+       return 0;
+}
+
+
+
+static int RDAFM_enable_seek(struct i2c_client *client)
+{
+       //Don't need it
+       return 0;
+}
+
+
+//b=true set stereo else set mono
+static int RDAFM_SetStereo(struct i2c_client *client, uint8_t b)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+
+       FM_DEBUG("RDAFM_SetStereo\n");
+
+       ret = RDAFM_read(client, 0x02, &tRegValue);
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_SetStereo  read register failed!\n"); 
+               return -1;
+       }
+       if (b)
+               tRegValue &= (~(1 << 13));//set stereo
+       else
+               tRegValue |= (1 << 13); //set mono
+
+       ret = RDAFM_write(client, 0x02, tRegValue);
+
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_SetStereo  write register failed!\n"); 
+               return -1;
+       }
+
+
+       return 0;
+
+}
+
+
+static int RDAFM_SetRSSI_Threshold(struct i2c_client *client, uint8_t RssiThreshold)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+
+       FM_DEBUG("RDAFM_SetRSSI_Threshold\n");
+
+       ret = RDAFM_read(client, 0x05, &tRegValue);
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_SetRSSI_Threshold  read register failed!\n"); 
+               return -1;
+       }
+
+       tRegValue &= 0x80FF;//clear valume
+       tRegValue |= ((RssiThreshold & 0x7f) << 8); //set valume
+
+       ret = RDAFM_write(client, 0x05, tRegValue);
+
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_SetRSSI_Threshold  write register failed!\n"); 
+               return -1;
+       }
+
+
+       return 0;
+
+}
+
+
+
+static int RDAFM_SetDe_Emphasis(struct i2c_client *client, uint8_t index)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+
+       FM_DEBUG("RDAFM_SetRSSI_Threshold\n");
+
+       ret = RDAFM_read(client, 0x04, &tRegValue);
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_SetRSSI_Threshold  read register failed!\n"); 
+               return -1;
+       }
+
+       if (0 == index)
+       {
+               tRegValue &= (~(1 << 11));//De_Emphasis=75us
+       }
+       else if (1 == index)
+       {
+               tRegValue |= (1 << 11);//De_Emphasis=50us
+       }
+
+
+       ret = RDAFM_write(client, 0x04, tRegValue);
+
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_SetRSSI_Threshold  write register failed!\n"); 
+               return -1;
+       }
+
+
+       return 0;
+
+
+}
+
+
+static void RDAFM_em_test(struct i2c_client *client, uint16_t group_idx, uint16_t item_idx, uint32_t item_value)
+{
+       FM_ALERT("RDAFM_em_test  %d:%d:%d\n", group_idx, item_idx, item_value); 
+       switch (group_idx)
+       {
+               case mono:
+                       if(item_value == 1)
+                       {
+                               RDAFM_SetStereo(client, 0); //force mono
+                       }
+                       else
+                       {
+                               RDAFM_SetStereo(client, 1); //stereo
+
+                       }
+
+                       break;
+               case stereo:
+                       if(item_value == 0)
+                       {
+                               RDAFM_SetStereo(client, 1); //stereo
+                       }
+                       else
+                       {
+                               RDAFM_SetStereo(client, 0); //force mono        
+                       }
+                       break;
+               case RSSI_threshold:
+                       item_value &= 0x7F;
+                       RDAFM_SetRSSI_Threshold(client, item_value);
+                       break;              
+               case Softmute_Enable:
+                       if (item_idx)
+                       {
+                               RDAFM_enable_hmute(client);
+                       }
+                       else
+                       {
+                               RDAFM_clear_hmute(client);
+                       }
+                       break;
+               case De_emphasis:
+                       if(item_idx >= 2) //0us
+                       {
+                               FM_ALERT("RDAFM not support De_emphasis 0\n");          
+                       }
+                       else
+                       {
+                               RDAFM_SetDe_Emphasis(client,item_idx);//0=75us,1=50us
+                       }
+                       break;
+
+               case HL_Side:
+
+                       break;
+               default:
+                       FM_ALERT("RDAFM not support this setting\n");
+                       break;   
+       }
+}
+
+static bool RDAFM_Scan(struct i2c_client *client, 
+               uint16_t min_freq, uint16_t max_freq,
+               uint16_t *pFreq,
+               uint16_t *pScanTBL, 
+               uint16_t *ScanTBLsize, 
+               uint16_t scandir, 
+               uint16_t space)
+{
+       uint16_t tFreq, tRegValue = 0;
+       uint16_t tmp_scanTBLsize = *ScanTBLsize;
+       int ret = -1;
+       bool isTrueStation = false;
+       uint16_t oldValue = 0;
+       int channel = 0;
+
+       if((!pScanTBL) || (tmp_scanTBLsize == 0)) {
+               return false;
+       }
+
+       //clear the old value of pScanTBL
+       memset(pScanTBL, 0, sizeof(uint16_t)*RDA599X_SCANTBL_SIZE);
+
+       if(tmp_scanTBLsize > RDA599X_SCANTBL_SIZE)
+       {
+               tmp_scanTBLsize = RDA599X_SCANTBL_SIZE;
+       }
+
+       //scan up
+       if(scandir == RDA599X_FM_SCAN_UP){ // now, only support scan up
+               tFreq = min_freq;
+       }else{ //scan down
+               tFreq = max_freq;//max_freq compare need or not   
+       }
+
+       //mute FM
+       RDAFM_enable_hmute(client);
+
+       //set seekth
+       tRegValue = 0;
+       RDAFM_read(client, 0x05, &tRegValue);
+       tRegValue &= (~(0x7f<<8));
+       tRegValue |= ((0x8 & 0x7f) << 8);
+       RDAFM_write(client, 0x05, tRegValue);
+       msleep(50);
+
+       atomic_set(&scan_complete_flag, 1);
+       do {
+               if(atomic_read(&scan_complete_flag) == 0)
+                       break;
+               isTrueStation = false;
+
+               //set channel and enable TUNE
+               tRegValue = 0;
+               RDAFM_read(client, 0x03, &tRegValue);
+               tRegValue &= (~(0x03ff<<6)); //clear bit[15:6]
+               channel = tFreq - min_freq; 
+               tRegValue |= ((channel << 6) | (1 << 4)); //set bit[15:6] and bit[4]
+               ret = RDAFM_write(client, 0x03, tRegValue);
+               msleep(40);
+
+               //read 0x0B and check FM_TRUE(bit[8])
+               tRegValue = 0;
+               ret = RDAFM_read(client, 0x0B, &tRegValue);
+               if(!ret){
+                       if((tRegValue & 0x0100) == 0x0100){
+                               isTrueStation = true;
+                       }
+               }
+
+               //if this freq is a true station, read the channel
+               if(isTrueStation){
+                       //tRegValue = 0;
+                       //RDAFM_read(client, 0x03, &tRegValue);
+                       //channel = ((tRegValue>>6) & 0x03ff) - 5;
+                       channel = channel - 5;
+                       if((channel >= 0) && (channel != 85)){
+                               oldValue = *(pScanTBL+(channel/16));
+                               oldValue |= (1<<(channel%16));
+                               *(pScanTBL+(channel/16)) = oldValue;
+                       }
+               }
+
+               //increase freq
+               tFreq += space;
+       }while( tFreq <= max_freq );
+
+#if defined(MTK_MT6515) && defined(MT6626)
+       *(pScanTBL+13) = 0xb2d4;
+       *(pScanTBL+14) = 0xb2d4;
+       *(pScanTBL+15) = 0xb2d4;
+#endif
+
+       *ScanTBLsize = tmp_scanTBLsize;
+       *pFreq = 0;
+
+       //clear FM mute
+       RDAFM_clear_hmute(client);
+
+       return true;
+}
+
+
+static int fm_setup_cdev(struct fm *fm)
+{
+       int err;
+
+       err = alloc_chrdev_region(&fm->dev_t, 0, 1, FM_NAME);
+       if (err) {
+               FM_ALERT("alloc dev_t failed\n");
+               return -1;
+       }
+
+       FM_ALERT("alloc %s:%d:%d\n", FM_NAME,
+                       MAJOR(fm->dev_t), MINOR(fm->dev_t));
+
+       cdev_init(&fm->cdev, &fm_ops);
+
+       fm->cdev.owner = THIS_MODULE;
+       fm->cdev.ops = &fm_ops;
+
+       err = cdev_add(&fm->cdev, fm->dev_t, 1);
+       if (err) {
+               FM_ALERT("alloc dev_t failed\n");
+               return -1;
+       }
+
+       fm->cls = class_create(THIS_MODULE, FM_NAME);
+       if (IS_ERR(fm->cls)) {
+               err = PTR_ERR(fm->cls);
+               FM_ALERT("class_create err:%d\n", err);
+               return err;            
+       }    
+       fm->dev = device_create(fm->cls, NULL, fm->dev_t, NULL, FM_NAME);
+
+       return 0;
+}
+
+
+
+static int fm_ops_ioctl(struct file *filp,
+               unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+       struct fm *fm = container_of(filp->f_dentry->d_inode->i_cdev, struct fm, cdev);
+
+       FM_DEBUG("%s cmd(%x)\n", __func__, cmd);
+
+       switch(cmd)
+       {
+               case FM_IOCTL_POWERUP:
+                       {
+                               struct fm_tune_parm parm;
+                               FM_DEBUG("FM_IOCTL_POWERUP\n");
+
+                               // FIXME!!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //                return -EPERM;
+
+                               if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm)))
+                                       return -EFAULT;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_powerup(fm, &parm);
+                               up(&fm_ops_mutex);
+                               if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm)))
+                                       return -EFAULT;
+                               //                      fm_low_power_wa(1);
+                               break;
+                       }
+
+               case FM_IOCTL_POWERDOWN:
+                       {
+                               FM_DEBUG("FM_IOCTL_POWERDOWN\n");
+                               // FIXME!!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //                return -EPERM;
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_powerdown(fm);
+                               up(&fm_ops_mutex);
+                               //                      fm_low_power_wa(0);
+                               break;
+                       }
+
+                       // tune (frequency, auto Hi/Lo ON/OFF )
+               case FM_IOCTL_TUNE:
+                       {
+                               struct fm_tune_parm parm;
+                               FM_DEBUG("FM_IOCTL_TUNE\n");
+                               // FIXME!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //                return -EPERM;
+
+                               if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm)))
+                                       return -EFAULT;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_tune(fm, &parm);
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_SEEK:
+                       {
+                               struct fm_seek_parm parm;
+                               FM_DEBUG("FM_IOCTL_SEEK\n");
+
+                               // FIXME!!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //              return -EPERM;
+
+                               if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_seek_parm)))
+                                       return -EFAULT;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_seek(fm, &parm);
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &parm, sizeof(struct fm_seek_parm)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_SETVOL:
+                       {
+                               uint32_t vol;
+                               FM_DEBUG("FM_IOCTL_SETVOL\n");
+
+                               // FIXME!!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //              return -EPERM;
+
+                               if(copy_from_user(&vol, (void*)arg, sizeof(uint32_t))) {
+                                       FM_ALERT("copy_from_user failed\n");
+                                       return -EFAULT;
+                               }
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_setvol(fm, vol);
+                               up(&fm_ops_mutex);
+
+                               break;
+                       }
+
+               case FM_IOCTL_GETVOL:
+                       {
+                               uint32_t vol;
+                               FM_DEBUG("FM_IOCTL_GETVOL\n");
+
+                               // FIXME!!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //              return -EPERM;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_getvol(fm, &vol);
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &vol, sizeof(uint32_t)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_MUTE:
+                       {
+                               uint32_t bmute;
+                               FM_DEBUG("FM_IOCTL_MUTE\n");
+
+                               // FIXME!!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //              return -EPERM;
+                               if (copy_from_user(&bmute, (void*)arg, sizeof(uint32_t)))
+                               {
+                                       FM_DEBUG("copy_from_user mute failed!\n");
+                                       return -EFAULT;    
+                               }
+
+                               FM_DEBUG("FM_IOCTL_MUTE:%d\n", bmute); 
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+
+                               if (bmute){
+                                       ret = RDAFM_enable_hmute(fm->i2c_client);
+                               }else{
+                                       ret = RDAFM_clear_hmute(fm->i2c_client);
+                               }
+
+                               up(&fm_ops_mutex);
+
+                               break;
+                       }
+
+               case FM_IOCTL_GETRSSI:
+                       {
+                               uint32_t rssi;
+                               FM_DEBUG("FM_IOCTL_GETRSSI\n");
+
+                               // FIXME!!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //              return -EPERM;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+
+                               ret = fm_getrssi(fm, &rssi);
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &rssi, sizeof(uint32_t)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_RW_REG:
+                       {
+                               struct fm_ctl_parm parm_ctl;
+                               FM_DEBUG("FM_IOCTL_RW_REG\n");
+
+                               // FIXME!!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //              return -EPERM;
+
+                               if (copy_from_user(&parm_ctl, (void*)arg, sizeof(struct fm_ctl_parm)))
+                                       return -EFAULT;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+
+                               if(parm_ctl.rw_flag == 0) //write
+                               {
+                                       ret = RDAFM_write(fm->i2c_client, parm_ctl.addr, parm_ctl.val);
+                               }
+                               else
+                               {
+                                       ret = RDAFM_read(fm->i2c_client, parm_ctl.addr, &parm_ctl.val);
+                               }
+
+                               up(&fm_ops_mutex);
+                               if ((parm_ctl.rw_flag == 0x01) && (!ret)) // Read success.
+                               { 
+                                       if (copy_to_user((void*)arg, &parm_ctl, sizeof(struct fm_ctl_parm)))
+                                               return -EFAULT;
+                               }
+                               break;
+                       }
+
+               case FM_IOCTL_GETCHIPID:
+                       {
+                               uint16_t chipid;            
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+
+                               RDAFM_GetChipID(fm->i2c_client, &chipid);
+                               //chipid = fm->chip_id;
+                               chipid = 0x6620;
+                               FM_DEBUG("FM_IOCTL_GETCHIPID:%04x\n", chipid);   
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &chipid, sizeof(uint16_t)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_EM_TEST:
+                       {
+                               struct fm_em_parm parm_em;
+                               FM_DEBUG("FM_IOCTL_EM_TEST\n");
+
+                               // FIXME!!
+                               //            if (!capable(CAP_SYS_ADMIN))
+                               //              return -EPERM;
+
+                               if (copy_from_user(&parm_em, (void*)arg, sizeof(struct fm_em_parm)))
+                                       return -EFAULT;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+
+                               RDAFM_em_test(fm->i2c_client, parm_em.group_idx, parm_em.item_idx, parm_em.item_value);
+
+                               up(&fm_ops_mutex);
+
+                               break;
+                       }
+               case FM_IOCTL_IS_FM_POWERED_UP:
+                       {
+                               uint32_t powerup;
+                               FM_DEBUG("FM_IOCTL_IS_FM_POWERED_UP");
+                               if (fm->powerup) {
+                                       powerup = 1;
+                               } else {
+                                       powerup = 0;
+                               }
+                               if (copy_to_user((void*)arg, &powerup, sizeof(uint32_t)))
+                                       return -EFAULT;
+                               break;
+                       }
+
+#ifdef FMDEBUG
+               case FM_IOCTL_DUMP_REG:
+                       {
+                               uint16_t chipid = 0;
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               RDAFM_GetChipID(fm->i2c_client, &chipid);
+                               up(&fm_ops_mutex);
+
+                               break;
+                       }
+#endif
+
+               case FM_IOCTL_SCAN:
+                       {
+                               struct fm_scan_parm parm;
+                               FM_DEBUG("FM_IOCTL_SCAN\n");
+                               if (false == fm->powerup){
+                                       return -EFAULT;
+                               }
+                               if(copy_from_user(&parm, (void*)arg, sizeof(struct fm_scan_parm))){
+                                       return -EFAULT;
+                               }
+                               if (down_interruptible(&fm_ops_mutex)){
+                                       return -EFAULT;
+                               }
+                               fm_scan(fm, &parm);
+                               up(&fm_ops_mutex);
+
+                               if(copy_to_user((void*)arg, &parm, sizeof(struct fm_scan_parm))){
+                                       return -EFAULT;
+                               }
+
+                               break;
+                       }
+
+               case FM_IOCTL_STOP_SCAN:
+                       {
+                               FM_DEBUG("FM_IOCTL_STOP_SCAN\n");
+                               break;
+                       }
+
+               default:
+                       {
+                               FM_DEBUG("default\n");
+                               break;
+                       }
+       }
+
+       return ret;
+}
+static loff_t fm_ops_lseek(struct file *filp, loff_t off, int whence)
+{
+//     struct fm *fm = filp->private_data;
+
+       if(whence == SEEK_END){
+               //fm_hwscan_stop(fm);
+               atomic_set(&scan_complete_flag, 0);
+       }else if(whence == SEEK_SET){  
+               //FM_EVENT_SEND(fm->rds_event, FM_RDS_DATA_READY);
+       }   
+       return off;    
+}
+
+static int fm_ops_open(struct inode *inode, struct file *filp)
+{
+       struct fm *fm = container_of(inode->i_cdev, struct fm, cdev);
+
+       FM_DEBUG("%s\n", __func__);
+
+       if (down_interruptible(&fm_ops_mutex))
+               return -EFAULT;
+
+       // TODO: only have to set in the first time?
+       // YES!!!!
+
+       fm->ref++;
+
+       up(&fm_ops_mutex);
+
+       filp->private_data = fm;
+
+       // TODO: check open flags
+
+       return 0;
+}
+
+static int fm_ops_release(struct inode *inode, struct file *filp)
+{
+       int err = 0;
+       struct fm *fm = container_of(inode->i_cdev, struct fm, cdev);
+
+       FM_DEBUG("%s\n", __func__);
+
+       if (down_interruptible(&fm_ops_mutex))
+               return -EFAULT;
+       fm->ref--;
+       if(fm->ref < 1) {
+               if(fm->powerup == true) {
+                       fm_powerdown(fm);           
+               }
+       }
+
+       up(&fm_ops_mutex);
+
+       return err;
+}
+
+static int fm_init(struct i2c_client *client)
+{
+       int err;
+       struct fm *fm = NULL;
+       int ret = -1;
+
+
+       FM_DEBUG("%s()\n", __func__);
+       if (!(fm = kzalloc(sizeof(struct fm), GFP_KERNEL)))
+       {
+               FM_ALERT("-ENOMEM\n");
+               err = -ENOMEM;
+               goto ERR_EXIT;
+       }
+
+       fm->ref = 0;
+       fm->powerup = false;
+       atomic_set(&scan_complete_flag, 0);
+
+       // First, read 5802NM chip ID
+       FM_DEBUG("%s()First, read 5802NM chip ID\n", __func__);
+       ret = RDAFM_GetChipID(client, &RDAFM_CHIP_ID);
+       FM_DEBUG("%s() 5802NM chip ID = 0x%04x\n", __func__, RDAFM_CHIP_ID);
+       // if failed, means use FM in 5990P_E
+       if(ret < 0){
+               // enable the FM chip in combo
+               FM_DEBUG("%s() enable the FM chip in combo\n", __func__);
+               ret = rda_fm_power_on();
+               if(ret < 0){
+                       err = -ENOMEM;
+                       goto ERR_EXIT;
+               }
+               msleep(100);
+               ret = RDAFM_GetChipID(client, &RDAFM_CHIP_ID);
+               FM_DEBUG("%s() the FM in combo chip ID = 0x%04x\n", __func__, RDAFM_CHIP_ID);
+               if(ret < 0){
+                       err = -ENOMEM;
+                       goto ERR_EXIT;
+               }else{
+                       fm->chip_id = RDAFM_CHIP_ID;
+               }
+
+               // disable the FM chip for power saving
+               ret = rda_fm_power_off();
+               if(ret < 0){
+                       err = -ENOMEM;
+                       goto ERR_EXIT;
+               }
+       }else{
+               fm->chip_id = RDAFM_CHIP_ID;
+       }
+
+
+
+       if ((err = fm_setup_cdev(fm)))
+       {
+               goto ERR_EXIT;
+       }
+
+       g_fm_struct = fm;
+       fm->i2c_client = client;
+       i2c_set_clientdata(client, fm);
+
+
+       /***********Add porc file system*************/
+
+       g_fm_proc = create_proc_entry(FM_PROC_FILE, 0444, NULL);
+       if (g_fm_proc == NULL) {
+               FM_ALERT("create_proc_entry failed\n");
+               err = -ENOMEM;
+               goto ERR_EXIT;
+       } else {
+               g_fm_proc->read_proc = fm_proc_read;
+               g_fm_proc->write_proc = NULL;
+               //g_fm_proc->owner = THIS_MODULE;
+               FM_ALERT("create_proc_entry success\n");
+       }
+
+       /********************************************/
+
+       FM_DEBUG("fm_init is ok!\n");
+
+       return 0;
+
+ERR_EXIT:
+       kfree(fm);
+
+       return err;
+}
+
+static int fm_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+       int cnt= 0;
+       struct fm *fm  = g_fm_struct;
+       FM_ALERT("Enter fm_proc_read.\n");
+       if(off != 0)
+               return 0;
+       if (fm != NULL && fm->powerup) {
+               cnt = sprintf(page, "1\n");
+       } else {
+               cnt = sprintf(page, "0\n");
+       }
+       *eof = 1;
+       FM_ALERT("Leave fm_proc_read. cnt = %d\n", cnt);
+       return cnt;
+}
+
+
+static int fm_destroy(struct fm *fm)
+{
+       int err = 0;
+
+       FM_DEBUG("%s\n", __func__);
+
+       device_destroy(fm->cls, fm->dev_t);
+       class_destroy(fm->cls);
+
+       cdev_del(&fm->cdev);
+       unregister_chrdev_region(fm->dev_t, 1);
+
+       fm_powerdown(fm);
+
+       /***********************************/
+       remove_proc_entry(FM_PROC_FILE, NULL);
+
+       /**********************************/
+
+       // FIXME: any other hardware configuration ?
+
+       // free all memory
+       kfree(fm);
+
+       return err;
+}
+
+/*
+ *  fm_powerup
+ */
+static int fm_powerup(struct fm *fm, struct fm_tune_parm *parm)
+{
+       int i;
+       uint16_t tRegValue = 0x0002;
+       int ret = -1;
+
+       struct i2c_client *client = fm->i2c_client;
+
+       if (fm->powerup)
+       {
+               parm->err = FM_BADSTATUS;
+               return -EPERM;
+       }
+
+       // if chip_id is ID_RDA5820NS, enable the FM chip in combo
+       if(fm->chip_id == ID_RDA5820NS){
+               ret = rda_fm_power_on();
+               if(ret < 0){
+                       return -EPERM;
+               }
+               msleep(100);
+       }
+
+
+       //Reset RDA FM
+       tRegValue = 0x0002;
+       RDAFM_write(client, 0x02, tRegValue);
+       msleep(100);
+
+
+       if (ID_RDA5802N == RDAFM_CHIP_ID){
+               for (i=0; i<((sizeof(RDA5802N_initialization_reg)) / (sizeof(uint16_t))); i++)
+               {
+                       ret = RDAFM_write(client, i+2, RDA5802N_initialization_reg[i]);
+
+                       if (ret < 0)
+                       {
+                               FM_DEBUG("fm_powerup init failed!\n");
+
+                               parm->err = FM_FAILED;
+
+                               return -EPERM;
+                       }
+               }
+
+       }else if (ID_RDA5820NS == RDAFM_CHIP_ID){
+               if(RDA_RADIO_WorkType == FM_RECEIVER){
+                       for (i = 0; i < ((sizeof(RDA5820NS_RX_initialization_reg)) / (sizeof(RDA_FM_REG_T))); i++)
+                       {
+                               if(RDA5820NS_RX_initialization_reg[i].address == 0xFF){
+                                       msleep(RDA5820NS_RX_initialization_reg[i].value);
+                               }else{
+                                       ret = RDAFM_write(client, RDA5820NS_RX_initialization_reg[i].address, RDA5820NS_RX_initialization_reg[i].value);
+                                       if (ret < 0)
+                                       {
+                                               FM_DEBUG("fm_powerup init failed!\n");
+                                               parm->err = FM_FAILED;
+                                               return -EPERM;
+                                       }
+                               }
+                       }
+               }else{
+                       for (i = 0; i < ((sizeof(RDA5820NS_TX_initialization_reg)) / (sizeof(RDA_FM_REG_T))); i++)
+                       {
+                               if(RDA5820NS_TX_initialization_reg[i].address == 0xFF){
+                                       msleep(RDA5820NS_TX_initialization_reg[i].value);
+                               }else{
+                                       ret = RDAFM_write(client, RDA5820NS_TX_initialization_reg[i].address, RDA5820NS_TX_initialization_reg[i].value);
+                                       if (ret < 0)
+                                       {
+                                               FM_DEBUG("fm_powerup init failed!\n");
+                                               parm->err = FM_FAILED;
+                                               return -EPERM;
+                                       }
+                               }
+                       }
+               }
+
+       }
+
+
+       FM_DEBUG("pwron ok\n");
+       fm->powerup = true;
+
+       if (fm_tune(fm, parm) < 0)
+       {
+               return -EPERM;
+       }
+       fm_tune_data.byPowerUp = true;
+       memcpy(&fm_tune_data.parm, parm, sizeof(fm_tune_data.parm));
+
+       parm->err = FM_SUCCESS;
+
+       return 0;
+
+}
+
+/*
+ *  fm_powerdown
+ */
+static int fm_powerdown(struct fm *fm)
+{
+       uint16_t tRegValue = 0;
+       int ret = -1;
+       struct i2c_client *client = fm->i2c_client;
+
+       RDAFM_read(client, 0x02, &tRegValue);
+       tRegValue &= (~(1 << 0));
+       RDAFM_write(client, 0x02, tRegValue);
+
+       if(fm->chip_id == ID_RDA5820NS){
+               ret = rda_fm_power_off();
+               if(ret < 0){
+                       return -EPERM;
+               }
+       }
+
+       fm->powerup = false;
+       FM_ALERT("pwrdown ok\n");
+
+       return 0;
+}
+
+/*
+ *  fm_seek
+ */
+static int fm_seek(struct fm *fm, struct fm_seek_parm *parm)
+{
+       int ret = 0;
+       uint16_t val = 0;
+       uint8_t spaec = 1;
+       uint16_t tFreq = 875;
+       uint16_t tRegValue = 0;
+       uint16_t bottomOfBand = 875;
+       int falseStation = -1;
+
+
+       struct i2c_client *client = fm->i2c_client;
+
+       if (!fm->powerup)
+       {
+               parm->err = FM_BADSTATUS;
+               return -EPERM;
+       }
+
+       if (parm->space == FM_SPACE_100K)
+       {
+               spaec = 1;
+               val &= (~((1<<0) | (1<<1)));
+       }
+       else if (parm->space == FM_SPACE_200K)
+       {
+               spaec = 2;
+               val &= (~(1<<1));
+               val |= (1<<0);
+       }
+       else
+       {
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->band == FM_BAND_UE)
+       {
+               val &= (~((1<<2) | (1<<3)));
+               bottomOfBand = 875;
+               fm->min_freq = 875;
+               fm->max_freq = 1080;
+       }
+       else if (parm->band == FM_BAND_JAPAN) 
+       {
+               val &= (~(1<<3));
+               val |= (1 << 2);
+               bottomOfBand = 760;
+               fm->min_freq = 760;
+               fm->max_freq = 910;
+       }
+       else if (parm->band == FM_BAND_JAPANW) {
+               val &= (~(1<<2));
+               val |= (1 << 3);
+               bottomOfBand = 760;
+               fm->min_freq = 760;
+               fm->max_freq = 1080;
+       }
+       else
+       {
+               FM_ALERT("band:%d out of range\n", parm->band);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->freq < fm->min_freq || parm->freq > fm->max_freq) {
+               FM_ALERT("freq:%d out of range\n", parm->freq);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->seekth > 0x0B) {
+               FM_ALERT("seekth:%d out of range\n", parm->seekth);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       RDAFM_read(client, 0x05, &tRegValue);
+       tRegValue &= (~(0x7f<<8));
+       //tRegValue |= ((parm->seekth & 0x7f) << 8);
+       tRegValue |= ((0x8 & 0x7f) << 8);
+       RDAFM_write(client, 0x05, tRegValue);
+
+
+#ifdef FMDEBUG
+       if (parm->seekdir == FM_SEEK_UP)
+               FM_DEBUG("seek %d up\n", parm->freq);
+       else
+               FM_DEBUG("seek %d down\n", parm->freq);
+#endif
+
+       // (1) set hmute bit
+       RDAFM_enable_hmute(client);
+
+       tFreq = parm->freq;
+
+       do {
+               if (parm->seekdir == FM_SEEK_UP)
+                       tFreq += spaec;
+               else
+                       tFreq -= spaec;
+
+               if (tFreq > fm->max_freq)
+                       tFreq = fm->min_freq;
+               if (tFreq < fm->min_freq)
+                       tFreq = fm->max_freq;
+
+               val = (((tFreq - bottomOfBand+5) << 6) | (1 << 4) | (val & 0x0f));
+               RDAFM_write(client, 0x03, val);
+               msleep(40);
+               ret = RDAFM_read(client, 0x0B, &tRegValue);
+               if (ret < 0)
+               {
+                       FM_DEBUG("fm_seek: read register failed tunning freq = %4X\n", tFreq);
+                       falseStation = -1;
+               }
+               else
+               {
+                       if ((tRegValue & 0x0100) == 0x0100)
+                               falseStation = 0;
+                       else
+                               falseStation = -1;
+               }
+
+               if(falseStation == 0)
+                       break;
+       }while(tFreq != parm->freq);
+
+
+       //clear hmute
+       RDAFM_clear_hmute(client);
+
+       if (falseStation == 0) // seek successfully
+       {    
+               parm->freq = tFreq;
+               FM_ALERT("fm_seek success, freq:%d\n", parm->freq);
+               parm->err = FM_SUCCESS;
+
+
+       }
+       else
+       {
+               FM_ALERT("fm_seek failed, invalid freq\n");
+               parm->err = FM_SEEK_FAILED;
+               ret = -1;
+       }
+
+       return ret;
+}
+
+/*
+ *  fm_scan
+ */
+static int  fm_scan(struct fm *fm, struct fm_scan_parm *parm)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+       uint16_t scandir = RDA599X_FM_SCAN_UP; //scandir ËÑË÷·½Ïò
+       uint8_t space = 1; 
+       struct i2c_client *client = fm->i2c_client;
+
+       if (!fm->powerup){
+               parm->err = FM_BADSTATUS;
+               return -EPERM;
+       }
+
+       RDAFM_read(client, 0x03, &tRegValue);
+
+       if (parm->space == FM_SPACE_100K){
+               space = 1;
+               tRegValue &= (~((1<<0) | (1<<1))); //set 03H's bit[1:0] to 00
+       }else if (parm->space == FM_SPACE_200K) {
+               space = 2;
+               tRegValue &= (~(1<<1)); //clear bit[1]
+               tRegValue |= (1<<0);    //set bit[0]
+       }else{
+               //default
+               space = 1;
+               tRegValue &= (~((1<<0) | (1<<1))); //set 03H's bit[1:0] to 00
+       }
+
+       if(parm->band == FM_BAND_UE){
+               tRegValue &= (~((1<<2) | (1<<3)));
+               fm->min_freq = 875;
+               fm->max_freq = 1080;
+       }else if(parm->band == FM_BAND_JAPAN){
+               tRegValue &= (~(1<<3));
+               tRegValue |= (1 << 2);
+               fm->min_freq = 760;
+               fm->max_freq = 900;
+       }else if(parm->band == FM_BAND_JAPANW){
+               tRegValue &= (~(1<<2));
+               tRegValue |= (1 << 3);
+               fm->min_freq = 760;
+               fm->max_freq = 1080;
+       }else{
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       //set space and band
+       RDAFM_write(client, 0x03, tRegValue);
+       msleep(40);
+
+
+       if(RDAFM_Scan(client, fm->min_freq, fm->max_freq, &(parm->freq), parm->ScanTBL, &(parm->ScanTBLSize), scandir, space)){
+               parm->err = FM_SUCCESS;
+       }else{
+               parm->err = FM_SEEK_FAILED;
+       }
+
+       return ret;
+}
+
+
+static int fm_setvol(struct fm *fm, uint32_t vol)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+       struct i2c_client *client = fm->i2c_client;
+
+       if (vol > 15)
+               vol = 15;
+
+       FM_DEBUG("fm_setvol:%d\n", vol);
+
+       ret = RDAFM_read(client, 0x05, &tRegValue);
+       if (ret)
+               return -EPERM;
+       tRegValue &= ~(0x000f);
+       tRegValue |= vol;
+
+       ret = RDAFM_write(client, 0x05, tRegValue);
+       if (ret)
+               return -EPERM;
+
+       return 0;
+}
+
+static int fm_getvol(struct fm *fm, uint32_t *vol)
+{
+       int ret = 0;
+       uint16_t tRegValue;
+       struct i2c_client *client = fm->i2c_client;
+
+       ret = RDAFM_read(client, 0x05, &tRegValue);
+       if (ret)
+               return -EPERM;
+
+       if (ret)
+               return -EPERM;
+
+       *vol = (tRegValue & 0x000F);
+
+       return 0;
+}
+
+static int fm_getrssi(struct fm *fm, uint32_t *rssi)
+{
+       int ret = 0;
+       uint16_t tRegValue;
+       struct i2c_client *client = fm->i2c_client;
+
+       ret = RDAFM_read(client, 0x0B, &tRegValue);
+       if (ret)
+               return -EPERM;
+
+
+       *rssi = (uint32_t)((tRegValue >> 9) & RDAFM_MASK_RSSI);
+
+       FM_DEBUG("rssi value:%d\n", *rssi);
+
+       return 0;
+}
+
+/*
+ *  fm_tune
+ */
+static int fm_tune(struct fm *fm, struct fm_tune_parm *parm)
+{
+       int ret;
+       uint16_t val = 0;
+       uint8_t space = 1;
+       uint16_t bottomOfBand = 875;
+
+       struct i2c_client *client = fm->i2c_client;
+
+       FM_DEBUG("%s\n", __func__);
+
+       if (!fm->powerup)
+       {
+               parm->err = FM_BADSTATUS;
+               return -EPERM;
+       } 
+
+       if (parm->space == FM_SPACE_100K)
+       {
+               space = 1;
+               val &= (~((1<<0) | (1<<1)));
+       }
+       else if (parm->space == FM_SPACE_200K)
+       {
+               space = 2;
+               val |= (1<<0);
+               val &= (~(1<<1));
+       }
+       else
+       {
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->band == FM_BAND_UE)
+       {
+               val &= (~((1<<2) | (1<<3)));
+               bottomOfBand = 875;
+               fm->min_freq = 875;
+               fm->max_freq = 1080;
+       }
+       else if (parm->band == FM_BAND_JAPAN) 
+       {
+               val &= (~(1<<3));
+               val |= (1 << 2);
+               bottomOfBand = 760;
+               fm->min_freq = 760;
+               fm->max_freq = 910;
+       }
+       else if (parm->band == FM_BAND_JAPANW) {
+               val &= (~(1<<2));
+               val |= (1 << 3);
+               bottomOfBand = 760;
+               fm->min_freq = 760;
+               fm->max_freq = 1080;
+       }
+       else
+       {
+               FM_ALERT("band:%d out of range\n", parm->band);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->freq < fm->min_freq || parm->freq > fm->max_freq) {
+               FM_ALERT("freq:%d out of range\n", parm->freq);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       FM_DEBUG("fm_tune, freq:%d\n", parm->freq);
+
+       //RDAFM_enable_hmute(client);
+
+       val = (((parm->freq - bottomOfBand + 5) << 6) | (1 << 4) | (val & 0x0f));
+
+       ret = RDAFM_write(client, 0x03, val);
+       if (ret < 0)
+       {
+               FM_ALERT("fm_tune write freq failed\n");
+               parm->err = FM_SEEK_FAILED;
+               return ret;
+       }
+       msleep(40);
+
+       return ret;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+/*
+ *  fm_i2c_attach_adapter
+ */
+static int fm_i2c_attach_adapter(struct i2c_adapter *adapter)
+{
+       int err = 0;
+
+       if (adapter->id == RDAFM_I2C_PORT)
+       {
+               return i2c_probe(adapter, &RDAFM_addr_data, fm_i2c_detect);
+       }
+
+       return err;
+}
+
+/*
+ *  fm_i2c_detect
+ *  This function is called by i2c_detect
+ */
+static int fm_i2c_detect(struct i2c_adapter *adapter, int addr, int kind)
+{
+       int err;
+       struct i2c_client *client = NULL;
+
+       /* skip this since MT6516 shall support all the needed functionalities
+          if (!i2c_check_functionality(adapter, xxx))
+          {
+          FM_DEBUG("i2c_check_functionality failed\n");
+          return -ENOTSUPP;
+          }
+          */
+
+       /* initial i2c client */
+       if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL)))
+       {
+               FM_ALERT("kzalloc failed\n");
+               err = -ENOMEM;
+               goto ERR_EXIT;
+       }
+
+       client->addr = addr;
+       client->adapter = adapter;
+       client->driver = &RDAFM_driver;
+       client->flags = 0;
+       strncpy(client->name, "RDA FM RADIO", I2C_NAME_SIZE);
+
+       if ((err = fm_init(client)))
+       {
+               FM_ALERT("fm_init ERR:%d\n", err);
+               goto ERR_EXIT;
+       }
+
+       if (err = i2c_attach_client(client))
+       {
+               FM_ALERT("i2c_attach_client ERR:%d\n", err);
+               goto ERR_EXIT;
+       }
+
+       return 0;
+
+ERR_EXIT:
+       kfree(client);
+
+       return err;
+}
+static int fm_i2c_detach_client(struct i2c_client *client)
+{
+       int err = 0;
+       struct fm *fm = i2c_get_clientdata(client);
+
+       FM_DEBUG("fm_i2c_detach_client\n");
+
+       err = i2c_detach_client(client);
+       if (err)
+       {
+               dev_err(&client->dev, "fm_i2c_detach_client failed\n");
+               return err;
+       }
+
+       fm_destroy(fm);
+       kfree(client);
+
+       return err;
+}
+#else
+static int fm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       int err = -1;
+       FM_DEBUG("fm_i2c_probe\n");    
+       //client->timing = 50;
+       //client->timing = 200;
+       if ((err = fm_init(client)))
+       {
+               FM_ALERT("fm_init ERR:%d\n", err);
+               goto ERR_EXIT;
+       }   
+
+       return 0;   
+
+ERR_EXIT:
+       return err;    
+}
+
+static int fm_i2c_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
+{
+       FM_DEBUG("fm_i2c_detect\n");
+       strcpy(info->type, RDAFM_DEV);
+       return 0;
+}
+
+static int fm_i2c_remove(struct i2c_client *client)
+{
+       int err = 0;
+       struct fm *fm = i2c_get_clientdata(client);
+
+       FM_DEBUG("fm_i2c_remove\n");
+       if(fm)
+       {    
+               fm_destroy(fm);
+               fm = NULL;
+       }
+
+       return err;
+}
+#endif
+
+int  i2c_static_add_device(struct i2c_board_info *info)
+{
+       struct i2c_adapter *adapter;
+       struct i2c_client  *client;
+       int    ret; 
+
+       adapter = i2c_get_adapter(RDAFM_I2C_PORT);
+       if (!adapter) {
+               FM_DEBUG("%s: can't get i2c adapter\n", __func__);
+               ret = -ENODEV;
+               goto i2c_err;
+       }    
+
+       client = i2c_new_device(adapter, info);
+       if (!client) {
+               FM_DEBUG("%s:  can't add i2c device at 0x%x\n",
+                               __FUNCTION__, (unsigned int)info->addr);
+               ret = -ENODEV;
+               goto i2c_err;
+       }    
+
+       i2c_put_adapter(adapter);
+
+       return 0;
+
+i2c_err:
+       return ret;
+}
+
+static int mt_fm_probe(struct platform_device *pdev)
+{
+       int err = -1;
+       FM_ALERT("mt_fm_probe\n");
+       err = i2c_static_add_device(&i2c_rdafm);
+       if (err < 0){
+               FM_DEBUG("%s(): add i2c device error, err = %d\n", __func__, err);
+               return err;
+       }
+
+       // Open I2C driver
+       err = i2c_add_driver(&RDAFM_driver);
+       if (err)
+       {
+               FM_ALERT("i2c err\n");
+       }
+
+       return err;   
+} 
+
+static int mt_fm_remove(struct platform_device *pdev)
+{
+       FM_ALERT("mt_fm_remove\n");
+       i2c_unregister_device(g_fm_struct->i2c_client);
+       i2c_del_driver(&RDAFM_driver); 
+
+       return 0; 
+}
+
+
+static struct platform_driver mt_fm_dev_drv =
+{
+       .probe   = mt_fm_probe,
+       .remove  = mt_fm_remove,
+#if 0//def CONFIG_PM //Not need now   
+       .suspend = mt_fm_suspend,
+       .resume  = mt_fm_resume,
+#endif    
+       .driver = {
+               .name   = FM_NAME,
+               .owner  = THIS_MODULE,    
+       }
+};
+
+#if defined(MTK_MT6515)
+static struct platform_device mt_fm_device = {
+       .name   = FM_NAME,
+       .id = -1, 
+};
+#endif
+
+
+/*
+ *  mt_fm_init
+ */
+static int __init mt_fm_init(void)
+{
+       int err = 0;
+
+       FM_DEBUG("mt_fm_init\n");
+#if defined(MTK_MT6515)
+       err = platform_device_register(&mt_fm_device);
+       if(err){
+               FM_DEBUG("platform_device_register  fail\n");
+               return err;
+       }else{
+               FM_DEBUG("platform_device_register  success\n");
+       }
+#endif
+       err = platform_driver_register(&mt_fm_dev_drv);
+       if (err)
+       {
+               FM_DEBUG("platform_driver_register failed\n");
+       }else{
+               FM_DEBUG("platform_driver_register success\n");
+       }
+
+       return err;
+}
+
+/*
+ *  mt_fm_exit
+ */
+static void __exit mt_fm_exit(void)
+{
+       FM_DEBUG("mt_fm_exit\n");
+       platform_driver_unregister(&mt_fm_dev_drv);
+#if defined(MTK_MT6515)
+       platform_device_unregister(&mt_fm_device);
+#endif
+}
+
+module_init(mt_fm_init);
+module_exit(mt_fm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MediaTek FM Driver");
+MODULE_AUTHOR("William Chung <William.Chung@MediaTek.com>");
+
+
diff --git a/drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv_gpio.c b/drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv_gpio.c
new file mode 100755 (executable)
index 0000000..b154fbc
--- /dev/null
@@ -0,0 +1,1784 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h> // udelay()
+#include <linux/device.h> // device_create()
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/version.h>      /* constant of kernel version */
+#include <asm/uaccess.h> // get_user()
+
+#include <linux/fm.h>
+#include <mach/mt6575_gpio.h>
+#include <mach/mtk_rtc.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+// if need debug, define FMDEBUG
+//#define FMDEBUG
+
+// if your platform is MT6515/6575, define MTK_MT6515 
+#define MTK_MT6515
+
+// if your platform is MT6515/6575 and MTK FM is MT6626, define MT6626 
+//#define MT6626
+
+#define FM_ALERT(f, s...) \
+       do { \
+               printk(KERN_ALERT "RDAFM " f, ## s); \
+       } while(0)
+
+#ifdef FMDEBUG
+#define FM_DEBUG(f, s...) \
+       do { \
+               printk("RDAFM " f, ## s); \
+       } while(0)
+#else
+#define FM_DEBUG(f, s...)
+#endif
+
+#define RDA599X_SCANTBL_SIZE  16 //16*uinit16_t
+#define RDA599X_FM_SCAN_UP       0x0
+#define RDA599X_FM_SCAN_DOWN     0x01
+
+extern int rda_gpio_i2c_read_1_addr_2_data(uint8_t chipAddr, uint8_t regAddr, unsigned short *buffer);
+extern int rda_gpio_i2c_write_1_addr_2_data(uint8_t chipAddr, uint8_t regAddr, unsigned short data);
+extern int rda_fm_power_off(void);
+extern int rda_fm_power_on(void);
+
+/******************************************************************************
+ * CONSTANT DEFINITIONS
+ *****************************************************************************/
+#define RDAFM_SLAVE_ADDR        0x11    //RDA FM Chip address
+
+#define RDAFM_MASK_RSSI         0X7F // RSSI
+
+
+
+#define ID_RDA5802E             0x5804
+#define ID_RDA5802H             0x5801
+#define ID_RDA5802N             0x5808
+#define ID_RDA5820              0x5805
+#define ID_RDA5820NS            0x5820
+
+
+
+static struct proc_dir_entry *g_fm_proc = NULL;
+static struct fm *g_fm_struct = NULL;
+static atomic_t scan_complete_flag;
+
+#define FM_PROC_FILE "fm"
+
+/******************************************************************************
+ * STRUCTURE DEFINITIONS
+ *****************************************************************************/
+
+enum RDAFM_CHIP_TYPE {
+       CHIP_TYPE_RDA5802E = 0,
+       CHIP_TYPE_RDA5802H,
+       CHIP_TYPE_RDA5802N,
+       CHIP_TYPE_RDA5820,
+       CHIP_TYPE_RDA5820NS,
+};
+
+
+typedef struct
+{
+       uint8_t         address;
+       uint16_t        value;
+}RDA_FM_REG_T;   
+
+typedef struct
+{
+       bool            byPowerUp;
+       struct fm_tune_parm parm
+}FM_TUNE_T;
+static FM_TUNE_T fm_tune_data = {false, {}};
+
+typedef enum
+{
+       FM_RECEIVER,                            //5800,5802,5804
+       FM_TRANSMITTER,                 //5820
+}RDA_RADIO_WORK_E;
+
+typedef enum
+{
+       OFF,
+       ON,
+}RDA_FM_POWER_STATE_T;
+
+struct fm {
+       uint32_t ref;
+       bool powerup;
+       uint16_t chip_id;
+       //    uint16_t device_id;
+       uint8_t chipAddr;
+       dev_t dev_t;
+       uint16_t min_freq; // KHz
+       uint16_t max_freq; // KHz
+       uint8_t band;   // TODO
+       struct class *cls;
+       struct device *dev;
+       struct cdev cdev;
+       //    struct i2c_client *i2c_client;
+};
+
+
+
+
+/******************************************************************************
+ * FUNCTION PROTOTYPES
+ *****************************************************************************/
+
+
+static int RDAFM_clear_hmute(uint8_t chipAddr);
+static int RDAFM_enable_hmute(uint8_t chipAddr);
+static bool RDAFM_Scan(uint8_t chipAddr,
+               uint16_t min_freq, uint16_t max_freq,
+               uint16_t *pFreq, //get the valid freq after scan
+               uint16_t *pScanTBL,
+               uint16_t *ScanTBLsize,
+               uint16_t scandir,
+               uint16_t space);
+
+
+static int RDAFM_read(uint8_t chipAddr, uint8_t addr, uint16_t *val);
+static int RDAFM_write(uint8_t chipAddr, uint8_t addr, uint16_t val);
+static int fm_setup_cdev(struct fm *fm);
+static int fm_ops_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+static loff_t fm_ops_lseek(struct file *filp, loff_t off, int whence);
+static int fm_ops_open(struct inode *inode, struct file *filp);
+static int fm_ops_release(struct inode *inode, struct file *filp);
+
+static int fm_init(void);
+static int fm_destroy(struct fm *fm);
+static int fm_powerup(struct fm *fm, struct fm_tune_parm *parm);
+static int fm_powerdown(struct fm *fm);
+
+static int fm_tune(struct fm *fm, struct fm_tune_parm *parm);
+static int fm_seek(struct fm *fm, struct fm_seek_parm *parm);
+static int fm_scan(struct fm *fm, struct fm_scan_parm *parm);
+static int fm_setvol(struct fm *fm, uint32_t vol);
+static int fm_getvol(struct fm *fm, uint32_t *vol);
+static int fm_getrssi(struct fm *fm, uint32_t *rssi);
+static int fm_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+
+
+
+
+
+
+static uint16_t RDAFM_CHIP_ID = 0x5808;
+static RDA_RADIO_WORK_E RDA_RADIO_WorkType = FM_RECEIVER;
+
+
+
+#if 1
+static uint16_t RDA5802N_initialization_reg[]={
+       0xC005, //02h 
+       0x0000,
+       0x0400,
+       0xC6ED, //0x86AD, //05h
+       0x6000,
+       0x721A, //0x42C6
+       0x0000,
+       0x0000,
+       0x0000,  //0x0ah
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,  //0x10h
+       0x0019,
+       0x2A11,
+       0xB042,  
+       0x2A11,  
+       0xB831,  //0x15h 
+       0xC000,
+       0x2A91,
+       0x9400,
+       0x00A8,
+       0xc400,  //0x1ah
+       0xF7CF,  //Ìá¸ßÔ¶¶ËÔëÉùÒÖÖÆ  
+       0x2414, //0x2ADC,  //0x1ch ÌáÉýVIO VDDÖ®¼äѹ²îÒýÆðµÄ²»Á¼
+       0x806F, 
+       0x4608,
+       0x0086,
+       0x0661, //0x20H
+       0x0000,
+       0x109E,
+       0x23C8,
+       0x0406,
+       0x0E1C, //0x25H
+};
+#else
+static uint16_t RDA5802N_initialization_reg[]={
+       0xc401, //02h
+       0x0000,
+       0x0400,
+       0x86ad, //05h//
+       0x0000,
+       0x42c6,
+       0x0000,
+       0x0000,
+       0x0000,  //0x0ah
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,
+       0x0000,  //0x10h
+       0x0019,  
+       0x2a11,
+       0xa053,//0x80,0x53,
+       0x3e11,//0x22,0x11,     
+       0xfc7d,  //0x15h 
+       0xc000,
+       0x2a91,
+       0x9400,
+       0x00a8,
+       0xc400,  //0x1ah
+       0xe000,
+       0x2b1d, //0x23,0x14
+       0x816a,
+       0x4608,
+       0x0086,
+       0x0661,  //0x20h
+       0x0000,  
+       0x109e,
+       0x2244,
+       0x0408,  //0x24
+       0x0408,  //0x25
+};
+#endif
+
+static RDA_FM_REG_T RDA5820NS_TX_initialization_reg[]={
+       {0x02, 0xE003},
+       {0xFF, 100},    // if address is 0xFF, sleep value ms
+       {0x02, 0xE001},
+       {0x19, 0x88A8},
+       {0x1A, 0x4290},
+       {0x68, 0x0AF0},
+       {0x40, 0x0001},
+       {0x41, 0x41FF},
+       {0xFF, 500},
+       {0x03, 0x1B90},
+};
+
+static RDA_FM_REG_T RDA5820NS_RX_initialization_reg[]={
+       {0x02, 0x0002}, //Soft reset
+       {0xFF, 100},    // wait
+       {0x02, 0xC001},  //Power Up 
+       {0x05, 0x888F},  //LNAP  0x884F --LNAN
+       {0x06, 0x6000},
+       {0x13, 0x80E1},
+       {0x14, 0x2A11},
+       {0x1C, 0x22DE},
+       {0x21, 0x0020},
+       {0x03, 0x1B90},
+};
+
+
+
+static struct file_operations fm_ops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = fm_ops_ioctl,
+       .llseek = fm_ops_lseek,
+       .open = fm_ops_open,
+       .release = fm_ops_release,
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+static DECLARE_MUTEX(fm_ops_mutex);
+#else
+DEFINE_SEMAPHORE(fm_ops_mutex);
+#endif
+
+static int RDAFM_GetChipID(uint8_t chipAddr, uint16_t *pChipID)
+{
+       int err;
+       int ret = -1;
+       uint16_t val = 0x0002;
+
+       //Reset RDA FM
+       err = RDAFM_write(chipAddr, 0x02, val);
+       if(err < 0){
+#ifdef FMDEBUG
+               FM_DEBUG("RDAFM_GetChipID: reset FM chip failed!\n");
+#endif
+               ret = -1;
+               return ret;
+       }
+       msleep(80);
+
+       val = 0;
+       err = RDAFM_read(chipAddr, 0x0C, &val);
+       if (err == 0)
+       {
+               if ((0x5802 == val) || (0x5803 == val))
+               {
+                       err = RDAFM_read(chipAddr, 0x0E, &val);
+
+                       if (err == 0)
+                               *pChipID = val;
+                       else
+                               *pChipID = 0x5802;
+
+#ifdef FMDEBUG
+                       FM_DEBUG("RDAFM_GetChipID: Chip ID = %04X\n", val);
+#endif
+
+                       ret = 0;
+
+               }
+               else if ((0x5805 == val) || (0x5820 == val))
+               {
+                       *pChipID = val;
+                       ret = 0;
+               }
+               else
+               {
+#ifdef FMDEBUG
+                       FM_DEBUG("RDAFM_GetChipID: get chip ID failed! get value = %04X\n", val);
+#endif
+                       ret = -1;
+               }
+
+       }
+       else
+       {
+#ifdef FMDEBUG
+               FM_DEBUG("RDAFM_GetChipID: get chip ID failed!\n");
+#endif
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+/*
+ *  RDAFM_read
+ */
+static int RDAFM_read(uint8_t chipAddr, uint8_t regAddr, uint16_t *val)
+{
+       int ret = -1;
+       ret = rda_gpio_i2c_read_1_addr_2_data(chipAddr, regAddr, val);
+
+       return ret;
+}
+
+/*
+ *  RDAFM_write
+ */
+static int RDAFM_write(uint8_t chipAddr, uint8_t regAddr, uint16_t val)
+{
+       int n;
+
+       n = rda_gpio_i2c_write_1_addr_2_data(chipAddr, regAddr, val);
+       if (n < 0)
+       {
+               FM_ALERT("RDAFM_write send:0x%X err:%d\n", regAddr, n);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int RDAFM_clear_hmute(uint8_t chipAddr)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+
+       FM_DEBUG("RDAFM_clear_hmute\n");
+
+       ret = RDAFM_read(chipAddr, 0x02, &tRegValue);
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_clear_hmute  read register failed!\n"); 
+               return -1;
+       }
+
+       tRegValue |= (1 << 14);
+
+       ret = RDAFM_write(chipAddr, 0x02, tRegValue);
+
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_clear_hmute  write register failed!\n"); 
+               return -1;
+       }
+
+       if(fm_tune_data.byPowerUp){
+               if (fm_tune(g_fm_struct, &(fm_tune_data.parm)) < 0)
+               {
+                       fm_tune_data.byPowerUp = false;
+                       memset(&fm_tune_data.parm, 0, sizeof(fm_tune_data.parm));
+                       return -EPERM;
+               }
+               fm_tune_data.byPowerUp = false;
+               memset(&fm_tune_data.parm, 0, sizeof(fm_tune_data.parm));
+       }
+
+       return 0;
+}
+
+
+
+static int RDAFM_enable_hmute(uint8_t chipAddr)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+
+       FM_DEBUG("RDAFM_enable_hmute\n");
+
+       ret = RDAFM_read(chipAddr, 0x02, &tRegValue);
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_enable_hmute  read register failed!\n"); 
+               return -1;
+       }
+
+       tRegValue &= (~(1 << 14));
+
+       ret = RDAFM_write(chipAddr, 0x02, tRegValue);
+
+       if (ret < 0)
+       {
+               FM_ALERT("RDAFM_enable_hmute  write register failed!\n"); 
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static bool RDAFM_Scan(uint8_t chipAddr, 
+               uint16_t min_freq, uint16_t max_freq,
+               uint16_t *pFreq,
+               uint16_t *pScanTBL, 
+               uint16_t *ScanTBLsize, 
+               uint16_t scandir, 
+               uint16_t space)
+{
+       uint16_t tFreq, tRegValue = 0;
+       uint16_t tmp_scanTBLsize = *ScanTBLsize;
+       int ret = -1;
+       bool isTrueStation = false;
+       uint16_t oldValue = 0;
+       int channel = 0;
+
+       if((!pScanTBL) || (tmp_scanTBLsize == 0)) {
+               return false;
+       }
+
+       //clear the old value of pScanTBL
+       memset(pScanTBL, 0, sizeof(uint16_t)*RDA599X_SCANTBL_SIZE);
+
+       if(tmp_scanTBLsize > RDA599X_SCANTBL_SIZE)
+       {
+               tmp_scanTBLsize = RDA599X_SCANTBL_SIZE;
+       }
+
+       //scan up
+       if(scandir == RDA599X_FM_SCAN_UP){ // now, only support scan up
+               tFreq = min_freq;
+       }else{ //scan down
+               tFreq = max_freq;//max_freq compare need or not   
+       }
+
+       //mute FM
+       RDAFM_enable_hmute(chipAddr);
+
+       //set seekth
+       tRegValue = 0;
+       RDAFM_read(chipAddr, 0x05, &tRegValue);
+       tRegValue &= (~(0x7f<<8));
+       tRegValue |= ((0x8 & 0x7f) << 8);
+       RDAFM_write(chipAddr, 0x05, tRegValue);
+       msleep(50);
+
+       atomic_set(&scan_complete_flag, 1);
+
+       do {
+               if(atomic_read(&scan_complete_flag) == 0)
+                       break;
+               isTrueStation = false;
+
+               //set channel and enable TUNE
+               tRegValue = 0;
+               RDAFM_read(chipAddr, 0x03, &tRegValue);
+               tRegValue &= (~(0x03ff<<6)); //clear bit[15:6]
+               channel = tFreq - min_freq; 
+               tRegValue |= ((channel << 6) | (1 << 4)); //set bit[15:6] and bit[4]
+               ret = RDAFM_write(chipAddr, 0x03, tRegValue);
+               msleep(40);
+
+               //read 0x0B and check FM_TRUE(bit[8])
+               tRegValue = 0;
+               ret = RDAFM_read(chipAddr, 0x0B, &tRegValue);
+               if(!ret){
+                       if((tRegValue & 0x0100) == 0x0100){
+                               isTrueStation = true;
+                       }
+               }
+
+               //if this freq is a true station, read the channel
+               if(isTrueStation){
+                       //tRegValue = 0;
+                       //RDAFM_read(chipAddr, 0x0A, &tRegValue);
+                       //channel = ((tRegValue) & 0x03ff) - 5;
+                       channel = channel - 5;
+                       if((channel >= 0) && (channel != 85)){
+                               oldValue = *(pScanTBL+(channel/16));
+                               oldValue |= (1<<(channel%16));
+                               *(pScanTBL+(channel/16)) = oldValue;
+                       }
+               }
+
+               //increase freq
+               tFreq += space;
+       }while( tFreq <= max_freq );
+
+#if defined(MTK_MT6515) && defined(MT6626)
+       *(pScanTBL+13) = 0xb2d4;
+       *(pScanTBL+14) = 0xb2d4;
+       *(pScanTBL+15) = 0xb2d4;
+#endif
+
+       *ScanTBLsize = tmp_scanTBLsize;
+       *pFreq = 0;
+
+       //clear FM mute
+       RDAFM_clear_hmute(chipAddr);
+
+       return true;
+}
+
+
+static int fm_setup_cdev(struct fm *fm)
+{
+       int err;
+
+       err = alloc_chrdev_region(&fm->dev_t, 0, 1, FM_NAME);
+       if (err) {
+               FM_ALERT("alloc dev_t failed\n");
+               return -1;
+       }
+
+       FM_ALERT("alloc %s:%d:%d\n", FM_NAME,
+                       MAJOR(fm->dev_t), MINOR(fm->dev_t));
+
+       cdev_init(&fm->cdev, &fm_ops);
+
+       fm->cdev.owner = THIS_MODULE;
+       fm->cdev.ops = &fm_ops;
+
+       err = cdev_add(&fm->cdev, fm->dev_t, 1);
+       if (err) {
+               FM_ALERT("alloc dev_t failed\n");
+               return -1;
+       }
+
+       fm->cls = class_create(THIS_MODULE, FM_NAME);
+       if (IS_ERR(fm->cls)) {
+               err = PTR_ERR(fm->cls);
+               FM_ALERT("class_create err:%d\n", err);
+               return err;            
+       }    
+       fm->dev = device_create(fm->cls, NULL, fm->dev_t, NULL, FM_NAME);
+
+       return 0;
+}
+
+
+
+static int fm_ops_ioctl(struct file *filp,
+               unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+       struct fm *fm = container_of(filp->f_dentry->d_inode->i_cdev, struct fm, cdev);
+
+       FM_DEBUG("%s cmd(%x)\n", __func__, cmd);
+
+       switch(cmd)
+       {
+               case FM_IOCTL_POWERUP:
+                       {
+                               struct fm_tune_parm parm;
+                               FM_DEBUG("FM_IOCTL_POWERUP\n");
+
+                               if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm)))
+                                       return -EFAULT;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_powerup(fm, &parm);
+                               up(&fm_ops_mutex);
+                               if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm)))
+                                       return -EFAULT;
+                               break;
+                       }
+
+               case FM_IOCTL_POWERDOWN:
+                       {
+                               FM_DEBUG("FM_IOCTL_POWERDOWN\n");
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_powerdown(fm);
+                               up(&fm_ops_mutex);
+                               break;
+                       }
+
+                       // tune (frequency, auto Hi/Lo ON/OFF )
+               case FM_IOCTL_TUNE:
+                       {
+                               struct fm_tune_parm parm;
+                               FM_DEBUG("FM_IOCTL_TUNE\n");
+
+                               if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_tune_parm)))
+                                       return -EFAULT;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_tune(fm, &parm);
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &parm, sizeof(struct fm_tune_parm)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_SEEK:
+                       {
+                               struct fm_seek_parm parm;
+                               FM_DEBUG("FM_IOCTL_SEEK\n");
+
+                               if (copy_from_user(&parm, (void*)arg, sizeof(struct fm_seek_parm)))
+                                       return -EFAULT;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_seek(fm, &parm);
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &parm, sizeof(struct fm_seek_parm)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_SETVOL:
+                       {
+                               uint32_t vol;
+                               FM_DEBUG("FM_IOCTL_SETVOL\n");
+
+                               if(copy_from_user(&vol, (void*)arg, sizeof(uint32_t))) {
+                                       FM_ALERT("copy_from_user failed\n");
+                                       return -EFAULT;
+                               }
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_setvol(fm, vol);
+                               up(&fm_ops_mutex);
+
+                               break;
+                       }
+
+               case FM_IOCTL_GETVOL:
+                       {
+                               uint32_t vol;
+                               FM_DEBUG("FM_IOCTL_GETVOL\n");
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               ret = fm_getvol(fm, &vol);
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &vol, sizeof(uint32_t)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_MUTE:
+                       {
+                               uint32_t bmute;
+                               FM_DEBUG("FM_IOCTL_MUTE\n");
+
+                               if (copy_from_user(&bmute, (void*)arg, sizeof(uint32_t)))
+                               {
+                                       FM_DEBUG("copy_from_user mute failed!\n");
+                                       return -EFAULT;    
+                               }
+
+                               FM_DEBUG("FM_IOCTL_MUTE:%d\n", bmute); 
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+
+                               if (bmute){
+                                       ret = RDAFM_enable_hmute(fm->chipAddr);
+                               }else{
+                                       ret = RDAFM_clear_hmute(fm->chipAddr);
+                               }
+
+                               up(&fm_ops_mutex);
+
+                               break;
+                       }
+
+               case FM_IOCTL_GETRSSI:
+                       {
+                               uint32_t rssi;
+                               FM_DEBUG("FM_IOCTL_GETRSSI\n");
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+
+                               ret = fm_getrssi(fm, &rssi);
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &rssi, sizeof(uint32_t)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_RW_REG:
+                       {
+                               struct fm_ctl_parm parm_ctl;
+                               FM_DEBUG("FM_IOCTL_RW_REG\n");
+
+                               if (copy_from_user(&parm_ctl, (void*)arg, sizeof(struct fm_ctl_parm)))
+                                       return -EFAULT;
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+
+                               if(parm_ctl.rw_flag == 0) //write
+                               {
+                                       ret = RDAFM_write(fm->chipAddr, parm_ctl.addr, parm_ctl.val);
+                               }
+                               else
+                               {
+                                       ret = RDAFM_read(fm->chipAddr, parm_ctl.addr, &parm_ctl.val);
+                               }
+
+                               up(&fm_ops_mutex);
+                               if ((parm_ctl.rw_flag == 0x01) && (!ret)) // Read success.
+                               { 
+                                       if (copy_to_user((void*)arg, &parm_ctl, sizeof(struct fm_ctl_parm)))
+                                               return -EFAULT;
+                               }
+                               break;
+                       }
+
+               case FM_IOCTL_GETCHIPID:
+                       {
+                               uint16_t chipid;            
+
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+
+                               RDAFM_GetChipID(fm->chipAddr, &chipid);
+                               //chipid = fm->chip_id;
+                               chipid = 0x6620;
+                               FM_DEBUG("FM_IOCTL_GETCHIPID:%04x\n", chipid);   
+                               up(&fm_ops_mutex);
+
+                               if (copy_to_user((void*)arg, &chipid, sizeof(uint16_t)))
+                                       return -EFAULT;
+
+                               break;
+                       }
+
+               case FM_IOCTL_IS_FM_POWERED_UP:
+                       {
+                               uint32_t powerup;
+                               FM_DEBUG("FM_IOCTL_IS_FM_POWERED_UP");
+                               if (fm->powerup) {
+                                       powerup = 1;
+                               } else {
+                                       powerup = 0;
+                               }
+                               if (copy_to_user((void*)arg, &powerup, sizeof(uint32_t)))
+                                       return -EFAULT;
+                               break;
+                       }
+
+#ifdef FMDEBUG
+               case FM_IOCTL_DUMP_REG:
+                       {
+                               uint16_t chipid = 0;
+                               if (down_interruptible(&fm_ops_mutex))
+                                       return -EFAULT;
+                               RDAFM_GetChipID(fm->chipAddr, &chipid);
+                               up(&fm_ops_mutex);
+
+                               break;
+                       }
+#endif
+
+               case FM_IOCTL_SCAN:
+                       {
+                               struct fm_scan_parm parm;
+                               FM_DEBUG("FM_IOCTL_SCAN\n");
+                               if (false == fm->powerup){
+                                       return -EFAULT;
+                               }
+                               if(copy_from_user(&parm, (void*)arg, sizeof(struct fm_scan_parm))){
+                                       return -EFAULT;
+                               }
+                               if (down_interruptible(&fm_ops_mutex)){
+                                       return -EFAULT;
+                               }
+                               fm_scan(fm, &parm);
+                               up(&fm_ops_mutex);
+
+                               if(copy_to_user((void*)arg, &parm, sizeof(struct fm_scan_parm))){
+                                       return -EFAULT;
+                               }
+
+                               break;
+                       }
+
+               case FM_IOCTL_STOP_SCAN:
+                       {
+                               FM_DEBUG("FM_IOCTL_STOP_SCAN\n");
+                               break;
+                       }
+
+               case FM_IOCTL_SCAN_GETRSSI:
+                       {
+                               FM_DEBUG("FM_IOCTL_SCAN_GETRSSI\n");
+                               break;
+                       }
+
+               case FM_IOCTL_GETMONOSTERO:
+                       {
+                               FM_DEBUG("FM_IOCTL_GETMONOSTERO\n");
+                               break;
+                       }
+
+               case FM_IOCTL_SETMONOSTERO:
+                       {
+                               FM_DEBUG("FM_IOCTL_SETMONOSTERO\n");
+                               break;
+                       }
+
+               case FM_IOCTL_GETCURPAMD:
+                       {
+                               FM_DEBUG("FM_IOCTL_GETCURPAMD\n");
+                               break;
+                       }
+
+               case FM_IOCTL_EM_TEST:
+                       {
+                               FM_DEBUG("FM_IOCTL_EM_TEST\n");
+                               break;
+                       }
+
+               case FM_IOCTL_RDS_SUPPORT:
+                       {
+                               FM_DEBUG("FM_IOCTL_RDS_SUPPORT\n");
+                               break;
+                       }
+
+               case FM_IOCTL_RDS_ONOFF:
+                       {
+                               FM_DEBUG("FM_IOCTL_RDS_ONOFF\n");
+                               break;
+                       }
+
+               case FM_IOCTL_GETGOODBCNT:
+                       {
+                               FM_DEBUG("FM_IOCTL_GETGOODBCNT\n");
+                               break;
+                       }
+
+               case FM_IOCTL_GETBADBNT:
+                       {
+                               FM_DEBUG("FM_IOCTL_GETBADBNT\n");
+                               break;
+                       }
+
+               case FM_IOCTL_GETBLERRATIO:
+                       {
+                               FM_DEBUG("FM_IOCTL_GETBLERRATIO\n");
+                               break;
+                       }
+
+               case FM_IOCTL_POWERUP_TX:
+                       {
+                               FM_DEBUG("FM_IOCTL_POWERUP_TX\n");
+                               break;
+                       }
+
+               case FM_IOCTL_TUNE_TX:
+                       {
+                               FM_DEBUG("FM_IOCTL_TUNE_TX\n");
+                               break;
+                       }
+
+               case FM_IOCTL_TX_SUPPORT:
+                       {
+                               FM_DEBUG("FM_IOCTL_TX_SUPPORT\n");
+                               break;
+                       }
+
+               case FM_IOCTL_RDSTX_ENABLE:
+                       {
+                               FM_DEBUG("FM_IOCTL_RDSTX_ENABLE\n");
+                               break;
+                       }
+
+               case FM_IOCTL_RDSTX_SUPPORT:
+                       {
+                               FM_DEBUG("FM_IOCTL_RDSTX_SUPPORT\n");
+                               break;
+                       }
+
+               case FM_IOCTL_TX_SCAN:
+                       {
+                               FM_DEBUG("FM_IOCTL_TX_SCAN\n");
+                               break;
+                       }
+
+               case FM_IOCTL_RDS_TX:
+                       {
+                               FM_DEBUG("FM_IOCTL_RDS_TX\n");
+                               break;
+                       }
+
+               case FM_IOCTL_OVER_BT_ENABLE:
+                       {
+                               FM_DEBUG("FM_IOCTL_OVER_BT_ENABLE\n");
+                               break;
+                       }
+
+               case FM_IOCTL_ANA_SWITCH:
+                       {
+                               FM_DEBUG("FM_IOCTL_ANA_SWITCH\n");
+                               break;
+                       }
+
+               case FM_IOCTL_GETCAPARRAY:
+                       {
+                               FM_DEBUG("FM_IOCTL_GETCAPARRAY\n");
+                               break;
+                       }
+
+               case FM_IOCTL_GPS_RTC_DRIFT:
+                       {
+                               FM_DEBUG("FM_IOCTL_GPS_RTC_DRIFT\n");
+                               break;
+                       }
+
+               case FM_IOCTL_I2S_SETTING:
+                       {
+                               FM_DEBUG("FM_IOCTL_I2S_SETTING\n");
+                               break;
+                       }
+
+               case FM_IOCTL_RDS_GROUPCNT:
+                       {
+                               FM_DEBUG("FM_IOCTL_RDS_GROUPCNT\n");
+                               break;
+                       }
+
+               case FM_IOCTL_RDS_GET_LOG:
+                       {
+                               FM_DEBUG("FM_IOCTL_RDS_GET_LOG\n");
+                               break;
+                       }
+
+               case FM_IOCTL_GET_HW_INFO:
+                       {
+                               FM_DEBUG("FM_IOCTL_GET_HW_INFO\n");
+                               break;
+                       }
+               default:
+                       {
+                               FM_DEBUG("default\n");
+                               break;
+                       }
+       }
+
+       return ret;
+}
+static loff_t fm_ops_lseek(struct file *filp, loff_t off, int whence)
+{
+//     struct fm *fm = filp->private_data;
+
+       if(whence == SEEK_END){
+               //fm_hwscan_stop(fm);
+               atomic_set(&scan_complete_flag, 0);
+       }else if(whence == SEEK_SET){  
+               //FM_EVENT_SEND(fm->rds_event, FM_RDS_DATA_READY);
+       }   
+       return off;    
+}
+
+static int fm_ops_open(struct inode *inode, struct file *filp)
+{
+       struct fm *fm = container_of(inode->i_cdev, struct fm, cdev);
+
+       FM_DEBUG("%s\n", __func__);
+
+       if (down_interruptible(&fm_ops_mutex))
+               return -EFAULT;
+
+       // TODO: only have to set in the first time?
+       // YES!!!!
+
+       fm->ref++;
+
+       up(&fm_ops_mutex);
+
+       filp->private_data = fm;
+
+       // TODO: check open flags
+
+       return 0;
+}
+
+static int fm_ops_release(struct inode *inode, struct file *filp)
+{
+       int err = 0;
+       struct fm *fm = container_of(inode->i_cdev, struct fm, cdev);
+
+       FM_DEBUG("%s\n", __func__);
+
+       if (down_interruptible(&fm_ops_mutex))
+               return -EFAULT;
+       fm->ref--;
+       if(fm->ref < 1) {
+               if(fm->powerup == true) {
+                       fm_powerdown(fm);           
+               }
+       }
+
+       up(&fm_ops_mutex);
+
+       return err;
+}
+
+static int fm_init(void)
+{
+       int err;
+       struct fm *fm = NULL;
+       int ret = -1;
+
+
+       FM_DEBUG("%s()\n", __func__);
+       if (!(fm = kzalloc(sizeof(struct fm), GFP_KERNEL)))
+       {
+               FM_ALERT("-ENOMEM\n");
+               err = -ENOMEM;
+               goto ERR_EXIT;
+       }
+
+       fm->ref = 0;
+       fm->powerup = false;
+       fm->chipAddr = RDAFM_SLAVE_ADDR;
+       atomic_set(&scan_complete_flag, 0);
+
+       // First, read 5802NM chip ID
+       FM_DEBUG("%s()First, read 5802NM chip ID\n", __func__);
+       ret = RDAFM_GetChipID(fm->chipAddr, &RDAFM_CHIP_ID);
+       FM_DEBUG("%s() 5802NM chip ID = 0x%04x\n", __func__, RDAFM_CHIP_ID);
+       // if failed, means use FM in 5990P_E
+       if(ret < 0){
+               // enable the FM chip in combo
+               FM_DEBUG("%s() enable the FM chip in combo\n", __func__);
+               ret = rda_fm_power_on();
+               if(ret < 0){
+                       err = -ENOMEM;
+                       goto ERR_EXIT;
+               }
+               msleep(100);
+               ret = RDAFM_GetChipID(fm->chipAddr, &RDAFM_CHIP_ID);
+               FM_DEBUG("%s() the FM in combo chip ID = 0x%04x\n", __func__, RDAFM_CHIP_ID);
+               if(ret < 0){
+                       err = -ENOMEM;
+                       goto ERR_EXIT;
+               }else{
+                       fm->chip_id = RDAFM_CHIP_ID;
+               }
+
+               // disable the FM chip for power saving
+               ret = rda_fm_power_off();
+               if(ret < 0){
+                       err = -ENOMEM;
+                       goto ERR_EXIT;
+               }
+       }else{
+               fm->chip_id = RDAFM_CHIP_ID;
+       }
+
+
+
+       if ((err = fm_setup_cdev(fm)))
+       {
+               goto ERR_EXIT;
+       }
+
+       g_fm_struct = fm;
+
+       /***********Add porc file system*************/
+
+       g_fm_proc = create_proc_entry(FM_PROC_FILE, 0444, NULL);
+       if (g_fm_proc == NULL) {
+               FM_ALERT("create_proc_entry failed\n");
+               err = -ENOMEM;
+               goto ERR_EXIT;
+       } else {
+               g_fm_proc->read_proc = fm_proc_read;
+               g_fm_proc->write_proc = NULL;
+               //g_fm_proc->owner = THIS_MODULE;
+               FM_ALERT("create_proc_entry success\n");
+       }
+
+       /********************************************/
+
+       FM_DEBUG("fm_init is ok!\n");
+
+       return 0;
+
+ERR_EXIT:
+       kfree(fm);
+
+       return err;
+}
+
+static int fm_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+       int cnt= 0;
+       struct fm *fm  = g_fm_struct;
+       FM_ALERT("Enter fm_proc_read.\n");
+       if(off != 0)
+               return 0;
+       if (fm != NULL && fm->powerup) {
+               cnt = sprintf(page, "1\n");
+       } else {
+               cnt = sprintf(page, "0\n");
+       }
+       *eof = 1;
+       FM_ALERT("Leave fm_proc_read. cnt = %d\n", cnt);
+       return cnt;
+}
+
+
+static int fm_destroy(struct fm *fm)
+{
+       int err = 0;
+
+       FM_DEBUG("%s\n", __func__);
+
+       device_destroy(fm->cls, fm->dev_t);
+       class_destroy(fm->cls);
+
+       cdev_del(&fm->cdev);
+       unregister_chrdev_region(fm->dev_t, 1);
+
+       fm_powerdown(fm);
+
+       /***********************************/
+       remove_proc_entry(FM_PROC_FILE, NULL);
+
+       /**********************************/
+
+       // FIXME: any other hardware configuration ?
+
+       // free all memory
+       kfree(fm);
+
+       return err;
+}
+
+/*
+ *  fm_powerup
+ */
+static int fm_powerup(struct fm *fm, struct fm_tune_parm *parm)
+{
+       int i;
+       uint16_t tRegValue = 0x0002;
+       int ret = -1;
+
+
+       if (fm->powerup)
+       {
+               parm->err = FM_BADSTATUS;
+               return -EPERM;
+       }
+
+       //  if chip_id is ID_RDA5820NS, enable the FM chip in combo
+       if(fm->chip_id == ID_RDA5820NS){
+               ret = rda_fm_power_on();
+               if(ret < 0){
+                       return -EPERM;
+               }
+               msleep(100);
+       }
+
+
+       //Reset RDA FM
+       tRegValue = 0x0002;
+       RDAFM_write(fm->chipAddr, 0x02, tRegValue);
+       msleep(100);
+
+
+
+       if (ID_RDA5802N == RDAFM_CHIP_ID){
+               for (i=0; i<((sizeof(RDA5802N_initialization_reg)) / (sizeof(uint16_t))); i++)
+               {
+                       ret = RDAFM_write(fm->chipAddr, i+2, RDA5802N_initialization_reg[i]);
+
+                       if (ret < 0)
+                       {
+                               FM_DEBUG("fm_powerup init failed!\n");
+
+                               parm->err = FM_FAILED;
+
+                               return -EPERM;
+                       }
+               }
+
+       }else if (ID_RDA5820NS == RDAFM_CHIP_ID){
+               if(RDA_RADIO_WorkType == FM_RECEIVER){
+                       for (i = 0; i < ((sizeof(RDA5820NS_RX_initialization_reg)) / (sizeof(RDA_FM_REG_T))); i++)
+                       {
+                               if(RDA5820NS_RX_initialization_reg[i].address == 0xFF){
+                                       msleep(RDA5820NS_RX_initialization_reg[i].value);
+                               }else{
+                                       ret = RDAFM_write(fm->chipAddr, RDA5820NS_RX_initialization_reg[i].address, RDA5820NS_RX_initialization_reg[i].value);
+                                       if (ret < 0)
+                                       {
+                                               FM_DEBUG("fm_powerup init failed!\n");
+                                               parm->err = FM_FAILED;
+                                               return -EPERM;
+                                       }
+                               }
+                       }
+               }else{
+                       for (i = 0; i < ((sizeof(RDA5820NS_TX_initialization_reg)) / (sizeof(RDA_FM_REG_T))); i++)
+                       {
+                               if(RDA5820NS_TX_initialization_reg[i].address == 0xFF){
+                                       msleep(RDA5820NS_TX_initialization_reg[i].value);
+                               }else{
+                                       ret = RDAFM_write(fm->chipAddr, RDA5820NS_TX_initialization_reg[i].address, RDA5820NS_TX_initialization_reg[i].value);
+                                       if (ret < 0)
+                                       {
+                                               FM_DEBUG("fm_powerup init failed!\n");
+                                               parm->err = FM_FAILED;
+                                               return -EPERM;
+                                       }
+                               }
+                       }
+               }
+
+       }
+
+
+       FM_DEBUG("pwron ok\n");
+       fm->powerup = true;
+
+       if (fm_tune(fm, parm) < 0)
+       {
+               return -EPERM;
+       }
+       fm_tune_data.byPowerUp = true;
+       memcpy(&fm_tune_data.parm, parm, sizeof(fm_tune_data.parm));
+
+       parm->err = FM_SUCCESS;
+
+       return 0;
+
+}
+
+/*
+ *  fm_powerdown
+ */
+static int fm_powerdown(struct fm *fm)
+{
+       uint16_t tRegValue = 0;
+       int ret = -1;
+
+       RDAFM_read(fm->chipAddr, 0x02, &tRegValue);
+       tRegValue &= (~(1 << 0));
+       RDAFM_write(fm->chipAddr, 0x02, tRegValue);
+
+       if(fm->chip_id == ID_RDA5820NS){
+               ret = rda_fm_power_off();
+               if(ret < 0){
+                       return -EPERM;
+               }
+       }
+
+       fm->powerup = false;
+       FM_ALERT("pwrdown ok\n");
+
+       return 0;
+}
+
+/*
+ *  fm_seek
+ */
+static int fm_seek(struct fm *fm, struct fm_seek_parm *parm)
+{
+       int ret = 0;
+       uint16_t val = 0;
+       uint8_t spaec = 1;
+       uint16_t tFreq = 875;
+       uint16_t tRegValue = 0;
+       uint16_t bottomOfBand = 875;
+       int falseStation = -1;
+
+
+       if (!fm->powerup)
+       {
+               parm->err = FM_BADSTATUS;
+               return -EPERM;
+       }
+
+       if (parm->space == FM_SPACE_100K)
+       {
+               spaec = 1;
+               val &= (~((1<<0) | (1<<1)));
+       }
+       else if (parm->space == FM_SPACE_200K)
+       {
+               spaec = 2;
+               val &= (~(1<<1));
+               val |= (1<<0);
+       }
+       else
+       {
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->band == FM_BAND_UE)
+       {
+               val &= (~((1<<2) | (1<<3)));
+               bottomOfBand = 875;
+               fm->min_freq = 875;
+               fm->max_freq = 1080;
+       }
+       else if (parm->band == FM_BAND_JAPAN) 
+       {
+               val &= (~(1<<3));
+               val |= (1 << 2);
+               bottomOfBand = 760;
+               fm->min_freq = 760;
+               fm->max_freq = 910;
+       }
+       else if (parm->band == FM_BAND_JAPANW) {
+               val &= (~(1<<2));
+               val |= (1 << 3);
+               bottomOfBand = 760;
+               fm->min_freq = 760;
+               fm->max_freq = 1080;
+       }
+       else
+       {
+               FM_ALERT("band:%d out of range\n", parm->band);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->freq < fm->min_freq || parm->freq > fm->max_freq) {
+               FM_ALERT("freq:%d out of range\n", parm->freq);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->seekth > 0x0B) {
+               FM_ALERT("seekth:%d out of range\n", parm->seekth);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       RDAFM_read(fm->chipAddr, 0x05, &tRegValue);
+       tRegValue &= (~(0x7f<<8));
+       //tRegValue |= ((parm->seekth & 0x7f) << 8);
+       tRegValue |= ((0x8 & 0x7f) << 8);
+       RDAFM_write(fm->chipAddr, 0x05, tRegValue);
+
+
+#ifdef FMDEBUG
+       if (parm->seekdir == FM_SEEK_UP)
+               FM_DEBUG("seek %d up\n", parm->freq);
+       else
+               FM_DEBUG("seek %d down\n", parm->freq);
+#endif
+
+       // (1) set hmute bit
+       RDAFM_enable_hmute(fm->chipAddr);
+
+       tFreq = parm->freq;
+
+       do {
+
+               if (parm->seekdir == FM_SEEK_UP)
+                       tFreq += spaec;
+               else
+                       tFreq -= spaec;
+
+               if (tFreq > fm->max_freq)
+                       tFreq = fm->min_freq;
+               if (tFreq < fm->min_freq)
+                       tFreq = fm->max_freq;
+
+               val = (((tFreq - bottomOfBand+5) << 6) | (1 << 4) | (val & 0x0f));
+               RDAFM_write(fm->chipAddr, 0x03, val);
+               msleep(40);
+               ret = RDAFM_read(fm->chipAddr, 0x0B, &tRegValue);
+               if (ret < 0)
+               {
+                       FM_DEBUG("fm_seek: read register failed tunning freq = %4X\n", tFreq);
+                       falseStation = -1;
+               }
+               else
+               {
+                       if ((tRegValue & 0x0100) == 0x0100)
+                               falseStation = 0;
+                       else
+                               falseStation = -1;
+               }
+
+               if(falseStation == 0)
+                       break;
+
+       }while(tFreq != parm->freq);
+
+
+       //clear hmute
+       RDAFM_clear_hmute(fm->chipAddr);
+
+       if (falseStation == 0) // seek successfully
+       {    
+               parm->freq = tFreq;
+               FM_ALERT("fm_seek success, freq:%d\n", parm->freq);
+               parm->err = FM_SUCCESS;
+
+       }
+       else
+       {
+               FM_ALERT("fm_seek failed, invalid freq\n");
+               parm->err = FM_SEEK_FAILED;
+               ret = -1;
+       }
+
+       return ret;
+}
+
+/*
+ *  fm_scan
+ */
+static int  fm_scan(struct fm *fm, struct fm_scan_parm *parm)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+       uint16_t scandir = RDA599X_FM_SCAN_UP; //scandir ËÑË÷·½Ïò
+       uint8_t space = 1; 
+
+       if (!fm->powerup){
+               parm->err = FM_BADSTATUS;
+               return -EPERM;
+       }
+
+       RDAFM_read(fm->chipAddr, 0x03, &tRegValue);
+
+       if (parm->space == FM_SPACE_100K){
+               space = 1;
+               tRegValue &= (~((1<<0) | (1<<1))); //set 03H's bit[1:0] to 00
+       }else if (parm->space == FM_SPACE_200K) {
+               space = 2;
+               tRegValue &= (~(1<<1)); //clear bit[1]
+               tRegValue |= (1<<0);    //set bit[0]
+       }else{
+               //default
+               space = 1;
+               tRegValue &= (~((1<<0) | (1<<1))); //set 03H's bit[1:0] to 00
+       }
+
+       if(parm->band == FM_BAND_UE){
+               tRegValue &= (~((1<<2) | (1<<3)));
+               fm->min_freq = 875;
+               fm->max_freq = 1080;
+       }else if(parm->band == FM_BAND_JAPAN){
+               tRegValue &= (~(1<<3));
+               tRegValue |= (1 << 2);
+               fm->min_freq = 760;
+               fm->max_freq = 900;
+       }else if(parm->band == FM_BAND_JAPANW){
+               tRegValue &= (~(1<<2));
+               tRegValue |= (1 << 3);
+               fm->min_freq = 760;
+               fm->max_freq = 1080;
+       }else{
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       //set space and band
+       RDAFM_write(fm->chipAddr, 0x03, tRegValue);
+       msleep(40);
+
+
+       if(RDAFM_Scan(fm->chipAddr, fm->min_freq, fm->max_freq, &(parm->freq), parm->ScanTBL, &(parm->ScanTBLSize), scandir, space)){
+               parm->err = FM_SUCCESS;
+       }else{
+               parm->err = FM_SEEK_FAILED;
+       }
+
+       return ret;
+}
+
+
+static int fm_setvol(struct fm *fm, uint32_t vol)
+{
+       int ret = 0;
+       uint16_t tRegValue = 0;
+
+       if (vol > 15)
+               vol = 15;
+
+       FM_DEBUG("fm_setvol:%d\n", vol);
+
+       ret = RDAFM_read(fm->chipAddr, 0x05, &tRegValue);
+       if (ret)
+               return -EPERM;
+       tRegValue &= ~(0x000f);
+       tRegValue |= vol;
+
+       ret = RDAFM_write(fm->chipAddr, 0x05, tRegValue);
+       if (ret)
+               return -EPERM;
+
+       return 0;
+}
+
+static int fm_getvol(struct fm *fm, uint32_t *vol)
+{
+       int ret = 0;
+       uint16_t tRegValue;
+
+       ret = RDAFM_read(fm->chipAddr, 0x05, &tRegValue);
+       if (ret)
+               return -EPERM;
+
+       if (ret)
+               return -EPERM;
+
+       *vol = (tRegValue & 0x000F);
+
+       return 0;
+}
+
+static int fm_getrssi(struct fm *fm, uint32_t *rssi)
+{
+       int ret = 0;
+       uint16_t tRegValue;
+
+       ret = RDAFM_read(fm->chipAddr, 0x0B, &tRegValue);
+       if (ret)
+               return -EPERM;
+
+
+       *rssi = (uint32_t)((tRegValue >> 9) & RDAFM_MASK_RSSI);
+
+       FM_DEBUG("rssi value:%d\n", *rssi);
+
+       return 0;
+}
+
+/*
+ *  fm_tune
+ */
+static int fm_tune(struct fm *fm, struct fm_tune_parm *parm)
+{
+       int ret;
+       uint16_t val = 0;
+       uint8_t space = 1;
+       uint16_t bottomOfBand = 875;
+
+
+       FM_DEBUG("%s\n", __func__);
+
+       if (!fm->powerup)
+       {
+               parm->err = FM_BADSTATUS;
+               return -EPERM;
+       } 
+
+       if (parm->space == FM_SPACE_100K)
+       {
+               space = 1;
+               val &= (~((1<<0) | (1<<1)));
+       }
+       else if (parm->space == FM_SPACE_200K)
+       {
+               space = 2;
+               val |= (1<<0);
+               val &= (~(1<<1));
+       }
+       else
+       {
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->band == FM_BAND_UE)
+       {
+               val &= (~((1<<2) | (1<<3)));
+               bottomOfBand = 875;
+               fm->min_freq = 875;
+               fm->max_freq = 1080;
+       }
+       else if (parm->band == FM_BAND_JAPAN) 
+       {
+               val &= (~(1<<3));
+               val |= (1 << 2);
+               bottomOfBand = 760;
+               fm->min_freq = 760;
+               fm->max_freq = 910;
+       }
+       else if (parm->band == FM_BAND_JAPANW) {
+               val &= (~(1<<2));
+               val |= (1 << 3);
+               bottomOfBand = 760;
+               fm->min_freq = 760;
+               fm->max_freq = 1080;
+       }
+       else
+       {
+               FM_ALERT("band:%d out of range\n", parm->band);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       if (parm->freq < fm->min_freq || parm->freq > fm->max_freq) {
+               FM_ALERT("freq:%d out of range\n", parm->freq);
+               parm->err = FM_EPARM;
+               return -EPERM;
+       }
+
+       FM_DEBUG("fm_tune, freq:%d\n", parm->freq);
+
+
+       val = (((parm->freq - bottomOfBand + 5) << 6) | (1 << 4) | (val & 0x0f));
+
+       ret = RDAFM_write(fm->chipAddr, 0x03, val);
+       if (ret < 0)
+       {
+               FM_ALERT("fm_tune write freq failed\n");
+               parm->err = FM_SEEK_FAILED;
+               return ret;
+       }
+       msleep(40);
+
+       return ret;
+}
+
+
+static int mt_fm_probe(struct platform_device *pdev)
+{
+       int err = -1;
+       FM_DEBUG("mt_fm_probe\n");
+
+       if ((err = fm_init()))
+       {
+               FM_ALERT("fm_init ERR:%d\n", err);
+       }   
+
+       return err;   
+} 
+
+static int mt_fm_remove(struct platform_device *pdev)
+{
+       FM_DEBUG("mt_fm_remove\n");
+
+       struct fm *fm = g_fm_struct;
+       if(fm)
+       {    
+               fm_destroy(fm);
+               fm = NULL;
+       }
+
+       return 0; 
+}
+
+
+static struct platform_driver mt_fm_dev_drv =
+{
+       .probe   = mt_fm_probe,
+       .remove  = mt_fm_remove,
+       .driver = {
+               .name   = FM_NAME,
+               .owner  = THIS_MODULE,    
+       }
+};
+
+#if defined(MTK_MT6515)
+static struct platform_device mt_fm_device = {
+       .name   = FM_NAME,
+       .id = -1, 
+};
+#endif
+
+
+/*
+ *  mt_fm_init
+ */
+static int __init mt_fm_init(void)
+{
+       int err = 0;
+
+       FM_DEBUG("mt_fm_init\n");
+#if defined(MTK_MT6515)
+       err = platform_device_register(&mt_fm_device);
+       if(err){
+               FM_DEBUG("platform_device_register  fail\n");
+               return err;
+       }else{
+               FM_DEBUG("platform_device_register  success\n");
+       }
+#endif
+       err = platform_driver_register(&mt_fm_dev_drv);
+       if (err)
+       {
+               FM_DEBUG("platform_driver_register failed\n");
+       }else{
+               FM_DEBUG("platform_driver_register success\n");
+       }
+
+       return err;
+}
+
+/*
+ *  mt_fm_exit
+ */
+static void __exit mt_fm_exit(void)
+{
+       FM_DEBUG("mt_fm_exit\n");
+       platform_driver_unregister(&mt_fm_dev_drv);
+#if defined(MTK_MT6515)
+       platform_device_unregister(&mt_fm_device);
+#endif
+}
+
+module_init(mt_fm_init);
+module_exit(mt_fm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MediaTek FM Driver");
+MODULE_AUTHOR("William Chung <William.Chung@MediaTek.com>");
diff --git a/drivers/net/wireless/rda5990/rda_5990_power_ctrl/Makefile b/drivers/net/wireless/rda5990/rda_5990_power_ctrl/Makefile
new file mode 100755 (executable)
index 0000000..ab2ac90
--- /dev/null
@@ -0,0 +1,3 @@
+obj-y := rda_5990_power_ctrl.o 
+#for 模拟I2C
+#obj-y := rda_5990_power_ctrl_by_gpio.o 
diff --git a/drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl.c b/drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl.c
new file mode 100755 (executable)
index 0000000..250d032
--- /dev/null
@@ -0,0 +1,1895 @@
+/* ----------------------------------------------------------------------- *\r
+ *\r
+ This file created by albert RDA Inc\r
+ */\r
+\r
+#include <linux/module.h>\r
+#include <linux/init.h>\r
+#include <linux/slab.h>\r
+#include <linux/i2c.h>\r
+#include <linux/string.h>\r
+#include <linux/rtc.h>       /* get the user-level API */\r
+#include <linux/bcd.h>\r
+#include <linux/list.h>\r
+#include <linux/delay.h>\r
+\r
+\r
+#include <linux/nfs_fs.h>\r
+#include <linux/nfs_fs_sb.h>\r
+#include <linux/nfs_mount.h>\r
+#include <linux/fs.h>\r
+#include <linux/file.h>\r
+#include <linux/tty.h>\r
+#include <linux/syscalls.h>\r
+#include <asm/termbits.h>\r
+#include <linux/serial.h>\r
+\r
+#include <linux/wakelock.h>\r
+\r
+#define RDA5890_USE_CRYSTAL // if use share crystal should close this\r
+#define RDA5990_USE_DCDC\r
+\r
+#define u32 unsigned int\r
+#define u8 unsigned char\r
+#define u16 unsigned short\r
+\r
+#define RDA_I2C_CHANNEL                (0)\r
+#define RDA_WIFI_CORE_ADDR (0x13 << 1)\r
+#define RDA_WIFI_RF_ADDR (0x14 << 1) //correct add is 0x14\r
+#define RDA_BT_CORE_ADDR (0x15 << 1)\r
+#define RDA_BT_RF_ADDR (0x16 << 1)\r
+\r
+#define I2C_MASTER_ACK              (1<<0)\r
+#define I2C_MASTER_RD               (1<<4)\r
+#define I2C_MASTER_STO              (1<<8)\r
+#define I2C_MASTER_WR               (1<<12)\r
+#define I2C_MASTER_STA              (1<<16)\r
+\r
+#define RDA_WIFI_RF_I2C_DEVNAME "rda_wifi_rf_i2c"\r
+#define RDA_WIFI_CORE_I2C_DEVNAME "rda_wifi_core_i2c"\r
+#define RDA_BT_RF_I2C_DEVNAME "rda_bt_rf_i2c"\r
+#define RDA_BT_CORE_I2C_DEVNAME "rda_bt_core_i2c"\r
+\r
+static struct mutex i2c_rw_lock;\r
+//extern int\r
+//i2c_register_board_info(int busnum, struct i2c_board_info const *info,\r
+//                        unsigned n);\r
+\r
+\r
+static unsigned short wlan_version = 0;\r
+static struct wake_lock rda_5990_wake_lock;\r
+static struct delayed_work   rda_5990_sleep_worker;\r
+static struct i2c_client * rda_wifi_core_client = NULL;\r
+static struct i2c_client * rda_wifi_rf_client = NULL;\r
+static struct i2c_client * rda_bt_core_client = NULL;\r
+static struct i2c_client * rda_bt_rf_client = NULL;\r
+\r
+static const struct i2c_device_id wifi_rf_i2c_id[] = {{RDA_WIFI_RF_I2C_DEVNAME, 0}, {}};\r
+MODULE_DEVICE_TABLE(i2c, wifi_rf_i2c_id);\r
+\r
+static unsigned short wifi_rf_force[] = { RDA_I2C_CHANNEL, RDA_WIFI_RF_ADDR, I2C_CLIENT_END, I2C_CLIENT_END };\r
+\r
+static struct i2c_board_info __initdata i2c_rda_wifi_rf={ I2C_BOARD_INFO(RDA_WIFI_RF_I2C_DEVNAME, (RDA_WIFI_RF_ADDR>>1))};\r
+static struct i2c_driver rda_wifi_rf_driver;\r
+\r
+\r
+\r
+static const struct i2c_device_id wifi_core_i2c_id[] = {{RDA_WIFI_CORE_I2C_DEVNAME, 0}, {}};\r
+MODULE_DEVICE_TABLE(i2c, wifi_core_i2c_id);\r
+\r
+static unsigned short wifi_core_force[] = { RDA_I2C_CHANNEL, RDA_WIFI_CORE_ADDR, I2C_CLIENT_END, I2C_CLIENT_END };\r
+static struct i2c_board_info __initdata i2c_rda_wifi_core={ I2C_BOARD_INFO(RDA_WIFI_CORE_I2C_DEVNAME, (RDA_WIFI_CORE_ADDR>>1))};\r
+static struct i2c_driver rda_wifi_core_driver;\r
+\r
+\r
+\r
+static const struct i2c_device_id bt_rf_i2c_id[] = {{RDA_BT_RF_I2C_DEVNAME, 0}, {}};\r
+\r
+MODULE_DEVICE_TABLE(i2c, bt_rf_i2c_id);\r
+\r
+static unsigned short bt_rf_force[] = { RDA_I2C_CHANNEL, RDA_BT_RF_ADDR, I2C_CLIENT_END, I2C_CLIENT_END };\r
+static struct i2c_board_info __initdata i2c_rda_bt_rf={ I2C_BOARD_INFO(RDA_BT_RF_I2C_DEVNAME, (RDA_BT_RF_ADDR>>1))};\r
+static struct i2c_driver rda_bt_rf_driver;\r
+\r
+static const struct i2c_device_id bt_core_i2c_id[] = {{RDA_BT_CORE_I2C_DEVNAME, 0}, {}};\r
+MODULE_DEVICE_TABLE(i2c, bt_core_i2c_id);\r
+\r
+static unsigned short bt_core_force[] = { RDA_I2C_CHANNEL, RDA_BT_CORE_ADDR, I2C_CLIENT_END, I2C_CLIENT_END };\r
+static struct i2c_board_info __initdata i2c_rda_bt_core={ I2C_BOARD_INFO(RDA_BT_CORE_I2C_DEVNAME, (RDA_BT_CORE_ADDR>>1))};\r
+static struct i2c_driver rda_bt_core_driver;\r
+\r
+\r
+static u8 isBigEnded = 0;\r
+static u8 wifi_in_test_mode = 0;\r
+\r
+static int i2c_write_data_4_addr_4_data(struct i2c_client* client, const u32 addr, const u32 data)\r
+{\r
+       unsigned char ADDR[4], DATA[4];\r
+       int ret = 0;\r
+\r
+       if(!isBigEnded)\r
+       {\r
+               ADDR[0] = addr >> 24;\r
+               ADDR[1] = addr >> 16;\r
+               ADDR[2] = addr >> 8;\r
+               ADDR[3] = addr >> 0;\r
+\r
+               DATA[0] = data >> 24;\r
+               DATA[1] = data >> 16;\r
+               DATA[2] = data >> 8;\r
+               DATA[3] = data >> 0;\r
+       }\r
+       else\r
+       {\r
+               memcpy(ADDR, &addr, sizeof(u32));\r
+               memcpy(DATA, &data, sizeof(u32));\r
+       }\r
+\r
+       ret = i2c_master_send(client, (char*)ADDR, 4);\r
+       if (ret < 0)\r
+       {\r
+               printk(KERN_INFO "***i2c_write_data_4_addr_4_data send:0x%X err:%d\n", addr,ret);\r
+               return -1;\r
+       }\r
+\r
+       ret = i2c_master_send(client, (char*)DATA, 4);\r
+       if (ret < 0)\r
+       {\r
+               printk(KERN_INFO "***i2c_write_data_4_addr_4_data send:0x%X err:%d\n", data,ret);\r
+               return -1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int i2c_read_4_addr_4_data(struct i2c_client* client, const u32 addr, u32* data)\r
+{\r
+       unsigned char ADDR[4], DATA[4];\r
+       int ret = 0;\r
+\r
+       if(!isBigEnded)\r
+       {\r
+               ADDR[0] = addr >> 24;\r
+               ADDR[1] = addr >> 16;\r
+               ADDR[2] = addr >> 8;\r
+               ADDR[3] = addr >> 0;\r
+       }\r
+       else\r
+       {\r
+               memcpy(ADDR, &addr, sizeof(u32));\r
+       }\r
+\r
+       ret = i2c_master_send(client, (char*)ADDR, 4);\r
+       if (ret < 0)\r
+       {\r
+               printk(KERN_INFO "***i2c_read_4_addr_4_data send:0x%X err:%d\n", addr,ret);\r
+               return -1;\r
+       }\r
+\r
+       ret = i2c_master_recv(client, (char*)DATA, 4);\r
+       if (ret < 0)\r
+       {\r
+               printk(KERN_INFO "***i2c_read_4_addr_4_data send:0x%X err:%d\n", addr,ret);\r
+               return -1;\r
+       }\r
+\r
+       if(!isBigEnded)\r
+       {\r
+               data[3] = DATA[0];\r
+               data[2] = DATA[1];\r
+               data[1] = DATA[2];\r
+               data[0] = DATA[3];\r
+       }\r
+       else\r
+               memcpy(data, DATA, sizeof(u32));\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int i2c_write_1_addr_2_data(struct i2c_client* client, const u8 addr, const u16 data)\r
+{\r
+       unsigned char  DATA[3];\r
+       int ret = 0;\r
+\r
+       if(!isBigEnded)\r
+       {\r
+               DATA[0] = addr;\r
+               DATA[1] = data >> 8;\r
+               DATA[2] = data >> 0;\r
+       }\r
+       else\r
+       {\r
+               DATA[0] = addr;\r
+               DATA[1] = data >> 0;\r
+               DATA[2] = data >> 8;\r
+       }\r
+\r
+       ret = i2c_master_send(client, (char*)DATA, 3);\r
+       if (ret < 0)\r
+       {\r
+               printk(KERN_INFO "***i2c_write_1_addr_2_data send:0x%X err:%d bigendia: %d \n", addr,ret, isBigEnded);\r
+               return -1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int i2c_read_1_addr_2_data(struct i2c_client* client, const u8 addr, u16* data)\r
+{\r
+       unsigned char DATA[2];\r
+       int ret = 0;\r
+\r
+       ret = i2c_master_send(client, (char*)&addr, 1);\r
+       if (ret < 0)\r
+       {\r
+               printk(KERN_INFO "***i2c_read_1_addr_2_data send:0x%X err:%d\n", addr,ret);\r
+               return -1;\r
+       }\r
+\r
+       ret = i2c_master_recv(client, DATA, 2);\r
+       if (ret < 0)\r
+       {\r
+               printk(KERN_INFO "***i2c_read_1_addr_2_data send:0x%X err:%d\n", addr,ret);\r
+               return -1;\r
+       }\r
+\r
+       if(!isBigEnded)\r
+       {\r
+               *data = (DATA[0] << 8) | DATA[1];\r
+       }\r
+       else\r
+       {\r
+               *data = (DATA[1] << 8) | DATA[0];\r
+       }\r
+       return 0;\r
+}\r
+\r
+\r
+static int rda_wifi_rf_probe(struct i2c_client *client, const struct i2c_device_id *id)\r
+{\r
+       int result = 0;\r
+\r
+       rda_wifi_rf_client = client;\r
+       return result;\r
+}\r
+\r
+static int rda_wifi_rf_remove(struct i2c_client *client)\r
+{\r
+       return 0;\r
+}\r
+\r
+static int rda_wifi_rf_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)\r
+{  \r
+       strcpy(info->type, RDA_WIFI_RF_I2C_DEVNAME);   \r
+       return 0;   \r
+}\r
+\r
+static struct i2c_driver rda_wifi_rf_driver = {\r
+       .probe = rda_wifi_rf_probe,\r
+       .remove = rda_wifi_rf_remove,\r
+       .detect = rda_wifi_rf_detect,\r
+       .driver.name = RDA_WIFI_RF_I2C_DEVNAME,\r
+       .id_table = wifi_rf_i2c_id,\r
+//     .address_data = &rda_wifi_rf_addr_data,\r
+       //.address_list = (const unsigned short*) wifi_rf_force,\r
+};\r
+\r
+static int rda_wifi_core_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)\r
+{\r
+       strcpy(info->type, RDA_WIFI_CORE_I2C_DEVNAME);\r
+       return 0;   \r
+}\r
+\r
+static int rda_wifi_core_probe(struct i2c_client *client, const struct i2c_device_id *id)\r
+{\r
+       int result = 0;\r
+\r
+       rda_wifi_core_client = client;\r
+       return result;\r
+}\r
+\r
+static int rda_wifi_core_remove(struct i2c_client *client)\r
+{\r
+       return 0;\r
+}\r
+\r
+int rda_wifi_power_off(void);\r
+static void rda_wifi_shutdown(struct i2c_client * client)\r
+{\r
+       printk("rda_wifi_shutdown \n");\r
+       rda_wifi_power_off();\r
+}\r
+\r
+static struct i2c_driver rda_wifi_core_driver = \r
+{\r
+       .probe = rda_wifi_core_probe,\r
+       .remove = rda_wifi_core_remove,\r
+       .detect = rda_wifi_core_detect,\r
+       .shutdown = rda_wifi_shutdown,\r
+       .driver.name = RDA_WIFI_CORE_I2C_DEVNAME,\r
+       .id_table = wifi_core_i2c_id,\r
+       //.address_data = &rda_wifi_core_addr_data,\r
+       //.address_list = (const unsigned short*)wifi_core_force,\r
+};\r
+\r
+const u32 wifi_core_init_data[][2] = \r
+{\r
+\r
+};\r
+\r
+u16 wifi_off_data[][2] = \r
+{\r
+       { 0x3F, 0x0001 }, //page up\r
+       { 0x31, 0x0B40 }, //power off wifi\r
+       { 0x3F, 0x0000 }, //page down\r
+};\r
+\r
+u16 wifi_en_data[][2] = \r
+{\r
+    //item:VerD_wf_on_2012_02_08\r
+    {0x3f, 0x0001},\r
+#ifdef RDA5990_USE_DCDC     /*houzhen update Mar 15 2012 */\r
+    {0x23, 0x8FA1},//20111001 higher AVDD voltage to improve EVM to 0x8f21 download current -1db 0x8fA1>>0x8bA1   \r
+#else\r
+       {0x23, 0x0FA1},\r
+#endif\r
+    {0x31, 0x0B40 }, //power off wifi\r
+//    {0x22, 0xD3C7},//for ver.c 20111109, txswitch\r
+    {0x24, 0x80C8},//freq_osc_in[1:0]00  0x80C8 >> 0x80CB\r
+    {0x27, 0x4925},//for ver.c20111109, txswitch\r
+    //                {0x28, 0x80A1}, //BT_enable \r
+    {0x31, 0x8140},//enable wifi  \r
+    {0x32, 0x0113},//set_ rdenout_ldooff_wf=0; rden4in_ldoon_wf=1                                              \r
+    //                {0x39, 0x0004},  //uart switch to wf  \r
+    {0x3F, 0x0000}, //page down\r
+};\r
+\r
+\r
+u16 wifi_dc_cal_data[][2]=\r
+{\r
+       {0x3f, 0x0000},\r
+       {0x30, 0x0248},\r
+       {0x30, 0x0249},\r
+       //{wait 200ms; } here\r
+};\r
+\r
+u16 wifi_dig_reset_data[][2]=\r
+{\r
+       {0x3F,  0x0001},\r
+       {0x31,  0x8D40},\r
+       {0x31,  0x8F40},\r
+       {0x31,  0x8b40},\r
+       {0x3F,  0x0000},\r
+};\r
+\r
+u16 wifi_rf_init_data_verE[][2] = \r
+{\r
+       {0x3f, 0x0000},\r
+       //{;;set_rf_swi},ch\r
+       {0x06, 0x0101},\r
+       {0x07, 0x0101},\r
+       {0x08, 0x0101},\r
+       {0x09, 0x0101},\r
+       {0x0A, 0x002C},//aain_0\r
+       {0x0D, 0x0507},\r
+       {0x0E, 0x2300},\r
+       {0x0F, 0x5689},//\r
+       //{;;//set_RF  },\r
+       {0x10, 0x0f78},//20110824\r
+       {0x11, 0x0602},\r
+       {0x13, 0x0652},//adc_tuning_bit[011]\r
+       {0x14, 0x8886},\r
+       {0x15, 0x0990},\r
+       {0x16, 0x049f},\r
+       {0x17, 0x0990},\r
+       {0x18, 0x049F},\r
+       {0x19, 0x3C01},\r
+       {0x1C, 0x0934},\r
+       {0x1D, 0xFF00},//for ver.D20120119for temperature 70 degree\r
+       //{0x1F, 0x01F8},//for ver.c20111109\r
+       //{0x1F, 0x0300},//for burst tx 不锁\r
+       {0x20, 0x06E4},\r
+       {0x21, 0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset\r
+       {0x22, 0x24DC},\r
+       {0x23, 0x23FF},\r
+       {0x24, 0x00FC},\r
+       {0x26, 0x004F},//004F >> 005f premote pa \r
+       {0x27, 0x171D},///mdll*7\r
+       {0x28, 0x031D},///mdll*7\r
+       {0x2A, 0x2860},//et0x2849-8.5p  :yd 0x2861-7pf C1,C2=6.8p\r
+       {0x2B, 0x0800},//bbpll,or ver.c20111116\r
+       {0x32, 0x8a08},\r
+       {0x33, 0x1D02},//liuyanan\r
+       //{;;//agc_gain},\r
+       {0x36, 0x02f4}, //00F8;//gain_7\r
+       {0x37, 0x01f4}, //0074;//aain_6\r
+       {0x38, 0x21d4}, //0014;//gain_5\r
+       {0x39, 0x25d4}, //0414;//aain_4\r
+       {0x3A, 0x2584}, //1804;//gain_3\r
+       {0x3B, 0x2dc4}, //1C04;//aain_2\r
+       {0x3C, 0x2d04}, //1C02;//gain_1\r
+       {0x3D, 0x2c02}, //3C01;//gain_0\r
+       {0x33, 0x1502},//liuyanan\r
+       //{;;SET_channe},_to_11\r
+       {0x1B, 0x0001},//set_channel   \r
+       {0x30, 0x024D},\r
+       {0x29, 0xD468},\r
+       {0x29, 0x1468},\r
+       {0x30, 0x0249},\r
+       {0x3f, 0x0000},\r
+};\r
+\r
+u16 wifi_rf_init_data[][2] = \r
+{\r
+       {0x3f, 0x0000},\r
+       //{;;set_rf_swi},ch\r
+       {0x06, 0x0101},\r
+       {0x07, 0x0101},\r
+       {0x08, 0x0101},\r
+       {0x09, 0x0101},\r
+       {0x0A, 0x002C},//aain_0\r
+       {0x0D, 0x0507},\r
+       {0x0E, 0x2300},//2012_02_20  \r
+       {0x0F, 0x5689},//\r
+       //{;;//set_RF  },\r
+       {0x10, 0x0f78},//20110824\r
+       {0x11, 0x0602},\r
+       {0x13, 0x0652},//adc_tuning_bit[011]\r
+       {0x14, 0x8886},\r
+       {0x15, 0x0990},\r
+       {0x16, 0x049f},\r
+       {0x17, 0x0990},\r
+       {0x18, 0x049F},\r
+       {0x19, 0x3C01},//sdm_vbit[3:0]=1111\r
+       {0x1C, 0x0934},\r
+       {0x1D, 0xFF00},//for ver.D20120119for temperature 70 degree 0xCE00 >> 0xFF00\r
+       {0x1F, 0x0300},//div2_band_48g_dr=1;div2_band_48g_reg[8:0]\r
+       {0x20, 0x06E4},\r
+       {0x21, 0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset\r
+       {0x22, 0x24DC},\r
+       {0x23, 0x23FF},\r
+       {0x24, 0x00FC},\r
+       {0x26, 0x004F},//004F >> 005f premote pa \r
+       {0x27, 0x171D},///mdll*7\r
+       {0x28, 0x031D},///mdll*7\r
+       {0x2A, 0x2860},//et0x2849-8.5p  :yd 0x2861-7pf\r
+       {0x2B, 0x0800},//bbpll,or ver.c20111116\r
+       {0x32, 0x8a08},\r
+       {0x33, 0x1D02},//liuyanan\r
+       //{;;//agc_gain},\r
+       {0x36, 0x02f4}, //00F8;//gain_7\r
+       {0x37, 0x01f4}, //0074;//aain_6\r
+       {0x38, 0x21d4}, //0014;//gain_5\r
+       {0x39, 0x25d4}, //0414;//aain_4\r
+       {0x3A, 0x2584}, //1804;//gain_3\r
+       {0x3B, 0x2dc4}, //1C04;//aain_2\r
+       {0x3C, 0x2d04}, //1C02;//gain_1\r
+       {0x3D, 0x2c02}, //3C01;//gain_0\r
+       {0x33, 0x1502},//liuyanan\r
+       //{;;SET_channe},_to_11\r
+       {0x1B, 0x0001},//set_channel   \r
+       {0x30, 0x024D},\r
+       {0x29, 0xD468},\r
+       {0x29, 0x1468},\r
+       {0x30, 0x0249},\r
+       {0x3f, 0x0000},\r
+};\r
+u16 wifi_uart_debug_data[][2] = \r
+{\r
+    {0x3F,0x0001},\r
+    {0x28,0x80A1}, //BT_enable \r
+    {0x39,0x0004}, //uart switch to wf\r
+    {0x3f,0x0000},\r
+};\r
+\r
+u16 wifi_tm_en_data[][2] =\r
+{\r
+    {0x3F,0x0001},\r
+#ifdef RDA5990_USE_DCDC     /*houzhen update Mar 15 2012 */\r
+    {0x23, 0x8FA1},//20111001 higher AVDD voltage to improve EVM to 0x8f21 download current -1db 0x8fA1>>0x8bA1   \r
+#else\r
+       {0x23, 0x0FA1},\r
+#endif\r
+    {0x22,0xD3C7},//for ver.c 20111109, tx\r
+       {0x24, 0x80C8},//freq_osc_in[1:0]00  0x80C8 >> 0x80CB \r
+    {0x27,0x4925},//for ver.c20111109, txs\r
+    {0x28,0x80A1}, //BT_enable            \r
+    {0x29,0x111F},                        \r
+    {0x31,0x8140},                        \r
+    {0x32,0x0113},//set_ rdenout_ldooff_wf\r
+    {0x39,0x0004},//uart switch to wf\r
+    {0x3f,0x0000},\r
+};\r
+\r
+u16 wifi_tm_rf_init_data[][2] = \r
+{\r
+       {0x3f, 0x0000},\r
+       //set_rf_switch                                                  \r
+       {0x06,0x0101},                                                     \r
+       {0x07,0x0101},                                                     \r
+       {0x08,0x0101},                                                     \r
+       {0x09,0x0101},                                                     \r
+       {0x0A,0x002C},//aain_0   \r
+       {0x0D, 0x0507},                                          \r
+       {0x0E,0x2300},//2012_02_20                                         \r
+       {0x0F,0x5689},//                                                   \r
+       //set_RF                                                            \r
+       {0x10,0x0f78},//20110824                                             \r
+       {0x11,0x0602},                                                     \r
+       {0x13,0x0652},//adc_tuning_bit[011]                               \r
+       {0x14,0x8886},                                                     \r
+       {0x15,0x0990},                                                     \r
+       {0x16,0x049f},                                                     \r
+       {0x17,0x0990},                                                     \r
+       {0x18,0x049F},                                                     \r
+       {0x19,0x3C01},//sdm_vbit[3:0]=1111                                 \r
+       {0x1C,0x0934},                                                     \r
+       {0x1D,0xFF00},//for ver.D20120119for temperature 70 degree         \r
+       {0x1F,0x0300},//div2_band_48g_dr=1;div2_band_48g_reg[8:0]1000000000\r
+       {0x20,0x06E4},                                                     \r
+       {0x21,0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset      \r
+       {0x22,0x24DC},                                                     \r
+       {0x23,0x23FF},                                                     \r
+       {0x24,0x00FC},                                                     \r
+       {0x26,0x004F},                                                     \r
+       {0x27,0x171D},///mdll*7                                            \r
+       {0x28,0x031D},///mdll*7                                            \r
+       {0x2A,0x2860},                                                     \r
+       {0x2B,0x0800},//bbpll,or ver.c20111116                             \r
+       {0x32,0x8a08},                                                     \r
+       {0x33,0x1D02},//liuyanan                                           \r
+       //agc_gain                                                          \r
+       {0x36,0x02f4}, //00F8;//gain_7                                     \r
+       {0x37,0x01f4}, //0074;//aain_6                                     \r
+       {0x38,0x21d4}, //0014;//gain_5                                     \r
+       {0x39,0x25d4}, //0414;//aain_4                                     \r
+       {0x3A,0x2584}, //1804;//gain_3                                     \r
+       {0x3B,0x2dc4}, //1C04;//aain_2                                     \r
+       {0x3C,0x2d04}, //1C02;//gain_1                                     \r
+       {0x3D,0x2c02}, //3C01;//gain_0                                     \r
+       //DC_CAL                                                            \r
+       {0x30,0x0248},                                                     \r
+       {0x30,0x0249},                                                     \r
+       //wait 200ms;                                                       \r
+       {0x33,0x1502},//liuyanan                                           \r
+       //SET_channel_to_11                                                 \r
+       {0x1B,0x0001},//set_channel     \r
+       {0x3f,0x0000},\r
+};\r
+\r
+/*houzhen update Mar 15 2012\r
+  should be called when power up/down bt\r
+  */\r
+static int rda5990_wf_setup_A2_power(int enable)\r
+{\r
+       int ret;\r
+       u16 temp_data=0;\r
+       printk(KERN_INFO "***rda5990_wf_setup_A2_power start! \n");\r
+       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001);\r
+       if(ret)\r
+               goto err;\r
+\r
+       if(enable)\r
+       {\r
+               ret=i2c_read_1_addr_2_data(rda_wifi_rf_client,0x22,&temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               printk(KERN_INFO "***0xA2 readback value:0x%X \n", temp_data);\r
+\r
+               temp_data |=0x0200;   /*en reg4_pa bit*/\r
+#ifdef RDA5890_USE_CRYSTAL     \r
+               temp_data &= ~(1 << 14); //disable xen_out\r
+#endif\r
+               ret=i2c_write_1_addr_2_data(rda_wifi_rf_client,0x22,temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               //read wlan version\r
+               ret = i2c_read_1_addr_2_data(rda_wifi_rf_client,0x21,&temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               else\r
+                       wlan_version = temp_data;\r
+       }\r
+       else\r
+       {\r
+               ret=i2c_read_1_addr_2_data(rda_wifi_rf_client,0x28,&temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               if(temp_data&0x8000)        // bt is on \r
+               {\r
+                       goto out;\r
+               }\r
+               else\r
+               {\r
+                       ret=i2c_read_1_addr_2_data(rda_wifi_rf_client,0x22,&temp_data);\r
+                       if(ret)\r
+                               goto err;\r
+                       temp_data&=0xfdff;\r
+\r
+                       ret=i2c_write_1_addr_2_data(rda_wifi_rf_client,0x22,temp_data);\r
+                       if(ret)\r
+                               goto err;\r
+               }\r
+               wlan_version = 0;\r
+       }\r
+\r
+out:\r
+       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000);\r
+       if(ret)\r
+               goto err;\r
+       printk(KERN_INFO "***rda5990_wf_setup_A2_power succeed! \n");\r
+       return 0;\r
+\r
+err:\r
+       printk(KERN_INFO "***rda5990_wf_setup_A2_power failed! \n");\r
+       return -1;\r
+}\r
+\r
+\r
+int rda_wifi_rf_init(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       \r
+       mutex_lock(&i2c_rw_lock);\r
+       if( (wlan_version&0x1f) == 7)\r
+       {\r
+               for(count = 0; count < sizeof(wifi_rf_init_data)/sizeof(wifi_rf_init_data[0]); count ++)\r
+               {\r
+                       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_rf_init_data[count][0], wifi_rf_init_data[count][1]);\r
+                       if(ret)\r
+                               goto err;\r
+               }\r
+       }\r
+       else if((wlan_version&0x1f) == 4 || (wlan_version&0x1f)==5)\r
+       {\r
+               for(count = 0; count < sizeof(wifi_rf_init_data_verE)/sizeof(wifi_rf_init_data_verE[0]); count ++)\r
+               {\r
+                       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_rf_init_data_verE[count][0], wifi_rf_init_data_verE[count][1]);\r
+                       if(ret)\r
+                               goto err;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               printk("unknown version of this 5990 chip\n");\r
+               goto err;\r
+       }\r
+       mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_rf_init_succceed \n");\r
+       msleep(5);   //5ms delay\r
+       return 0;\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_rf_init failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_wifi_dc_cal(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       for(count = 0; count < sizeof(wifi_dc_cal_data)/sizeof(wifi_dc_cal_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client ,wifi_dc_cal_data[count][0], wifi_dc_cal_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_rf_dc_calsuccceed \n");\r
+       msleep(50);   //50ms delay\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_rf_dc_calf_failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_wifi_en(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+    mutex_lock(&i2c_rw_lock); \r
+       for(count = 0; count < sizeof(wifi_en_data)/sizeof(wifi_en_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_en_data[count][0], wifi_en_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+               if(wifi_en_data[count][0] == 0x31)\r
+                       msleep(10);   //10ms delay\r
+       }\r
+\r
+       ret=rda5990_wf_setup_A2_power(1);       //en pa_reg for wf\r
+       if(ret)\r
+               goto err;\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       msleep(8);   //8ms delay\r
+\r
+       printk(KERN_INFO "***rda_wifi_en_succceed \n");\r
+       return 0;\r
+err:\r
+     mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_power_on failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_wifi_debug_en(void)\r
+{\r
+    unsigned int count = 0;\r
+    int ret = 0;\r
+    \r
+    mutex_lock(&i2c_rw_lock); \r
+       for(count = 0; count < sizeof(wifi_uart_debug_data)/sizeof(wifi_uart_debug_data[0]); count ++)\r
+       {\r
+      ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_uart_debug_data[count][0], wifi_uart_debug_data[count][1]);\r
+      if(ret)\r
+          goto err;\r
+       }\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+    return ret;\r
+}\r
+\r
+int rda_tm_wifi_en(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+    unsigned short temp_data;\r
+\r
+       for(count = 0; count < sizeof(wifi_tm_en_data)/sizeof(wifi_tm_en_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_tm_en_data[count][0], wifi_tm_en_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+       msleep(8);   //8ms delay\r
+    ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001); //PAGE UP\r
+    if(ret)\r
+        goto err;\r
\r
+    ret = i2c_read_1_addr_2_data(rda_wifi_rf_client,0x21,&temp_data);\r
+    if(ret)\r
+        goto err;\r
+    else\r
+        wlan_version = temp_data;\r
+\r
+    ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000); //PAGE DOWN\r
+    if(ret)\r
+        goto err;\r
\r
+\r
+       printk(KERN_INFO "***rda_wifi_en_succceed \n");\r
+       return 0;\r
+err:\r
+       printk(KERN_INFO "***rda_wifi_power_on failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_tm_wifi_rf_init(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+       for(count = 0; count < sizeof(wifi_tm_rf_init_data)/sizeof(wifi_tm_rf_init_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_tm_rf_init_data[count][0], wifi_tm_rf_init_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+       printk(KERN_INFO "***rda_wifi_rf_init_succceed \n");\r
+       msleep(5);   //5ms delay\r
+       return 0;\r
+\r
+err:\r
+       printk(KERN_INFO "***rda_wifi_rf_init failed! \n");\r
+       return -1;\r
+}\r
+/*houzhen add 2012 04 09\r
+  add to ensure wf dig powerup\r
+  */\r
+\r
+int rda_wifi_dig_reset(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       msleep(8);   //8ms delay\r
+    mutex_lock(&i2c_rw_lock);\r
+\r
+       for(count = 0; count < sizeof(wifi_dig_reset_data)/sizeof(wifi_dig_reset_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_dig_reset_data[count][0], wifi_dig_reset_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       msleep(8);   //8ms delay\r
+       printk(KERN_INFO "***rda_wifi_dig_reset \n");\r
+       return 0;\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_dig_reset failed! \n"); \r
+       return -1;\r
+}\r
+\r
+int rda_wlan_version(void)\r
+{\r
+       printk("******version %x \n", wlan_version);\r
+       return wlan_version;\r
+}\r
+\r
+int rda_wifi_power_off(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       u16 temp=0x0000;\r
+       printk(KERN_INFO "rda_wifi_power_off \n");\r
+\r
+       if(!rda_wifi_rf_client)\r
+       {\r
+               printk(KERN_INFO "rda_wifi_power_off failed on:i2c client \n");\r
+               return -1;\r
+       }\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       ret=rda5990_wf_setup_A2_power(0);   //disable pa_reg for wf\r
+       if(ret)\r
+               goto err;\r
+\r
+       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001);   //page up\r
+       if(ret)\r
+               goto err;\r
+\r
+       ret=i2c_read_1_addr_2_data(rda_wifi_rf_client,0x28,&temp);   //poll bt status\r
+       if(ret)\r
+               goto err;\r
+\r
+       if(temp&0x8000)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000);   //page down\r
+               if(ret)\r
+                       goto err;\r
+\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x0f, 0x2223);   // set antenna for bt\r
+               if(ret)\r
+                       goto err;\r
+\r
+       }\r
+\r
+       for(count = 0; count < sizeof(wifi_off_data)/sizeof(wifi_off_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_off_data[count][0], wifi_off_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+       printk(KERN_INFO "***rda_wifi_power_off success!!! \n");\r
+\r
+\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_power_off failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+int rda_wifi_power_on(void)\r
+{\r
+       int ret;\r
+       char retry = 3;\r
+\r
+    printk("------------------rda_wifi_power_on\n");\r
+       if(!rda_wifi_rf_client)\r
+       {\r
+               printk(KERN_INFO "rda_wifi_power_on failed on:i2c client \n");\r
+               return -1;\r
+       }\r
+\r
+_retry: \r
+\r
+\r
+       if(!wifi_in_test_mode)\r
+       {\r
+               ret = rda_wifi_en();    \r
+               if(ret < 0)\r
+                       goto err;\r
+\r
+               ret = rda_wifi_rf_init();       \r
+               if(ret < 0)\r
+                       goto err;\r
+\r
+               ret = rda_wifi_dc_cal();        \r
+               if(ret < 0)\r
+                       goto err;\r
+\r
+               msleep(20);   //20ms delay\r
+               ret=rda_wifi_dig_reset();   //houzhen add to ensure wf power up safely\r
+\r
+               if(ret < 0)\r
+                       goto err;\r
+               msleep(20);   //20ms delay\r
+       }\r
+       else\r
+       {\r
+               ret = rda_tm_wifi_en();\r
+               if(ret < 0)\r
+                       goto err;\r
+\r
+               ret = rda_tm_wifi_rf_init();\r
+               if(ret < 0)\r
+                       goto err;\r
+       }\r
+       printk(KERN_INFO "rda_wifi_power_on_succeed!! \n");\r
+\r
+       return 0;\r
+\r
+err:\r
+       printk(KERN_INFO "rda_wifi_power_on_failed retry:%d \n", retry);\r
+       if(retry -- > 0)\r
+       {   \r
+               rda_wifi_power_off();\r
+               goto _retry;\r
+       }\r
+\r
+       return -1;\r
+}\r
+\r
+extern int rda_wifi_power_on(void);\r
+\r
+int rda_fm_power_on(void)\r
+{\r
+       int ret = 0;\r
+       u16 temp = 0;\r
+\r
+       if(!rda_wifi_rf_client)\r
+       {\r
+               printk(KERN_INFO "rda_wifi_rf_client is NULL, rda_fm_power_on failed!\n");\r
+               return -1;\r
+       }\r
+\r
+       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001);   // page down\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+       ret = i2c_read_1_addr_2_data(rda_wifi_rf_client, 0x22, &temp);          //read 0xA2\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() read from address(0x%02x) failed! \n", __func__, 0x22);\r
+               return -1;\r
+       }\r
+       temp = temp & (~(1 << 15));             //clear bit[15]\r
+       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x22, temp);          //write back\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000);   // page up\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+int rda_fm_power_off(void)\r
+{\r
+       int ret = 0;\r
+       u16 temp = 0;\r
+\r
+       if(!rda_wifi_rf_client)\r
+       {\r
+               printk(KERN_INFO "rda_wifi_rf_client is NULL, rda_fm_power_off failed!\n");\r
+               return -1;\r
+       }\r
+\r
+       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001);   // page down\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+       ret = i2c_read_1_addr_2_data(rda_wifi_rf_client, 0x22, &temp);          //read 0xA2\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() read from address(0x%02x) failed! \n", __func__, 0x22);\r
+               return -1;\r
+       }\r
+       temp = temp | (1 << 15);                //set bit[15]\r
+       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x22, temp);          //write back\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+       ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000);   // page up\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+static int rda_bt_rf_probe(struct i2c_client *client, const struct i2c_device_id *id)\r
+{\r
+       int result = 0;\r
+\r
+       rda_bt_rf_client = client;\r
+       return result;\r
+}\r
+\r
+static int rda_bt_rf_remove(struct i2c_client *client)\r
+{\r
+       rda_bt_rf_client = NULL;\r
+       return 0;\r
+}\r
+\r
+static int rda_bt_rf_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)\r
+{  \r
+       strcpy(info->type, RDA_BT_RF_I2C_DEVNAME);   \r
+       return 0;   \r
+}\r
+\r
+static struct i2c_driver rda_bt_rf_driver = {\r
+       .probe = rda_bt_rf_probe,\r
+       .remove = rda_bt_rf_remove,\r
+       .detect = rda_bt_rf_detect,\r
+       .driver.name = RDA_BT_RF_I2C_DEVNAME,\r
+       .id_table = bt_rf_i2c_id,\r
+       //.address_data = &rda_bt_rf_addr_data,\r
+       //.address_list = (const unsigned short*)bt_rf_force,\r
+};\r
+\r
+\r
+static int rda_bt_core_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)\r
+{\r
+       strcpy(info->type, RDA_BT_CORE_I2C_DEVNAME);\r
+       return 0;   \r
+}\r
+\r
+static int rda_bt_core_probe(struct i2c_client *client, const struct i2c_device_id *id)\r
+{\r
+       int result = 0;\r
+\r
+       rda_bt_core_client = client;\r
+       return result;\r
+}\r
+\r
+static int rda_bt_core_remove(struct i2c_client *client)\r
+{\r
+       return 0;\r
+}\r
+\r
+static struct i2c_driver rda_bt_core_driver = {\r
+       .probe = rda_bt_core_probe,\r
+       .remove = rda_bt_core_remove,\r
+       .detect = rda_bt_core_detect,\r
+       .driver.name = RDA_BT_CORE_I2C_DEVNAME,\r
+       .id_table = bt_core_i2c_id,\r
+       //.address_data = &rda_bt_core_addr_data,\r
+       //.address_list = (const unsigned short*)bt_core_force,\r
+};\r
+\r
+u16 rda_5990_bt_off_data[][2] = \r
+{\r
+       {0x3f, 0x0001 }, //pageup\r
+       {0x28, 0x00A1 }, //power off bt\r
+       {0x3f, 0x0000 }, //pagedown\r
+};\r
+\r
+/*houzhen update 2012 03 06*/\r
+u16 rda_5990_bt_en_data[][2] = \r
+{\r
+    {0x3f, 0x0001 },           //pageup\r
+#ifdef RDA5990_USE_DCDC    \r
+    {0x23, 0x8FA1},              // //20111001 higher AVDD voltage to improve EVM\r
+#else\r
+       {0x23, 0x0FA1},\r
+#endif \r
+       {0x24, 0x80C8},           // ;//freq_osc_in[1:0]00      \r
+       {0x26, 0x47A5},           //  reg_vbit_normal_bt[2:0] =111\r
+       {0x27, 0x4925},           // //for ver.c20111109, txswitch\r
+       {0x29, 0x111F},           // // rden4in_ldoon_bt=1      \r
+       {0x32, 0x0111},           // set_ rdenout_ldooff_wf=0;                                           \r
+       {0x39, 0x0000},           //      //uart switch to bt\r
+\r
+       {0x28, 0x80A1},         // bt en\r
+       {0x3f, 0x0000},         //pagedown\r
+};\r
+\r
+\r
+u16 rda_5990_bt_dc_cal[][2] = \r
+{\r
+       {0x3f, 0x0000 }, \r
+       {0x30, 0x0129 },\r
+       {0x30, 0x012B },\r
+       {0x3f, 0x0000 }, \r
+};\r
+\r
+\r
+u16 rda_5990_bt_set_rf_switch_data[][2] = \r
+{\r
+       {0x3f, 0x0000 }, \r
+       {0x0F, 0x2223 },\r
+       {0x3f, 0x0000 }, \r
+};\r
+\r
+\r
+u16 RDA5990_bt_enable_clk_data[][2] = \r
+{\r
+       {0x3f, 0x0000 }, \r
+       {0x30, 0x0040 },\r
+       {0x2a, 0x285d },\r
+       {0x3f, 0x0000 }, \r
+};\r
+\r
+u16 RDA5990_bt_dig_reset_data[][2] = \r
+{\r
+       {0x3f, 0x0001 }, //pageup\r
+       {0x28, 0x86A1 },\r
+       {0x28, 0x87A1 },\r
+       {0x28, 0x85A1 },\r
+       {0x3f, 0x0000 }, //pagedown\r
+};\r
+\r
+/*houzhen update 2012 03 06*/\r
+u16 rda_5990_bt_rf_data[][2] = \r
+{\r
+       {0x3f, 0x0000}, //pagedown\r
+       {0x01, 0x1FFF},\r
+       {0x06, 0x07F7},\r
+       {0x08, 0x29E7},\r
+       {0x09, 0x0520},\r
+       {0x0B, 0x03DF},\r
+       {0x0C, 0x85E8},\r
+       {0x0F, 0x0DBC},\r
+       {0x12, 0x07F7},\r
+       {0x13, 0x0327},\r
+       {0x14, 0x0CCC},\r
+       {0x15, 0x0526},\r
+       {0x16, 0x8918},\r
+       {0x18, 0x8800},\r
+       {0x19, 0x10C8},\r
+       {0x1A, 0x9078},\r
+       {0x1B, 0x80E2},\r
+       {0x1C, 0x361F},\r
+       {0x1D, 0x4363},\r
+       {0x1E, 0x303F},\r
+       {0x23, 0x2222},\r
+       {0x24, 0x359D},\r
+       {0x27, 0x0011},\r
+       {0x28, 0x124F},\r
+       {0x39, 0xA5FC},\r
+       {0x3f, 0x0001}, //page 1\r
+       {0x00, 0x043F},\r
+       {0x01, 0x467F},\r
+       {0x02, 0x28FF},\r
+       {0x03, 0x67FF},\r
+       {0x04, 0x57FF},\r
+       {0x05, 0x7BFF},\r
+       {0x06, 0x3FFF},\r
+       {0x07, 0x7FFF},\r
+       {0x18, 0xF3F5},\r
+       {0x19, 0xF3F5},\r
+       {0x1A, 0xE7F3},\r
+       {0x1B, 0xF1FF},\r
+       {0x1C, 0xFFFF},\r
+       {0x1D, 0xFFFF},\r
+       {0x1E, 0xFFFF},\r
+       {0x1F, 0xFFFF},\r
+       //      {0x22, 0xD3C7}, \r
+       //      {0x23, 0x8fa1},\r
+       //      {0x24, 0x80c8},\r
+       //      {0x26, 0x47A5},\r
+       //      {0x27, 0x4925},\r
+       //      {0x28, 0x85a1},\r
+       //      {0x29, 0x111f},\r
+       //      {0x32, 0x0111},\r
+       //      {0x39, 0x0000},\r
+       {0x3f, 0x0000}, //pagedown\r
+};\r
+\r
+/*houzhen update Mar 15 2012\r
+  should be called when power up/down bt\r
+  */\r
+static int rda5990_bt_setup_A2_power(int enable)\r
+{\r
+       int ret;\r
+       u16 temp_data=0;\r
+\r
+       ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0001);\r
+       if(ret)\r
+               goto err;\r
+\r
+       if(enable)\r
+       {\r
+               ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x22,&temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               printk(KERN_INFO "***0xA2 readback value:0x%X \n", temp_data);\r
+\r
+               temp_data |=0x0200;   /*en reg4_pa bit*/\r
+\r
+               ret=i2c_write_1_addr_2_data(rda_bt_rf_client,0x22,temp_data);\r
+               if(ret)\r
+                       goto err;               \r
+       }\r
+       else\r
+       {\r
+               ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x31,&temp_data);\r
+               if(ret)\r
+                       goto err;\r
+\r
+               if(temp_data&0x8000)        // wf is on \r
+               {\r
+                       goto out;\r
+               }\r
+               else\r
+               {\r
+                       ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x22,&temp_data);\r
+                       if(ret)\r
+                               goto err;\r
+                       temp_data&=0xfdff;\r
+\r
+                       ret=i2c_write_1_addr_2_data(rda_bt_rf_client,0x22,temp_data);\r
+                       if(ret)\r
+                               goto err;\r
+               }\r
+\r
+       }\r
+\r
+out:\r
+       ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0000);\r
+       if(ret)\r
+               goto err;\r
+       return 0;\r
+\r
+err:\r
+       printk(KERN_INFO "***rda5990_bt_setup_A2_power failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_bt_power_off(void);\r
+\r
+int rda_bt_power_on(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+       printk(KERN_INFO "rda_bt_power_on \n");\r
+\r
+       if(!rda_bt_rf_client || !rda_bt_rf_client)\r
+       {\r
+               printk(KERN_INFO "rda_bt_power_on failed on:i2c client \n");\r
+               return -1;\r
+       }\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+\r
+       for(count = 0; count < sizeof(rda_5990_bt_en_data)/sizeof(rda_5990_bt_en_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_bt_rf_client, rda_5990_bt_en_data[count][0], rda_5990_bt_en_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+       ret=rda5990_bt_setup_A2_power(1);       \r
+       if(ret)\r
+       {   \r
+               printk(KERN_INFO "***rda5990_bt_setup_A2_power fail!!! \n");\r
+               goto err;\r
+       }\r
+\r
+       printk(KERN_INFO "***rda_bt_power_on success!!! \n");\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       /*houzhen update 2012 03 06*/\r
+       msleep(10);     //delay 10 ms after power on\r
+\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_bt_power_on failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+int rda_bt_power_off(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       printk(KERN_INFO "rda_bt_power_off \n");\r
+\r
+       if(!rda_bt_rf_client)\r
+       {\r
+               printk(KERN_INFO "rda_bt_power_off failed on:i2c client \n");\r
+               return -1;\r
+       }\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       for(count = 0; count < sizeof(rda_5990_bt_off_data)/sizeof(rda_5990_bt_off_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_bt_rf_client ,rda_5990_bt_off_data[count][0], rda_5990_bt_off_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+       msleep(10);   //10ms\r
+       printk(KERN_INFO "***rda_bt_power_off success!!! \n");\r
+\r
+       ret=rda5990_bt_setup_A2_power(0);//disable ldo_pa reg \r
+       if(ret)\r
+               goto err;\r
+\r
+\r
+\r
+    mutex_unlock(&i2c_rw_lock);    \r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_bt_power_off failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+\r
+int RDA5990_bt_rf_init(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       printk(KERN_INFO "RDA5990_bt_rf_init \n");\r
+\r
+       if(!rda_bt_rf_client)\r
+       {\r
+               printk(KERN_INFO "RDA5990_bt_rf_init on:i2c client \n");\r
+               return -1;\r
+       }\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       for(count = 0; count < sizeof(rda_5990_bt_rf_data)/sizeof(rda_5990_bt_rf_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_bt_rf_client, rda_5990_bt_rf_data[count][0], rda_5990_bt_rf_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_rf_init success!!! \n");\r
+       msleep(5);   //5ms\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_rf_init failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+/*houzhen add 2012 04 09\r
+  add to ensure bt dig powerup\r
+  */\r
+\r
+int RDA5990_bt_dig_reset(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+       printk(KERN_INFO "RDA5990_bt_dig_reset \n");\r
+       if(!rda_bt_rf_client)\r
+       {\r
+               printk(KERN_INFO "RDA5990_bt_dig_reset on:i2c client \n");\r
+               return -1;\r
+       }\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       for(count = 0; count < sizeof(RDA5990_bt_dig_reset_data)/sizeof(RDA5990_bt_dig_reset_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_bt_rf_client, RDA5990_bt_dig_reset_data[count][0], RDA5990_bt_dig_reset_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_rf_init success!!! \n");\r
+       msleep(5);   //5ms\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_rf_init failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+\r
+int RDA5990_bt_dc_cal(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       printk(KERN_INFO "rda_bt_dc_cal \n");\r
+\r
+       if(!rda_bt_rf_client)\r
+       {\r
+               printk(KERN_INFO "rda_bt_rf_client \n");\r
+               return -1;\r
+       }\r
+    mutex_lock(&i2c_rw_lock);\r
+\r
+       for(count = 0; count < sizeof(rda_5990_bt_dc_cal)/sizeof(rda_5990_bt_dc_cal[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_bt_rf_client, rda_5990_bt_dc_cal[count][0], rda_5990_bt_dc_cal[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_bt_dc_cal success!!! \n");\r
+       msleep(200);   //200ms\r
+\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_bt_dc_cal  failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+\r
+\r
+/*houzhen update Mar 15 2012 \r
+  bypass RDA5990_bt_set_rf_switch when wf is already on\r
+  */\r
+\r
+int RDA5990_bt_set_rf_switch(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       u16 temp_data=0;\r
+       printk(KERN_INFO "RDA5990_bt_set_rf_switch \n");\r
+\r
+       if(!rda_bt_rf_client || !rda_wifi_rf_client)\r
+       {\r
+               printk(KERN_INFO "RDA5990_bt_set_rf_switch failed on:i2c client \n");\r
+               return -1;\r
+       }\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+\r
+       ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0001);\r
+       if(ret)\r
+               goto err;       \r
+\r
+       ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x31,&temp_data);  \r
+\r
+       if(ret)\r
+               goto err;       \r
+\r
+       if(temp_data&0x8000)   // if wf is already on\r
+       {\r
+               printk(KERN_INFO "wf already en, bypass RDA5990_bt_set_rf_switch function \n");\r
+               ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0000);\r
+               if(ret)\r
+                       goto err;       \r
+        mutex_unlock(&i2c_rw_lock);    \r
+               return 0;\r
+       }\r
+\r
+       for(count = 0; count < sizeof(rda_5990_bt_set_rf_switch_data)/sizeof(rda_5990_bt_set_rf_switch_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, rda_5990_bt_set_rf_switch_data[count][0], rda_5990_bt_set_rf_switch_data[count][1]);\r
+               if(ret)\r
+                       goto err;           \r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_set_rf_switch success!!! \n");\r
+       msleep(50);   //50ms\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_set_rf_switch  failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+\r
+/*houzhen update Mar 15 2012 \r
+  bypass RDA5990_bt_enable_clk when wf is already on\r
+  */\r
+\r
+int RDA5990_bt_enable_clk(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       u16 temp_data=0;\r
+       printk(KERN_INFO "RDA5990_bt_enable_clk \n");\r
+\r
+       if(!rda_bt_rf_client)\r
+       {\r
+               printk(KERN_INFO "RDA5990_bt_enable_clk failed on:i2c client \n");\r
+               return -1;\r
+       }\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0001);\r
+       if(ret)\r
+               goto err;       \r
+\r
+       ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x31,&temp_data);  \r
+\r
+       if(ret)\r
+               goto err;       \r
+\r
+       if(temp_data&0x8000)   // if wf is already on\r
+       {\r
+               printk(KERN_INFO "wf already en, bypass RDA5990_bt_enable_clk function \n");\r
+               ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0000);\r
+               if(ret)\r
+                       goto err;       \r
+        mutex_unlock(&i2c_rw_lock);\r
+               return 0;\r
+       }\r
+\r
+\r
+       for(count = 0; count < sizeof(RDA5990_bt_enable_clk_data)/sizeof(RDA5990_bt_enable_clk_data[0]); count ++)\r
+       {\r
+               ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, RDA5990_bt_enable_clk_data[count][0], RDA5990_bt_enable_clk_data[count][1]);\r
+               if(ret)\r
+                       goto err;       \r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_enable_clk success!!! \n");\r
+       msleep(50);   //50ms\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_enable_clk  failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+//extern void mt_combo_bgf_enable_irq(void);\r
+//extern void mt_combo_bgf_disable_irq(void);\r
+\r
+#define RDA_BT_IOCTL_MAGIC 'u'\r
+#define RDA_BT_POWER_ON_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x01)\r
+#define RD_BT_RF_INIT_IOCTL   _IO(RDA_BT_IOCTL_MAGIC ,0x02)\r
+#define RD_BT_DC_CAL_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x03)\r
+#define RD_BT_SET_RF_SWITCH_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x04)\r
+#define RDA_BT_POWER_OFF_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x05)\r
+#define RDA_BT_EN_CLK _IO(RDA_BT_IOCTL_MAGIC ,0x06)\r
+#define RD_BT_DC_DIG_RESET_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x07)\r
+\r
+#define RDA_WIFI_POWER_ON_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x10)\r
+#define RDA_WIFI_POWER_OFF_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x11)\r
+#define RDA_WIFI_POWER_SET_TEST_MODE_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x12)\r
+#define RDA_WIFI_POWER_CANCEL_TEST_MODE_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x13)\r
+#define RDA_WIFI_DEBUG_MODE_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x14)\r
+\r
+extern int rk29sdk_wifi_set_carddetect(int val);\r
+static int rda_5990_pw_ioctl(struct file *file, unsigned int cmd, unsigned long arg)\r
+{\r
+       int ret = 0;\r
+\r
+       switch(cmd)\r
+       {\r
+               case RDA_WIFI_POWER_ON_IOCTL:\r
+                       rda_wifi_power_on();\r
+                       rk29sdk_wifi_set_carddetect(1);\r
+                       break;\r
+\r
+               case RDA_WIFI_POWER_OFF_IOCTL:\r
+                   rk29sdk_wifi_set_carddetect(0);\r
+                       rda_wifi_power_off();\r
+                       break;\r
+\r
+               case RDA_WIFI_POWER_SET_TEST_MODE_IOCTL:\r
+                       wifi_in_test_mode = 1;\r
+                       printk("****set rda wifi in test mode");\r
+                       break;\r
+\r
+               case RDA_WIFI_POWER_CANCEL_TEST_MODE_IOCTL:\r
+                       wifi_in_test_mode = 0;\r
+                       printk("****set rda wifi in normal mode");\r
+                       break;\r
+\r
+        case RDA_WIFI_DEBUG_MODE_IOCTL:\r
+            ret = rda_wifi_debug_en();\r
+            break;\r
+            \r
+               case RDA_BT_POWER_ON_IOCTL:\r
+                       {\r
+                               ret = rda_bt_power_on();\r
+                               //mt_combo_bgf_enable_irq();\r
+                       }\r
+                       break;\r
+\r
+                       /* should call thif function after bt_power_on*/\r
+               case RDA_BT_EN_CLK:\r
+                       ret = RDA5990_bt_enable_clk();\r
+                       break;\r
+\r
+               case RD_BT_RF_INIT_IOCTL:\r
+                       ret = RDA5990_bt_rf_init();\r
+                       break;\r
+\r
+               case RD_BT_DC_CAL_IOCTL:        \r
+                       ret = RDA5990_bt_dc_cal();\r
+                       break;\r
+\r
+               case RD_BT_DC_DIG_RESET_IOCTL:  \r
+                       ret = RDA5990_bt_dig_reset();\r
+                       break;\r
+\r
+               case RD_BT_SET_RF_SWITCH_IOCTL:\r
+                       ret = RDA5990_bt_set_rf_switch();                       \r
+                       break;\r
+\r
+               case RDA_BT_POWER_OFF_IOCTL:\r
+                       {\r
+                               //mt_combo_bgf_disable_irq();\r
+                               ret = rda_bt_power_off();               \r
+                       }\r
+                       break;\r
+\r
+               default:\r
+                       break;\r
+       }\r
+\r
+       printk(KERN_INFO "rda_bt_pw_ioctl cmd=0x%02x \n", cmd);\r
+\r
+       return ret;\r
+}      \r
+\r
+void mmc_rescan_slot(int id)\r
+{\r
+    rda_wifi_power_on();\r
+    rk29sdk_wifi_set_carddetect(1);\r
+}\r
+EXPORT_SYMBOL(mmc_rescan_slot);\r
+\r
+void mmc_remove(int id)\r
+{\r
+    rk29sdk_wifi_set_carddetect(0);\r
+                       rda_wifi_power_off();\r
+}\r
+EXPORT_SYMBOL(mmc_remove);\r
+\r
+static int rda_5990_major;\r
+static struct class *rda_5990_class = NULL;\r
+static const struct file_operations rda_5990_operations = {\r
+       .owner = THIS_MODULE,\r
+       .unlocked_ioctl = rda_5990_pw_ioctl,\r
+       .release = NULL\r
+};\r
+\r
+void rda_5990_sleep_worker_task(struct work_struct *work)\r
+{\r
+       printk("---rda_5990_sleep_worker_task end");\r
+       wake_unlock(&rda_5990_wake_lock);\r
+}\r
+\r
+void rda_5990_set_wake_lock(void)\r
+{\r
+       wake_lock(&rda_5990_wake_lock);\r
+       cancel_delayed_work(&rda_5990_sleep_worker);\r
+       schedule_delayed_work(&rda_5990_sleep_worker, 6*HZ);\r
+}\r
+\r
+int rda_5990_power_ctrl_init(void)\r
+{\r
+       int ret = 0;\r
+       printk("rda_5990_power_ctrl_init begin\n");\r
+\r
+       /*i2c_register_board_info(0, &i2c_rda_wifi_core, 1);\r
+       if (i2c_add_driver(&rda_wifi_core_driver))\r
+       {\r
+               printk("rda_wifi_core_driver failed!\n");\r
+               ret = -ENODEV;\r
+               return ret;\r
+       }\r
+       */\r
+\r
+       //i2c_register_board_info(0, &i2c_rda_wifi_rf, 1);\r
+       if (i2c_add_driver(&rda_wifi_rf_driver))\r
+       {\r
+               printk("rda_wifi_rf_driver failed!\n");\r
+               ret = -ENODEV;\r
+               return ret;\r
+       }\r
+       \r
+       /*\r
+       i2c_register_board_info(0, &i2c_rda_bt_core, 1);\r
+       if (i2c_add_driver(&rda_bt_core_driver))\r
+       {\r
+               printk("rda_bt_core_driver failed!\n");\r
+               ret = -ENODEV;\r
+               return ret;\r
+       }\r
+       */\r
+\r
+       //i2c_register_board_info(0, &i2c_rda_bt_rf, 1);\r
+       if (i2c_add_driver(&rda_bt_rf_driver))\r
+       {\r
+               printk("rda_bt_rf_driver failed!\n");\r
+               ret = -ENODEV;\r
+               return ret;\r
+       }\r
+\r
+       rda_5990_major = register_chrdev(0, "rda5990_power_ctrl", &rda_5990_operations);\r
+       if(rda_5990_major < 0)\r
+       {\r
+               printk(KERN_INFO "register rdabt_power_ctrl failed!!! \n");\r
+               return rda_5990_major;\r
+       }\r
+\r
+       rda_5990_class = class_create(THIS_MODULE, "rda_combo");\r
+       if(IS_ERR(rda_5990_class))\r
+       {\r
+               unregister_chrdev(rda_5990_major, "rdabt_power_ctrl");\r
+               return PTR_ERR(rda_5990_class);\r
+       }\r
+\r
+       device_create(rda_5990_class, NULL, MKDEV(rda_5990_major, 0), NULL, "rdacombo");\r
+\r
+       {\r
+               unsigned char*  temp = NULL;\r
+               unsigned short testData = 0xffee;\r
+               temp = (unsigned char *)&testData;\r
+               if(*temp == 0xee)\r
+                       isBigEnded = 0;\r
+               else\r
+                       isBigEnded = 1;\r
+       }\r
+\r
+       INIT_DELAYED_WORK(&rda_5990_sleep_worker, rda_5990_sleep_worker_task);\r
+       wake_lock_init(&rda_5990_wake_lock, WAKE_LOCK_SUSPEND, "RDA_sleep_worker_wake_lock");\r
+\r
+    mutex_init(&i2c_rw_lock);    \r
+       printk("rda_5990_power_ctrl_init end\n");\r
+       return 0;\r
+}\r
+\r
+void rda_5990_power_ctrl_exit(void)\r
+{\r
+       i2c_del_driver(&rda_wifi_core_driver);\r
+       i2c_del_driver(&rda_wifi_rf_driver);\r
+       i2c_del_driver(&rda_bt_core_driver);\r
+       i2c_del_driver(&rda_bt_rf_driver);\r
+\r
+       unregister_chrdev(rda_5990_major, "rdabt_power_ctrl");\r
+       if(rda_5990_class)\r
+               class_destroy(rda_5990_class);       \r
+\r
+       cancel_delayed_work_sync(&rda_5990_sleep_worker);\r
+       wake_lock_destroy(&rda_5990_wake_lock);\r
+}\r
+\r
+unsigned char rda_5990_wifi_in_test_mode(void)\r
+{\r
+       return wifi_in_test_mode;\r
+}\r
+\r
+\r
+static __inline__ void cfmakeraw(struct termios *s)\r
+{\r
+       s->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);\r
+       s->c_oflag &= ~OPOST;\r
+       s->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);\r
+       s->c_cflag &= ~(CSIZE|PARENB);\r
+       s->c_cflag |= CS8;\r
+}\r
+\r
+static __inline__ int cfsetospeed(struct termios *s, speed_t  speed)\r
+{\r
+       s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD);\r
+       return 0;\r
+}\r
+\r
+static __inline__ int cfsetispeed(struct termios *s, speed_t  speed)\r
+{\r
+       s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD);\r
+       return 0;\r
+}\r
+\r
+static int rda_wifi_init_uart(char *dev)\r
+{\r
+       int errno;\r
+       struct termios ti;\r
+       struct serial_struct ss;\r
+       int fd;\r
+\r
+       fd = sys_open(dev, O_RDWR | O_NOCTTY, 0);\r
+       if (fd < 0) {\r
+               printk("Can't open serial port");\r
+               return -1;\r
+       }\r
+\r
+       sys_ioctl(fd, TCFLSH, TCIOFLUSH);\r
+\r
+       /* Clear the cust flag */\r
+       if((errno = sys_ioctl(fd, TIOCGSERIAL, &ss))<0){\r
+               printk("BAUD: error to get the serial_struct info:%s\n", errno);\r
+               goto err;\r
+       }\r
+\r
+       if (ss.flags & ASYNC_SPD_CUST) {\r
+               printk("clear ASYNC_SPD_CUST\r\n");\r
+               ss.flags &= ~ASYNC_SPD_CUST;\r
+       }\r
+       if((errno = sys_ioctl(fd, TIOCSSERIAL, &ss))<0){\r
+               printk("BAUD: error to set serial_struct:%s\n", errno);\r
+               goto err;\r
+       }\r
+\r
+       if ((errno = sys_ioctl(fd, TCGETS, (long)&ti))  < 0) {\r
+               printk("unable to get UART port setting");\r
+               printk("Can't get port settings");\r
+               goto err;\r
+       }\r
+\r
+       cfmakeraw(&ti);\r
+\r
+       ti.c_cflag |= CLOCAL;\r
+       ti.c_cflag &= ~CRTSCTS;\r
+       ti.c_lflag = 0;\r
+       ti.c_cc[VTIME]    = 5; /* 0.5 sec */\r
+       ti.c_cc[VMIN]     = 0;\r
+\r
+       /* Set initial baudrate */\r
+       cfsetospeed(&ti, B115200);\r
+       cfsetispeed(&ti, B115200);\r
+\r
+       if ((errno = sys_ioctl(fd, TCSETS, (long)&ti)) < 0) {\r
+               printk("unable to set UART port setting");\r
+               printk("Can't set port settings");\r
+               goto err;\r
+       }\r
+\r
+       errno = sys_ioctl(fd, TCFLSH, TCIOFLUSH);\r
+       if(errno < 0)\r
+               goto err;\r
+\r
+       return fd;\r
+\r
+err:\r
+       if(fd > 0)\r
+               sys_close(fd);\r
+\r
+       return -1;\r
+}  \r
+\r
+EXPORT_SYMBOL(rda_wlan_version);\r
+\r
+EXPORT_SYMBOL(rda_wifi_init_uart);\r
+EXPORT_SYMBOL(rda_5990_wifi_in_test_mode);\r
+\r
+EXPORT_SYMBOL(rda_5990_set_wake_lock);\r
+EXPORT_SYMBOL(rda_wifi_power_off);\r
+EXPORT_SYMBOL(rda_wifi_power_on);\r
+EXPORT_SYMBOL(rda_fm_power_on);\r
+EXPORT_SYMBOL(rda_fm_power_off);\r
+\r
+module_init(rda_5990_power_ctrl_init);\r
+module_exit(rda_5990_power_ctrl_exit);\r
+\r
+MODULE_LICENSE("GPL");\r
+\r
diff --git a/drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl_by_gpio.c b/drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl_by_gpio.c
new file mode 100755 (executable)
index 0000000..8f508c1
--- /dev/null
@@ -0,0 +1,1462 @@
+/* ----------------------------------------------------------------------- *\r
+ *\r
+ This file created by albert RDA Inc\r
+ */\r
+\r
+#include <linux/module.h>\r
+#include <linux/init.h>\r
+#include <linux/slab.h>\r
+#include <linux/i2c.h>\r
+#include <linux/string.h>\r
+#include <linux/rtc.h>       /* get the user-level API */\r
+#include <linux/bcd.h>\r
+#include <linux/list.h>\r
+#include <linux/delay.h>\r
+\r
+\r
+#include <linux/nfs_fs.h>\r
+#include <linux/nfs_fs_sb.h>\r
+#include <linux/nfs_mount.h>\r
+#include <linux/fs.h>\r
+#include <linux/file.h>\r
+#include <linux/tty.h>\r
+#include <linux/syscalls.h>\r
+#include <asm/termbits.h>\r
+#include <linux/serial.h>\r
+\r
+\r
+#include <mach/mt6575_gpio.h>\r
+#include <mach/mtk_rtc.h>\r
+#include <linux/wakelock.h>\r
+\r
+#define RDA5890_USE_CRYSTAL  //if use share crystal should close this\r
+#define RDA5990_USE_DCDC\r
+\r
+#define u32 unsigned int\r
+#define u8 unsigned char\r
+#define u16 unsigned short\r
+\r
+extern int rda_gpio_i2c_read_1_addr_2_data(uint8_t chipAddr, uint8_t regAddr, uint16_t *buffer);\r
+extern int rda_gpio_i2c_write_1_addr_2_data(uint8_t chipAddr, uint8_t regAddr, uint16_t data);\r
+extern int rda_gpio_i2c_read_4_addr_4_data(uint8_t chipAddr, uint32_t regAddr, uint32_t *buffer);\r
+extern int rda_gpio_i2c_write_4_addr_4_data(uint8_t chipAddr, uint32_t regAddr, uint32_t data);\r
+\r
+\r
+//#define RDA_I2C_CHANNEL              (0)\r
+#define RDA_WIFI_CORE_ADDR (0x13)\r
+#define RDA_WIFI_RF_ADDR (0x14) //correct add is 0x14\r
+#define RDA_BT_CORE_ADDR (0x15)\r
+#define RDA_BT_RF_ADDR (0x16)\r
+\r
+static struct mutex i2c_rw_lock;\r
+static unsigned short wlan_version = 0;\r
+static struct wake_lock rda_5990_wake_lock;\r
+static struct delayed_work   rda_5990_sleep_worker;\r
+\r
+static u8 isBigEnded = 0;\r
+\r
+static u8 wifi_in_test_mode = 0;\r
+\r
+const u32 wifi_core_init_data[][2] = \r
+{\r
+\r
+};\r
+\r
+u16 wifi_off_data[][2] = \r
+{\r
+       { 0x3F, 0x0001 }, //page up\r
+       { 0x31, 0x0B40 }, //power off wifi\r
+       { 0x3F, 0x0000 }, //page down\r
+};\r
+\r
+u16 wifi_en_data[][2] = \r
+{\r
+    //item:VerD_wf_on_2012_02_08\r
+    {0x3f, 0x0001},\r
+#ifdef RDA5990_USE_DCDC     /*houzhen update Mar 15 2012 */\r
+    {0x23, 0x8FA1},//20111001 higher AVDD voltage to improve EVM to 0x8f21 download current -1db 0x8fA1>>0x8bA1   \r
+#else\r
+       {0x23, 0x0FA1},\r
+#endif\r
+    {0x31, 0x0B40 }, //power off wifi\r
+//    {0x22, 0xD3C7},//for ver.c 20111109, txswitch\r
+    {0x24, 0x80C8},//freq_osc_in[1:0]00  0x80C8 >> 0x80CB\r
+    {0x27, 0x4925},//for ver.c20111109, txswitch\r
+    //                {0x28, 0x80A1}, //BT_enable \r
+    {0x31, 0x8140},//enable wifi  \r
+    {0x32, 0x0113},//set_ rdenout_ldooff_wf=0; rden4in_ldoon_wf=1                                              \r
+    //                {0x39, 0x0004},  //uart switch to wf  \r
+    {0x3F, 0x0000}, //page down\r
+};\r
+\r
+\r
+u16 wifi_dc_cal_data[][2]=\r
+{\r
+       {0x3f, 0x0000},\r
+       {0x30, 0x0248},\r
+       {0x30, 0x0249},\r
+       //{wait 200ms; } here\r
+};\r
+\r
+u16 wifi_dig_reset_data[][2]=\r
+{\r
+       {0x3F,  0x0001},\r
+       {0x31,  0x8D40},\r
+       {0x31,  0x8F40},\r
+       {0x31,  0x8b40},\r
+       {0x3F,  0x0000},\r
+};\r
+\r
+u16 wifi_rf_init_data_verE[][2] = \r
+{\r
+       {0x3f, 0x0000},\r
+       //{;;set_rf_swi},ch\r
+       {0x06, 0x0101},\r
+       {0x07, 0x0101},\r
+       {0x08, 0x0101},\r
+       {0x09, 0x0101},\r
+       {0x0A, 0x002C},//aain_0\r
+       {0x0D, 0x0507},\r
+       {0x0E, 0x2300},\r
+       {0x0F, 0x5689},//\r
+       //{;;//set_RF  },\r
+       {0x10, 0x0f78},//20110824\r
+       {0x11, 0x0602},\r
+       {0x13, 0x0652},//adc_tuning_bit[011]\r
+       {0x14, 0x8886},\r
+       {0x15, 0x0990},\r
+       {0x16, 0x049f},\r
+       {0x17, 0x0990},\r
+       {0x18, 0x049F},\r
+       {0x19, 0x3C01},\r
+       {0x1C, 0x0934},\r
+       {0x1D, 0xFF00},//for ver.D20120119for temperature 70 degree\r
+       //{0x1F, 0x01F8},//for ver.E should not set\r
+       //{0x1F, 0x0300},//for ver.E should not set\r
+       {0x20, 0x06E4},\r
+       {0x21, 0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset\r
+       {0x22, 0x24DC},\r
+       {0x23, 0x23FF},\r
+       {0x24, 0x00FC},\r
+       {0x26, 0x004F},//004F >> 005f premote pa \r
+       {0x27, 0x171D},///mdll*7\r
+       {0x28, 0x031D},///mdll*7\r
+       {0x2A, 0x2860},//et0x2849-8.5p  :yd 0x2861-7pf C1,C2=6.8p\r
+       {0x2B, 0x0800},//bbpll,or ver.c20111116\r
+       {0x32, 0x8a08},\r
+       {0x33, 0x1D02},//liuyanan\r
+       //{;;//agc_gain},\r
+       {0x36, 0x02f4}, //00F8;//gain_7\r
+       {0x37, 0x01f4}, //0074;//aain_6\r
+       {0x38, 0x21d4}, //0014;//gain_5\r
+       {0x39, 0x25d4}, //0414;//aain_4\r
+       {0x3A, 0x2584}, //1804;//gain_3\r
+       {0x3B, 0x2dc4}, //1C04;//aain_2\r
+       {0x3C, 0x2d04}, //1C02;//gain_1\r
+       {0x3D, 0x2c02}, //3C01;//gain_0\r
+       {0x33, 0x1502},//liuyanan\r
+       //{;;SET_channe},_to_11\r
+       {0x1B, 0x0001},//set_channel   \r
+       {0x30, 0x024D},\r
+       {0x29, 0xD468},\r
+       {0x29, 0x1468},\r
+       {0x30, 0x0249},\r
+       {0x3f, 0x0000},\r
+};\r
+\r
+u16 wifi_rf_init_data[][2] = \r
+{\r
+       {0x3f, 0x0000},\r
+       //{;;set_rf_swi},ch\r
+       {0x06, 0x0101},\r
+       {0x07, 0x0101},\r
+       {0x08, 0x0101},\r
+       {0x09, 0x0101},\r
+       {0x0A, 0x002C},//aain_0\r
+       {0x0D, 0x0507},\r
+       {0x0E, 0x2300},\r
+       {0x0F, 0x5689},//\r
+       //{;;//set_RF  },\r
+       {0x10, 0x0f78},//20110824\r
+       {0x11, 0x0602},\r
+       {0x13, 0x0652},//adc_tuning_bit[011]\r
+       {0x14, 0x8886},\r
+       {0x15, 0x0990},\r
+       {0x16, 0x049f},\r
+       {0x17, 0x0990},\r
+       {0x18, 0x049F},\r
+       {0x19, 0x3C01},\r
+       {0x1C, 0x0934},\r
+       {0x1D, 0xFF00},//for ver.D20120119for temperature 70 degree\r
+       //{0x1F, 0x01F8},//for ver.c20111109\r
+       {0x1F, 0x0300},//for burst tx 不锁\r
+       {0x20, 0x06E4},\r
+       {0x21, 0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset\r
+       {0x22, 0x24DC},\r
+       {0x23, 0x23FF},\r
+       {0x24, 0x00FC},\r
+       {0x26, 0x004F},//004F >> 005f premote pa \r
+       {0x27, 0x171D},///mdll*7\r
+       {0x28, 0x031D},///mdll*7\r
+       {0x2A, 0x2860},//et0x2849-8.5p  :yd 0x2861-7pf C1,C2=6.8p\r
+       {0x2B, 0x0800},//bbpll,or ver.c20111116\r
+       {0x32, 0x8a08},\r
+       {0x33, 0x1D02},//liuyanan\r
+       //{;;//agc_gain},\r
+       {0x36, 0x02f4}, //00F8;//gain_7\r
+       {0x37, 0x01f4}, //0074;//aain_6\r
+       {0x38, 0x21d4}, //0014;//gain_5\r
+       {0x39, 0x25d4}, //0414;//aain_4\r
+       {0x3A, 0x2584}, //1804;//gain_3\r
+       {0x3B, 0x2dc4}, //1C04;//aain_2\r
+       {0x3C, 0x2d04}, //1C02;//gain_1\r
+       {0x3D, 0x2c02}, //3C01;//gain_0\r
+       {0x33, 0x1502},//liuyanan\r
+       //{;;SET_channe},_to_11\r
+       {0x1B, 0x0001},//set_channel   \r
+       {0x30, 0x024D},\r
+       {0x29, 0xD468},\r
+       {0x29, 0x1468},\r
+       {0x30, 0x0249},\r
+       {0x3f, 0x0000},\r
+};\r
+\r
+u16 wifi_uart_debug_data[][2] = \r
+{\r
+  {0x3F,0x0001},\r
+  {0x28,0x80A1}, //BT_enable \r
+  {0x39,0x0004}, //uart switch to wf\r
+  {0x3f,0x0000},\r
+};\r
+\r
+u16 wifi_tm_en_data[][2] =\r
+{\r
+    {0x3F,0x0001},\r
+#ifdef RDA5990_USE_DCDC     /*houzhen update Mar 15 2012 */\r
+    {0x23, 0x8FA1},//20111001 higher AVDD voltage to improve EVM to 0x8f21 download current -1db 0x8fA1>>0x8bA1   \r
+#else\r
+       {0x23, 0x0FA1},\r
+#endif\r
+    {0x22,0xD3C7},//for ver.c 20111109, tx\r
+       {0x24, 0x80C8},//freq_osc_in[1:0]00  0x80C8 >> 0x80CB \r
+       {0x27,0x4925},//for ver.c20111109, txs\r
+       {0x28,0x80A1}, //BT_enable            \r
+       {0x29,0x111F},                        \r
+       {0x31,0x8140},                        \r
+       {0x32,0x0113},//set_ rdenout_ldooff_wf\r
+       {0x39,0x0004},//uart switch to wf\r
+       {0x3f,0x0000},\r
+};\r
+\r
+u16 wifi_tm_rf_init_data[][2] = \r
+{\r
+       {0x3f, 0x0000},\r
+       //set_rf_switch                                                  \r
+       {0x06,0x0101},                                                     \r
+       {0x07,0x0101},                                                     \r
+       {0x08,0x0101},                                                     \r
+       {0x09,0x0101},                                                     \r
+       {0x0A,0x002C},//aain_0\r
+       {0x0D,0x0507},                                             \r
+       {0x0E,0x2300},//2012_02_20                                         \r
+       {0x0F,0x5689},//                                                   \r
+       //set_RF                                                            \r
+       {0x10,0x0f78},//20110824                                             \r
+       {0x11,0x0602},                                                     \r
+       {0x13,0x0652},//adc_tuning_bit[011]                               \r
+       {0x14,0x8886},                                                     \r
+       {0x15,0x0990},                                                     \r
+       {0x16,0x049f},                                                     \r
+       {0x17,0x0990},                                                     \r
+       {0x18,0x049F},                                                     \r
+       {0x19,0x3C01},//sdm_vbit[3:0]=1111                                 \r
+       {0x1C,0x0934},                                                     \r
+       {0x1D,0xCE00},//for ver.D20120119for temperature 70 degree         \r
+       {0x1F,0x0300},//div2_band_48g_dr=1;div2_band_48g_reg[8:0]1000000000\r
+       {0x20,0x06E4},                                                     \r
+       {0x21,0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset      \r
+       {0x22,0x24DC},                                                     \r
+       {0x23,0x23FF},                                                     \r
+       {0x24,0x00FC},                                                     \r
+       {0x26,0x004F},                                                     \r
+       {0x27,0x171D},///mdll*7                                            \r
+       {0x28,0x031D},///mdll*7                                            \r
+       {0x2A,0x2860},                                                     \r
+       {0x2B,0x0800},//bbpll,or ver.c20111116                             \r
+       {0x32,0x8a08},                                                     \r
+       {0x33,0x1D02},//liuyanan                                           \r
+       //agc_gain                                                          \r
+       {0x36,0x02f4}, //00F8;//gain_7                                     \r
+       {0x37,0x01f4}, //0074;//aain_6                                     \r
+       {0x38,0x21d4}, //0014;//gain_5                                     \r
+       {0x39,0x25d4}, //0414;//aain_4                                     \r
+       {0x3A,0x2584}, //1804;//gain_3                                     \r
+       {0x3B,0x2dc4}, //1C04;//aain_2                                     \r
+       {0x3C,0x2d04}, //1C02;//gain_1                                     \r
+       {0x3D,0x2c02}, //3C01;//gain_0                                     \r
+       //DC_CAL                                                            \r
+       {0x30,0x0248},                                                     \r
+       {0x30,0x0249},                                                     \r
+       //wait 200ms;                                                       \r
+       {0x33,0x1502},//liuyanan                                           \r
+       //SET_channel_to_11                                                 \r
+       {0x1B,0x0001},//set_channel     \r
+       {0x3f,0x0000},\r
+};\r
+\r
+/*houzhen update Mar 15 2012\r
+  should be called when power up/down bt\r
+  */\r
+static int rda5990_wf_setup_A2_power(int enable)\r
+{\r
+       int ret;\r
+       u16 temp_data=0;\r
+       printk("***rda5990_wf_setup_A2_power start! \n");\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001);\r
+       if(ret)\r
+               goto err;\r
+\r
+       if(enable)\r
+       {\r
+               ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, &temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               printk("***0xA2 readback value:0x%X \n", temp_data);\r
+\r
+               temp_data |=0x0200;   /*en reg4_pa bit*/\r
+#ifdef RDA5890_USE_CRYSTAL     \r
+               temp_data &= ~(1 << 14); //disable xen_out\r
+#endif\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               //read wlan version\r
+               ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x21, &temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               else\r
+                       wlan_version = temp_data;\r
+       }\r
+       else\r
+       {\r
+               ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x28, &temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               if(temp_data&0x8000)        // bt is on \r
+               {\r
+                       goto out;\r
+               }\r
+               else\r
+               {\r
+                       ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, &temp_data);\r
+                       if(ret)\r
+                               goto err;\r
+                       temp_data&=0xfdff;\r
+\r
+                       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, temp_data);\r
+                       if(ret)\r
+                               goto err;\r
+               }\r
+               wlan_version = 0;\r
+       }\r
+       printk("rda5990_wf_setup_A2_power, version:%d", wlan_version);\r
+\r
+out:\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000);\r
+       if(ret)\r
+               goto err;\r
+       printk("***rda5990_wf_setup_A2_power succeed! \n");\r
+       return 0;\r
+\r
+err:\r
+       printk("***rda5990_wf_setup_A2_power failed! \n");\r
+       return -1;\r
+}\r
+\r
+\r
+int rda_wifi_rf_init(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       mutex_lock(&i2c_rw_lock);\r
+       if( (wlan_version&0x1f) == 7)\r
+       {\r
+               for(count = 0; count < sizeof(wifi_rf_init_data)/sizeof(wifi_rf_init_data[0]); count ++)\r
+               {\r
+                       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_rf_init_data[count][0], wifi_rf_init_data[count][1]);\r
+                       if(ret)\r
+                               goto err;\r
+               }\r
+       }\r
+       else if((wlan_version&0x1f) == 4 || (wlan_version&0x1f)==5)\r
+       {\r
+               for(count = 0; count < sizeof(wifi_rf_init_data_verE)/sizeof(wifi_rf_init_data_verE[0]); count ++)\r
+               {\r
+                       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_rf_init_data_verE[count][0], wifi_rf_init_data_verE[count][1]);\r
+                       if(ret)\r
+                               goto err;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               printk("unknown version of this 5990 chip\n");\r
+               goto err;\r
+       }\r
+       mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_rf_init_succceed \n");\r
+       msleep(5);   //5ms delay\r
+       return 0;\r
+err:\r
+       mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_rf_init failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_wifi_dc_cal(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       for(count = 0; count < sizeof(wifi_dc_cal_data)/sizeof(wifi_dc_cal_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_dc_cal_data[count][0], wifi_dc_cal_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_rf_dc_calsuccceed \n");\r
+       msleep(50);   //50ms delay\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_rf_dc_calf_failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_wifi_en(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+    mutex_lock(&i2c_rw_lock); \r
+       for(count = 0; count < sizeof(wifi_en_data)/sizeof(wifi_en_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_en_data[count][0], wifi_en_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+\r
+               if(wifi_en_data[count][0] == 0x31)\r
+                       msleep(12);\r
+       }\r
+\r
+       ret=rda5990_wf_setup_A2_power(1);       //en pa_reg for wf\r
+       if(ret)\r
+               goto err;\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       msleep(8);   //8ms delay\r
+\r
+       printk(KERN_INFO "***rda_wifi_en_succceed \n");\r
+       return 0;\r
+err:\r
+     mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_power_on failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_wifi_debug_en(void)\r
+{\r
+    unsigned int count = 0;\r
+    int ret = 0;\r
+    \r
+    mutex_lock(&i2c_rw_lock); \r
+               for(count = 0; count < sizeof(wifi_uart_debug_data)/sizeof(wifi_uart_debug_data[0]); count ++)\r
+               {\r
+             ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_uart_debug_data[count][0], wifi_uart_debug_data[count][1]);\r
+             if(ret)\r
+                 goto err;\r
+               }\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+    return ret;\r
+}\r
+\r
+int rda_tm_wifi_en(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       unsigned short temp_data;\r
+\r
+       for(count = 0; count < sizeof(wifi_tm_en_data)/sizeof(wifi_tm_en_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_tm_en_data[count][0], wifi_tm_en_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+       msleep(8);   //8ms delay\r
+\r
+    ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001); //PAGE UP\r
+    if(ret)\r
+        goto err;\r
\r
+    ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR,0x21,&temp_data);\r
+    if(ret)\r
+        goto err;\r
+    else\r
+        wlan_version = temp_data;\r
+\r
+    ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000); //PAGE DOWN\r
+    if(ret)\r
+        goto err;\r
+\r
+       printk(KERN_INFO "***rda_wifi_en_succceed \n");\r
+       return 0;\r
+err:\r
+       printk(KERN_INFO "***rda_wifi_power_on failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_tm_wifi_rf_init(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+       for(count = 0; count < sizeof(wifi_tm_rf_init_data)/sizeof(wifi_tm_rf_init_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_tm_rf_init_data[count][0], wifi_tm_rf_init_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+       printk(KERN_INFO "***rda_wifi_rf_init_succceed \n");\r
+       msleep(5);   //5ms delay\r
+       return 0;\r
+\r
+err:\r
+       printk(KERN_INFO "***rda_wifi_rf_init failed! \n");\r
+       return -1;\r
+}\r
+/*houzhen add 2012 04 09\r
+  add to ensure wf dig powerup\r
+  */\r
+\r
+int rda_wifi_dig_reset(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       msleep(8);   //8ms delay\r
+    mutex_lock(&i2c_rw_lock);\r
+\r
+       for(count = 0; count < sizeof(wifi_dig_reset_data)/sizeof(wifi_dig_reset_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_dig_reset_data[count][0], wifi_dig_reset_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       msleep(8);   //8ms delay\r
+       printk(KERN_INFO "***rda_wifi_dig_reset \n");\r
+       return 0;\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_dig_reset failed! \n"); \r
+       return -1;\r
+}\r
+\r
+int rda_wlan_version(void)\r
+{\r
+       printk("******version %x \n", wlan_version);\r
+       return wlan_version;\r
+}\r
+\r
+int rda_wifi_power_off(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       u16 temp=0x0000;\r
+       printk(KERN_INFO "rda_wifi_power_off \n");\r
+\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       ret=rda5990_wf_setup_A2_power(0);   //disable pa_reg for wf\r
+       if(ret)\r
+               goto err;\r
+\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001);   //page up\r
+       if(ret)\r
+               goto err;\r
+\r
+       ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x28, &temp);   //poll bt status\r
+       if(ret)\r
+               goto err;\r
+\r
+       if(temp&0x8000)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000);   //page down\r
+               if(ret)\r
+                       goto err;\r
+\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x0f, 0x2223);   // set antenna for bt\r
+               if(ret)\r
+                       goto err;\r
+\r
+       }\r
+\r
+\r
+       for(count = 0; count < sizeof(wifi_off_data)/sizeof(wifi_off_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_off_data[count][0], wifi_off_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+       printk(KERN_INFO "***rda_wifi_power_off success!!! \n");\r
+\r
+\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_wifi_power_off failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+int rda_wifi_power_on(void)\r
+{\r
+       int ret;\r
+       char retry = 3;\r
+\r
+\r
+_retry:\r
+\r
+       if(!wifi_in_test_mode)\r
+       {\r
+               ret = rda_wifi_en();    \r
+               if(ret < 0)\r
+                       goto err;\r
+\r
+               ret = rda_wifi_rf_init();       \r
+               if(ret < 0)\r
+                       goto err;\r
+\r
+               ret = rda_wifi_dc_cal();        \r
+               if(ret < 0)\r
+                       goto err;\r
+\r
+               msleep(20);   //20ms delay\r
+               ret=rda_wifi_dig_reset();   //houzhen add to ensure wf power up safely\r
+\r
+               if(ret < 0)\r
+                       goto err;\r
+               msleep(20);   //20ms delay\r
+       }\r
+       else\r
+       {\r
+               ret = rda_tm_wifi_en();\r
+               if(ret < 0)\r
+                       goto err;\r
+\r
+               ret = rda_tm_wifi_rf_init();\r
+               if(ret < 0)\r
+                       goto err;\r
+       }\r
+       printk(KERN_INFO "rda_wifi_power_on_succeed!! \n");\r
+\r
+       return 0;\r
+\r
+err:\r
+       printk(KERN_INFO "rda_wifi_power_on_failed retry:%d \n", retry);\r
+       if(retry -- > 0)\r
+       {   \r
+               rda_wifi_power_off();\r
+               goto _retry;\r
+       }\r
+\r
+       return -1;\r
+}\r
+\r
+int rda_fm_power_on(void)\r
+{\r
+       int ret = 0;\r
+       u16 temp = 0;\r
+\r
+\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001);   // page down\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+       ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, &temp);           //read 0xA2\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() read from address(0x%02x) failed! \n", __func__, 0x22);\r
+               return -1;\r
+       }\r
+       temp = temp & (~(1 << 15));             //clear bit[15]\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, temp);           //write back\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000);   // page up\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+int rda_fm_power_off(void)\r
+{\r
+       int ret = 0;\r
+       u16 temp = 0;\r
+\r
+\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001);   // page down\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+       ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, &temp);           //read 0xA2\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() read from address(0x%02x) failed! \n", __func__, 0x22);\r
+               return -1;\r
+       }\r
+       temp = temp | (1 << 15);                //set bit[15]\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, temp);           //write back\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000);   // page up\r
+       if(ret < 0){\r
+               printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001);\r
+               return -1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+u16 rda_5990_bt_off_data[][2] = \r
+{\r
+       {0x3f, 0x0001 }, //pageup\r
+       {0x28, 0x00A1 }, //power off bt\r
+       {0x3f, 0x0000 }, //pagedown\r
+};\r
+\r
+/*houzhen update 2012 03 06*/\r
+u16 rda_5990_bt_en_data[][2] = \r
+{\r
+    {0x3f, 0x0001 },           //pageup\r
+#ifdef RDA5990_USE_DCDC    \r
+    {0x23, 0x8FA1},              // //20111001 higher AVDD voltage to improve EVM\r
+#else\r
+       {0x23, 0x0FA1},\r
+#endif \r
+       {0x24, 0x80C8},           // ;//freq_osc_in[1:0]00      \r
+       {0x26, 0x47A5},           //  reg_vbit_normal_bt[2:0] =111\r
+       {0x27, 0x4925},           // //for ver.c20111109, txswitch\r
+       {0x29, 0x111F},           // // rden4in_ldoon_bt=1      \r
+       {0x32, 0x0111},           // set_ rdenout_ldooff_wf=0;                                           \r
+       {0x39, 0x0000},           //      //uart switch to bt\r
+\r
+       {0x28, 0x80A1},         // bt en\r
+       {0x3f, 0x0000},         //pagedown\r
+};\r
+\r
+\r
+u16 rda_5990_bt_dc_cal[][2] = \r
+{\r
+       {0x3f, 0x0000 }, \r
+       {0x30, 0x0129 },\r
+       {0x30, 0x012B },\r
+       {0x3f, 0x0000 }, \r
+};\r
+\r
+\r
+u16 rda_5990_bt_set_rf_switch_data[][2] = \r
+{\r
+       {0x3f, 0x0000 }, \r
+       {0x0F, 0x2223 },\r
+       {0x3f, 0x0000 }, \r
+};\r
+\r
+\r
+u16 RDA5990_bt_enable_clk_data[][2] = \r
+{\r
+       {0x3f, 0x0000 }, \r
+       {0x30, 0x0040 },\r
+       {0x2a, 0x285d },\r
+       {0x3f, 0x0000 }, \r
+};\r
+\r
+u16 RDA5990_bt_dig_reset_data[][2] = \r
+{\r
+       {0x3f, 0x0001 }, //pageup\r
+       {0x28, 0x86A1 },\r
+       {0x28, 0x87A1 },\r
+       {0x28, 0x85A1 },\r
+       {0x3f, 0x0000 }, //pagedown\r
+};\r
+\r
+/*houzhen update 2012 03 06*/\r
+u16 rda_5990_bt_rf_data[][2] = \r
+{\r
+       {0x3f, 0x0000}, //pagedown\r
+       {0x01, 0x1FFF},\r
+       {0x06, 0x07F7},\r
+       {0x08, 0x29E7},\r
+       {0x09, 0x0520},\r
+       {0x0B, 0x03DF},\r
+       {0x0C, 0x85E8},\r
+       {0x0F, 0x0DBC},\r
+       {0x12, 0x07F7},\r
+       {0x13, 0x0327},\r
+       {0x14, 0x0CCC},\r
+       {0x15, 0x0526},\r
+       {0x16, 0x8918},\r
+       {0x18, 0x8800},\r
+       {0x19, 0x10C8},\r
+       {0x1A, 0x9078},\r
+       {0x1B, 0x80E2},\r
+       {0x1C, 0x361F},\r
+       {0x1D, 0x4363},\r
+       {0x1E, 0x303F},\r
+       {0x23, 0x2222},\r
+       {0x24, 0x359D},\r
+       {0x27, 0x0011},\r
+       {0x28, 0x124F},\r
+       {0x39, 0xA5FC},\r
+       {0x3f, 0x0001}, //page 1\r
+       {0x00, 0x043F},\r
+       {0x01, 0x467F},\r
+       {0x02, 0x28FF},\r
+       {0x03, 0x67FF},\r
+       {0x04, 0x57FF},\r
+       {0x05, 0x7BFF},\r
+       {0x06, 0x3FFF},\r
+       {0x07, 0x7FFF},\r
+       {0x18, 0xF3F5},\r
+       {0x19, 0xF3F5},\r
+       {0x1A, 0xE7F3},\r
+       {0x1B, 0xF1FF},\r
+       {0x1C, 0xFFFF},\r
+       {0x1D, 0xFFFF},\r
+       {0x1E, 0xFFFF},\r
+       {0x1F, 0xFFFF},\r
+       //      {0x22, 0xD3C7}, \r
+       //      {0x23, 0x8fa1},\r
+       //      {0x24, 0x80c8},\r
+       //      {0x26, 0x47A5},\r
+       //      {0x27, 0x4925},\r
+       //      {0x28, 0x85a1},\r
+       //      {0x29, 0x111f},\r
+       //      {0x32, 0x0111},\r
+       //      {0x39, 0x0000},\r
+       {0x3f, 0x0000}, //pagedown\r
+};\r
+\r
+/*houzhen update Mar 15 2012\r
+  should be called when power up/down bt\r
+  */\r
+static int rda5990_bt_setup_A2_power(int enable)\r
+{\r
+       int ret;\r
+       u16 temp_data=0;\r
+\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0001);\r
+       if(ret)\r
+               goto err;\r
+\r
+       if(enable)\r
+       {\r
+               ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x22, &temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               printk(KERN_INFO "***0xA2 readback value:0x%X \n", temp_data);\r
+\r
+               temp_data |=0x0200;   /*en reg4_pa bit*/\r
+\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x22, temp_data);\r
+               if(ret)\r
+                       goto err;               \r
+       }\r
+       else\r
+       {\r
+               ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x31, &temp_data);\r
+               if(ret)\r
+                       goto err;\r
+               if(temp_data&0x8000)        // wf is on \r
+               {\r
+                       goto out;\r
+               }\r
+               else\r
+               {\r
+                       ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x22, &temp_data);\r
+                       if(ret)\r
+                               goto err;\r
+                       temp_data&=0xfdff;\r
+\r
+                       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x22, temp_data);\r
+                       if(ret)\r
+                               goto err;\r
+               }\r
+\r
+       }\r
+\r
+\r
+out:\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0000);\r
+       if(ret)\r
+               goto err;\r
+       return 0;\r
+\r
+err:\r
+       printk(KERN_INFO "***rda5990_bt_setup_A2_power failed! \n");\r
+       return -1;\r
+}\r
+\r
+int rda_bt_power_off(void);\r
+\r
+int rda_bt_power_on(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+       printk(KERN_INFO "rda_bt_power_on \n");\r
+\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+\r
+       for(count = 0; count < sizeof(rda_5990_bt_en_data)/sizeof(rda_5990_bt_en_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, rda_5990_bt_en_data[count][0], rda_5990_bt_en_data[count][1]);\r
+\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+       ret=rda5990_bt_setup_A2_power(1);       \r
+       if(ret)\r
+       {   \r
+               printk(KERN_INFO "***rda5990_bt_setup_A2_power fail!!! \n");\r
+               goto err;\r
+       }\r
+\r
+       printk(KERN_INFO "***rda_bt_power_on success!!! \n");\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       /*houzhen update 2012 03 06*/\r
+       msleep(10);     //delay 10 ms after power on\r
+\r
+\r
+       return 0;\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_bt_power_on failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+int rda_bt_power_off(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       printk(KERN_INFO "rda_bt_power_off \n");\r
+\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       for(count = 0; count < sizeof(rda_5990_bt_off_data)/sizeof(rda_5990_bt_off_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, rda_5990_bt_off_data[count][0], rda_5990_bt_off_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+       msleep(10);   //10ms\r
+       printk(KERN_INFO "***rda_bt_power_off success!!! \n");\r
+\r
+       ret=rda5990_bt_setup_A2_power(0);//disable ldo_pa reg \r
+       if(ret)\r
+               goto err;\r
+\r
+\r
+\r
+    mutex_unlock(&i2c_rw_lock);    \r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_bt_power_off failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+\r
+int RDA5990_bt_rf_init(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       printk(KERN_INFO "RDA5990_bt_rf_init \n");\r
+\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       for(count = 0; count < sizeof(rda_5990_bt_rf_data)/sizeof(rda_5990_bt_rf_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, rda_5990_bt_rf_data[count][0], rda_5990_bt_rf_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_rf_init success!!! \n");\r
+       msleep(5);   //5ms\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_rf_init failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+\r
+\r
+/*houzhen add 2012 04 09\r
+  add to ensure bt dig powerup\r
+  */\r
+\r
+int RDA5990_bt_dig_reset(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+\r
+       printk(KERN_INFO "RDA5990_bt_dig_reset \n");\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       for(count = 0; count < sizeof(RDA5990_bt_dig_reset_data)/sizeof(RDA5990_bt_dig_reset_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, RDA5990_bt_dig_reset_data[count][0], RDA5990_bt_dig_reset_data[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_dig_reset success!!! \n");\r
+       msleep(5);   //5ms\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_dig_reset failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+\r
+int RDA5990_bt_dc_cal(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       printk(KERN_INFO "rda_bt_dc_cal \n");\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+\r
+       for(count = 0; count < sizeof(rda_5990_bt_dc_cal)/sizeof(rda_5990_bt_dc_cal[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, rda_5990_bt_dc_cal[count][0], rda_5990_bt_dc_cal[count][1]);\r
+               if(ret)\r
+                       goto err;\r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_bt_dc_cal success!!! \n");\r
+       msleep(200);   //200ms\r
+\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***rda_bt_dc_cal  failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+\r
+\r
+/*houzhen update Mar 15 2012 \r
+  bypass RDA5990_bt_set_rf_switch when wf is already on\r
+  */\r
+\r
+int RDA5990_bt_set_rf_switch(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       u16 temp_data=0;\r
+       printk(KERN_INFO "RDA5990_bt_set_rf_switch \n");\r
+\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0001);\r
+       if(ret)\r
+               goto err;       \r
+\r
+       ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x31, &temp_data);\r
+\r
+       if(ret)\r
+               goto err;       \r
+\r
+       if(temp_data&0x8000)   // if wf is already on\r
+       {\r
+\r
+               printk(KERN_INFO "wf already en, bypass RDA5990_bt_set_rf_switch function \n");\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0000);\r
+               if(ret)\r
+                       goto err;       \r
+        mutex_unlock(&i2c_rw_lock);    \r
+               return 0;\r
+       }\r
+\r
+       for(count = 0; count < sizeof(rda_5990_bt_set_rf_switch_data)/sizeof(rda_5990_bt_set_rf_switch_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, rda_5990_bt_set_rf_switch_data[count][0], rda_5990_bt_set_rf_switch_data[count][1]);\r
+               if(ret)\r
+                       goto err;           \r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_set_rf_switch success!!! \n");\r
+       msleep(50);   //50ms\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_set_rf_switch  failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+\r
+/*houzhen update Mar 15 2012 \r
+  bypass RDA5990_bt_enable_clk when wf is already on\r
+  */\r
+\r
+int RDA5990_bt_enable_clk(void)\r
+{\r
+       unsigned int count = 0;\r
+       int ret = 0;\r
+       u16 temp_data=0;\r
+       printk(KERN_INFO "RDA5990_bt_enable_clk \n");\r
+\r
+    mutex_lock(&i2c_rw_lock);\r
+       ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0001);\r
+       if(ret)\r
+               goto err;       \r
+\r
+       ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x31, &temp_data);\r
+\r
+       if(ret)\r
+               goto err;       \r
+\r
+       if(temp_data&0x8000)   // if wf is already on\r
+       {\r
+\r
+               printk(KERN_INFO "wf already en, bypass RDA5990_bt_enable_clk function \n");\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0000);\r
+               if(ret)\r
+                       goto err;       \r
+        mutex_unlock(&i2c_rw_lock);\r
+               return 0;\r
+       }\r
+\r
+\r
+       for(count = 0; count < sizeof(RDA5990_bt_enable_clk_data)/sizeof(RDA5990_bt_enable_clk_data[0]); count ++)\r
+       {\r
+               ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, RDA5990_bt_enable_clk_data[count][0], RDA5990_bt_enable_clk_data[count][1]);\r
+               if(ret)\r
+                       goto err;       \r
+       }\r
+\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_enable_clk success!!! \n");\r
+       msleep(50);   //50ms\r
+       return 0;\r
+\r
+err:\r
+    mutex_unlock(&i2c_rw_lock);\r
+       printk(KERN_INFO "***RDA5990_bt_enable_clk  failed! \n");\r
+       return -1;\r
+\r
+}\r
+\r
+extern void mt_combo_bgf_enable_irq(void);\r
+extern void mt_combo_bgf_disable_irq(void);\r
+\r
+\r
+#define RDA_BT_IOCTL_MAGIC 'u'\r
+#define RDA_BT_POWER_ON_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x01)\r
+#define RD_BT_RF_INIT_IOCTL   _IO(RDA_BT_IOCTL_MAGIC ,0x02)\r
+#define RD_BT_DC_CAL_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x03)\r
+#define RD_BT_SET_RF_SWITCH_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x04)\r
+#define RDA_BT_POWER_OFF_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x05)\r
+#define RDA_BT_EN_CLK _IO(RDA_BT_IOCTL_MAGIC ,0x06)\r
+#define RD_BT_DC_DIG_RESET_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x07)\r
+\r
+#define RDA_WIFI_POWER_ON_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x10)\r
+#define RDA_WIFI_POWER_OFF_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x11)\r
+#define RDA_WIFI_POWER_SET_TEST_MODE_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x12)\r
+#define RDA_WIFI_POWER_CANCEL_TEST_MODE_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x13)\r
+#define RDA_WIFI_DEBUG_MODE_IOCTL    _IO(RDA_BT_IOCTL_MAGIC ,0x14)\r
+\r
+static int rda_5990_pw_ioctl(struct file *file, unsigned int cmd, unsigned long arg)\r
+{\r
+       int ret = 0;\r
+\r
+       switch(cmd)\r
+       {\r
+               case RDA_WIFI_POWER_ON_IOCTL:\r
+            ret = rda_wifi_power_on();\r
+                       break;\r
+\r
+               case RDA_WIFI_POWER_OFF_IOCTL:\r
+            ret = rda_wifi_power_off();\r
+                       break;\r
+\r
+               case RDA_WIFI_POWER_SET_TEST_MODE_IOCTL:\r
+                       wifi_in_test_mode = 1;\r
+                       printk("****set rda wifi in test mode");\r
+                       break;\r
+\r
+               case RDA_WIFI_POWER_CANCEL_TEST_MODE_IOCTL:\r
+                       wifi_in_test_mode = 0;\r
+                       printk("****set rda wifi in normal mode");\r
+                       break;\r
+\r
+        case RDA_WIFI_DEBUG_MODE_IOCTL:\r
+            ret = rda_wifi_debug_en();\r
+            break;\r
+               case RDA_BT_POWER_ON_IOCTL:\r
+                       {\r
+                               ret = rda_bt_power_on();\r
+                               mt_combo_bgf_enable_irq();\r
+                       }\r
+                       break;\r
+\r
+                       /* should call thif function after bt_power_on*/\r
+               case RDA_BT_EN_CLK:\r
+                       ret = RDA5990_bt_enable_clk();\r
+                       break;\r
+\r
+               case RD_BT_RF_INIT_IOCTL:\r
+                       ret = RDA5990_bt_rf_init();\r
+                       break;\r
+\r
+               case RD_BT_DC_CAL_IOCTL:        \r
+                       ret = RDA5990_bt_dc_cal();\r
+                       break;\r
+\r
+               case RD_BT_DC_DIG_RESET_IOCTL:  \r
+                       ret = RDA5990_bt_dig_reset();\r
+                       break;\r
+\r
+               case RD_BT_SET_RF_SWITCH_IOCTL:\r
+                       ret = RDA5990_bt_set_rf_switch();                       \r
+                       break;\r
+\r
+               case RDA_BT_POWER_OFF_IOCTL:\r
+                       {\r
+                               mt_combo_bgf_disable_irq();\r
+                               ret = rda_bt_power_off();               \r
+                       }\r
+                       break;\r
+\r
+               default:\r
+                       break;\r
+       }\r
+\r
+       printk(KERN_INFO "rda_bt_pw_ioctl cmd=0x%02x \n", cmd);\r
+\r
+       return ret;\r
+}      \r
+\r
+static int rda_5990_major;\r
+static struct class *rda_5990_class = NULL;\r
+static const struct file_operations rda_5990_operations = {\r
+       .owner = THIS_MODULE,\r
+       .unlocked_ioctl = rda_5990_pw_ioctl,\r
+       .release = NULL\r
+};\r
+\r
+void rda_5990_sleep_worker_task(struct work_struct *work)\r
+{\r
+       printk("---rda_5990_sleep_worker_task end");\r
+       wake_unlock(&rda_5990_wake_lock);\r
+}\r
+\r
+void rda_5990_set_wake_lock(void)\r
+{\r
+       wake_lock(&rda_5990_wake_lock);\r
+       cancel_delayed_work(&rda_5990_sleep_worker);\r
+       schedule_delayed_work(&rda_5990_sleep_worker, 6*HZ);\r
+}\r
+\r
+int rda_5990_power_ctrl_init(void)\r
+{\r
+       int ret = 0;\r
+\r
+       printk("rda_5990_power_ctrl_init begin\n");\r
+\r
+       rda_5990_major = register_chrdev(0, "rda5990_power_ctrl", &rda_5990_operations);\r
+       if(rda_5990_major < 0)\r
+       {\r
+               printk(KERN_INFO "register rdabt_power_ctrl failed!!! \n");\r
+               return rda_5990_major;\r
+       }\r
+\r
+       rda_5990_class = class_create(THIS_MODULE, "rda_combo");\r
+       if(IS_ERR(rda_5990_class))\r
+       {\r
+               unregister_chrdev(rda_5990_major, "rdabt_power_ctrl");\r
+               return PTR_ERR(rda_5990_class);\r
+       }\r
+\r
+       device_create(rda_5990_class, NULL, MKDEV(rda_5990_major, 0), NULL, "rdacombo");\r
+\r
+\r
+       INIT_DELAYED_WORK(&rda_5990_sleep_worker, rda_5990_sleep_worker_task);\r
+       wake_lock_init(&rda_5990_wake_lock, WAKE_LOCK_SUSPEND, "RDA_sleep_worker_wake_lock");\r
+\r
+    mutex_init(&i2c_rw_lock);    \r
+       printk("rda_5990_power_ctrl_init end\n");\r
+       return 0;\r
+}\r
+\r
+void rda_5990_power_ctrl_exit(void)\r
+{\r
+\r
+       unregister_chrdev(rda_5990_major, "rdabt_power_ctrl");\r
+       if(rda_5990_class)\r
+               class_destroy(rda_5990_class);       \r
+\r
+       cancel_delayed_work_sync(&rda_5990_sleep_worker);\r
+       wake_lock_destroy(&rda_5990_wake_lock);\r
+}\r
+\r
+unsigned char rda_5990_wifi_in_test_mode(void)\r
+{\r
+       return wifi_in_test_mode;\r
+}\r
+\r
+\r
+static __inline__ void cfmakeraw(struct termios *s)\r
+{\r
+       s->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);\r
+       s->c_oflag &= ~OPOST;\r
+       s->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);\r
+       s->c_cflag &= ~(CSIZE|PARENB);\r
+       s->c_cflag |= CS8;\r
+}\r
+\r
+static __inline__ int cfsetospeed(struct termios *s, speed_t  speed)\r
+{\r
+       s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD);\r
+       return 0;\r
+}\r
+\r
+static __inline__ int cfsetispeed(struct termios *s, speed_t  speed)\r
+{\r
+       s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD);\r
+       return 0;\r
+}\r
+\r
+static int rda_wifi_init_uart(char *dev)\r
+{\r
+       int errno;\r
+       struct termios ti;\r
+       struct serial_struct ss;\r
+       int fd;\r
+\r
+       fd = sys_open(dev, O_RDWR | O_NOCTTY, 0);\r
+       if (fd < 0) {\r
+               printk("Can't open serial port");\r
+               return -1;\r
+       }\r
+\r
+       sys_ioctl(fd, TCFLSH, TCIOFLUSH);\r
+\r
+       /* Clear the cust flag */\r
+       if((errno = sys_ioctl(fd, TIOCGSERIAL, &ss))<0){\r
+               printk("BAUD: error to get the serial_struct info:%s\n", errno);\r
+               goto err;\r
+       }\r
+\r
+       if (ss.flags & ASYNC_SPD_CUST) {\r
+               printk("clear ASYNC_SPD_CUST\r\n");\r
+               ss.flags &= ~ASYNC_SPD_CUST;\r
+       }\r
+       if((errno = sys_ioctl(fd, TIOCSSERIAL, &ss))<0){\r
+               printk("BAUD: error to set serial_struct:%s\n", errno);\r
+               goto err;\r
+       }\r
+\r
+       if ((errno = sys_ioctl(fd, TCGETS, (long)&ti))  < 0) {\r
+               printk("unable to get UART port setting");\r
+               printk("Can't get port settings");\r
+               goto err;\r
+       }\r
+\r
+       cfmakeraw(&ti);\r
+\r
+       ti.c_cflag |= CLOCAL;\r
+       ti.c_cflag &= ~CRTSCTS;\r
+       ti.c_lflag = 0;\r
+       ti.c_cc[VTIME]    = 5; /* 0.5 sec */\r
+       ti.c_cc[VMIN]     = 0;\r
+\r
+       /* Set initial baudrate */\r
+       cfsetospeed(&ti, B115200);\r
+       cfsetispeed(&ti, B115200);\r
+\r
+       if ((errno = sys_ioctl(fd, TCSETS, (long)&ti)) < 0) {\r
+               printk("unable to set UART port setting");\r
+               printk("Can't set port settings");\r
+               goto err;\r
+       }\r
+\r
+       errno = sys_ioctl(fd, TCFLSH, TCIOFLUSH);\r
+       if(errno < 0)\r
+               goto err;\r
+\r
+       return fd;\r
+\r
+err:\r
+       if(fd > 0)\r
+               sys_close(fd);\r
+\r
+       return -1;\r
+}  \r
+\r
+EXPORT_SYMBOL(rda_wlan_version);\r
+EXPORT_SYMBOL(rda_wifi_init_uart);\r
+EXPORT_SYMBOL(rda_5990_wifi_in_test_mode);\r
+\r
+EXPORT_SYMBOL(rda_5990_set_wake_lock);\r
+EXPORT_SYMBOL(rda_wifi_power_off);\r
+EXPORT_SYMBOL(rda_wifi_power_on);\r
+EXPORT_SYMBOL(rda_fm_power_on);\r
+EXPORT_SYMBOL(rda_fm_power_off);\r
+\r
+module_init(rda_5990_power_ctrl_init);\r
+module_exit(rda_5990_power_ctrl_exit);\r
+\r
+MODULE_LICENSE("GPL");\r
+\r
diff --git a/drivers/net/wireless/rda5990/rda_gpio_i2c/Makefile b/drivers/net/wireless/rda5990/rda_gpio_i2c/Makefile
new file mode 100755 (executable)
index 0000000..9a19286
--- /dev/null
@@ -0,0 +1,39 @@
+# Copyright Statement:
+#
+# This software/firmware and related documentation ("MediaTek Software") are
+# protected under relevant copyright laws. The information contained herein
+# is confidential and proprietary to MediaTek Inc. and/or its licensors.
+# Without the prior written permission of MediaTek inc. and/or its licensors,
+# any reproduction, modification, use or disclosure of MediaTek Software,
+# and information contained herein, in whole or in part, shall be strictly prohibited.
+#
+# MediaTek Inc. (C) 2010. All rights reserved.
+#
+# BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+# THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+# RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+# AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+# NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+# SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+# SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+# THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+# THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+# CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+# SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+# STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+# CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+# AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+# OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+# MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+#
+# The following software/firmware and/or related documentation ("MediaTek Software")
+# have been modified by MediaTek Inc. All revisions are subject to any receiver's
+# applicable license agreements with MediaTek Inc.
+
+
+# Makefile generated by Mediatek
+
+obj-y += rda_gpio_i2c.o
+
diff --git a/drivers/net/wireless/rda5990/rda_gpio_i2c/rda_gpio_i2c.c b/drivers/net/wireless/rda5990/rda_gpio_i2c/rda_gpio_i2c.c
new file mode 100755 (executable)
index 0000000..01a4277
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * =====================================================================================
+ *
+ *       Filename:  rda_gpio_i2c.c
+ *
+ *    Description:  
+ *
+ *        Version:  1.0
+ *        Created:  04/19/2012 11:24:48 PM
+ *       Revision:  none
+ *       Compiler:  gcc
+ *
+ *         Author:  Allen_Hu
+ *   Organization: RDA Inc. 
+ *
+ * =====================================================================================
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <mach/mt6575_gpio.h>
+#include <mach/mtk_rtc.h>
+
+#define RDA5990_WIFI_32K_FLAG  0x00000001
+#define RDA5990_BT_32K_FLAG    0x00000002
+#define RDA5990_FM_32K_FLAG    0x00000004
+
+//#define DELAY   10
+#define DELAY   2
+// FM
+#define SCL     GPIO149
+#define SDA     GPIO150
+//
+/* WIFI
+#define SCL     GPIO104
+#define SDA     GPIO102
+*/
+
+#define u8             unsigned char
+
+//Data storage mode
+static u8 isBigEnded = 0;
+
+//The global variable
+static unsigned int  gpioInitialized = 0;
+static unsigned int rda5990_32k_state = 0;
+
+#define GPIO_MODE_00 0
+#define GPIO_DIR_OUT 1
+#define GPIO_DIR_IN  0
+#define GPIO_OUT_ONE 1
+#define GPIO_OUT_ZERO 0
+
+void set_SDA_output(void)
+{
+       mt_set_gpio_mode(SDA, GPIO_MODE_00);
+       mt_set_gpio_dir(SDA, GPIO_DIR_OUT);
+}
+
+void set_SDA_input(void)
+{
+       mt_set_gpio_mode(SDA, GPIO_MODE_00);
+       mt_set_gpio_dir(SDA, GPIO_DIR_IN);
+}
+
+void set_SCL_output(void)
+{
+       mt_set_gpio_mode(SCL, GPIO_MODE_00);
+       mt_set_gpio_dir(SCL, GPIO_DIR_OUT);
+}
+
+void set_SDA_high(void)
+{
+       mt_set_gpio_out(SDA, GPIO_OUT_ONE);
+}
+
+void set_SDA_low(void)
+{
+       mt_set_gpio_out(SDA, GPIO_OUT_ZERO);
+}
+
+void set_SCL_high(void)
+{
+       mt_set_gpio_out(SCL, GPIO_OUT_ONE);
+}
+
+void set_SCL_low(void)
+{
+       mt_set_gpio_out(SCL, GPIO_OUT_ZERO);
+}
+
+u8 get_SDA_bit(void)
+{
+       return mt_get_gpio_in(SDA);
+}
+
+void i2c_delay(unsigned short cnt)
+{
+       udelay(cnt);
+}
+
+//如果32k不能出来,请修改以下配置使能。
+void enable_32k_rtc(void)
+{
+       printk("enable 32k rtc called\n");
+       rtc_gpio_enable_32k(RTC_GPIO_USER_GPS);
+       //rtc_gpio_enable_32k(RTC_GPIO_USER_WIFI);
+       //rtc_gpio_enable_32k(RTC_GPIO_USER_BT);
+       //rtc_gpio_enable_32k(RTC_GPIO_USER_WIFI);
+       rtc_gpio_enable_32k(RTC_GPIO_USER_FM);
+       msleep(100);
+}
+
+void disable_32k_rtc(void)
+{
+       //rtc_gpio_disable_32k(RTC_GPIO_USER_WIFI);
+       rtc_gpio_enable_32k(RTC_GPIO_USER_GPS);
+       rtc_gpio_disable_32k(RTC_GPIO_USER_FM);
+       msleep(50);
+}
+
+void i2c_start(void)  
+{    
+       set_SDA_output();
+       set_SCL_output();
+
+       set_SDA_high();
+       i2c_delay(DELAY);  
+       set_SCL_high();
+       i2c_delay(DELAY);  
+
+       set_SDA_low();
+       i2c_delay(DELAY);
+       set_SCL_low();
+       i2c_delay(DELAY);
+}  
+
+void i2c_stop(void)  
+{   
+       set_SDA_output();
+       set_SCL_output();
+       set_SDA_low();
+       i2c_delay(DELAY);
+       set_SCL_high();
+       i2c_delay(4*DELAY);
+       set_SDA_high();
+       i2c_delay(4*DELAY);
+}
+
+
+/* 
+ * return value:
+ *      0 ---  收到ACK
+ *      1 ---  没收到ACK
+ */
+u8 i2c_send_byte(u8 send_byte)
+{
+       u8 rc = 0;
+       u8 out_mask = 0x80;
+       u8 value;
+       u8 count = 8;
+
+       set_SDA_output();
+       set_SCL_output();
+
+       while(count > 0) {
+               set_SCL_low();
+               i2c_delay(DELAY);
+               value = ((send_byte & out_mask) ? 1 : 0);
+               if (value == 1) {
+                       set_SDA_high();
+               }
+               else {
+                       set_SDA_low();
+               }
+               send_byte <<= 1;
+               i2c_delay(DELAY);
+
+               set_SCL_high();
+               i2c_delay(DELAY);
+
+               count--;
+       }
+       set_SCL_low();
+       set_SDA_input();
+       i2c_delay(4*DELAY);
+       set_SCL_high();
+       i2c_delay(DELAY);
+       rc = get_SDA_bit();
+       i2c_delay(DELAY);
+       set_SCL_low();
+
+       return rc;
+}
+
+/* 
+ * ack = 0 发送ACK
+ * ack = 1 不发送ACK
+ */
+void i2c_read_byte(u8 *buffer, u8 ack)
+{
+       u8 count = 0x08;
+       u8 data = 0x00;
+       u8 temp = 0;
+
+       set_SCL_output();
+       while(count > 0) { 
+               set_SCL_low();
+               i2c_delay(2*DELAY);
+               if(count == 8)
+                       set_SDA_input();
+               i2c_delay(DELAY);
+               set_SCL_high();
+               i2c_delay(2*DELAY);
+               temp = get_SDA_bit();
+               data <<= 1;
+               if (temp)
+                       data |= 0x01;
+
+               i2c_delay(DELAY);
+               count--;
+       } 
+
+       set_SCL_low();
+       i2c_delay(2*DELAY);
+       set_SDA_output();
+       i2c_delay(DELAY);
+       if(ack){
+               set_SDA_high();
+       }else{
+               set_SDA_low();
+       }
+       i2c_delay(DELAY);
+       set_SCL_high();
+       i2c_delay(2*DELAY);
+
+       *buffer = data;
+       set_SCL_low();
+}
+
+/*     
+*  write data to the I2C bus by GPIO simulated of a digital device rountine. 
+* 
+*  @param  
+*              chipAddr:  address of the device
+*              regAddr:   address of register within device
+*       data:    the data to be written
+*       len:      the data length
+*   
+*/
+int rda_gpio_i2c_write_1_addr_2_data(u8 chipAddr, u8 regAddr, unsigned short data)
+{
+       u8 acknowledge;
+       int ret = 0;
+       i2c_start();
+
+       acknowledge = i2c_send_byte((chipAddr << 1) | 0x00);
+       if(acknowledge == 1){
+       //      return -1;
+               ret = -1;
+               goto out;
+       }
+
+       acknowledge = i2c_send_byte(regAddr);
+       if(acknowledge == 1){
+               ret = -1;
+               goto out;
+       }
+
+       acknowledge = i2c_send_byte(data>>8);
+       if(acknowledge == 1){
+               ret = -1;
+               goto out;
+       }
+       acknowledge = i2c_send_byte(data);
+       ret = acknowledge;
+
+out:
+       i2c_stop();
+
+       //return acknowledge;
+       return ret;
+}
+/*     
+*  read data from the I2C bus by GPIO simulated of a digital device rountine. 
+* 
+*  @param  
+*              chipAddr:  address of the device
+*              regAddr:   address of register within device
+*       buffer:    the data to be stored
+*       len:      the data length
+*   
+*/
+int rda_gpio_i2c_read_1_addr_2_data(u8 chipAddr, u8 regAddr, unsigned short *buffer)
+{
+       u8 tempdata, acknowledge;
+       int ret = 0;
+
+       i2c_start();
+       acknowledge = i2c_send_byte( (chipAddr << 1) | 0x00 );
+       if(acknowledge == 1){
+               ret = -1;
+               goto out;
+       }
+       acknowledge = i2c_send_byte(regAddr);
+       if(acknowledge == 1){
+               ret = -1;
+               goto out;
+       }
+
+       i2c_start();//restart   
+       acknowledge = i2c_send_byte( (chipAddr << 1) | 0x01 );
+       if(acknowledge == 1){
+               ret = -1;
+               goto out;
+       }
+
+       i2c_read_byte(&tempdata, 0);
+       *buffer = (tempdata<<8);
+       i2c_read_byte(&tempdata, 1);
+       *buffer |= tempdata;
+
+out:
+       i2c_stop();
+//     return acknowledge;
+       return ret;
+}
+
+u8 rda_gpio_i2c_write_4_addr_4_data(u8 chipAddr, unsigned int regAddr, unsigned int data)
+{
+       u8 acknowledge;
+       i2c_start();
+
+       acknowledge = i2c_send_byte((chipAddr << 1) | 0x00);
+       acknowledge = i2c_send_byte(regAddr>>24);
+       acknowledge = i2c_send_byte(regAddr>>16);
+       acknowledge = i2c_send_byte(regAddr>>8);
+       acknowledge = i2c_send_byte(regAddr);
+
+       acknowledge = i2c_send_byte((chipAddr << 1) | 0x00);
+
+       acknowledge = i2c_send_byte(data>>24);
+       acknowledge = i2c_send_byte(data>>16);
+       acknowledge = i2c_send_byte(data>>8);
+       acknowledge = i2c_send_byte(data);
+
+       i2c_stop();
+       return acknowledge;
+}
+
+u8 rda_gpio_i2c_read_4_addr_4_data(u8 chipAddr, unsigned int regAddr, unsigned int *buffer)
+{
+       u8 tempdata, acknowledge;
+       u8 i = 0;
+
+       i2c_start();
+       acknowledge = i2c_send_byte( (chipAddr << 1) | 0x00 );
+       acknowledge = i2c_send_byte(regAddr>>24);
+       acknowledge = i2c_send_byte(regAddr>>16);
+       acknowledge = i2c_send_byte(regAddr>>8);
+       acknowledge = i2c_send_byte(regAddr);
+
+       i2c_start();//restart   
+       acknowledge = i2c_send_byte( (chipAddr << 1) | 0x01 );
+
+       i2c_read_byte(&tempdata, 0);
+       *buffer = (tempdata<<24);
+       i2c_read_byte(&tempdata, 0);
+       *buffer |= (tempdata<<16);
+       i2c_read_byte(&tempdata, 0);
+       *buffer |= (tempdata<<8);
+       i2c_read_byte(&tempdata, 1);
+       *buffer |= tempdata;
+
+       i2c_stop();
+       return acknowledge;
+}
+
+void rda_gpio_i2c_enable_32k(unsigned int flag)
+{
+       if(rda5990_32k_state == 0 )
+       {       
+               enable_32k_rtc();
+       }
+       rda5990_32k_state |= (flag&0x07);
+}
+
+void rda_gpio_i2c_disable_32k(unsigned int flag)
+{
+       rda5990_32k_state &= (~flag);
+       if(rda5990_32k_state == 0)
+               disable_32k_rtc();
+}
+
+/*   
+* initializes I2C interface routine. 
+* 
+* @return value:
+*              0--success; 
+*              1--error. 
+* 
+*/
+static int __init rda_gpio_i2c_init(void)
+{
+       if(gpioInitialized == 0){
+               printk(KERN_INFO "RDA GPIO control for I2C Driver \n");
+
+               unsigned char *temp = NULL;
+               unsigned short testData = 0xaa55;
+               temp = (unsigned char *)&testData;
+               if(*temp == 0x55){
+                       isBigEnded = 0;
+               }else{
+                       isBigEnded = 1;
+               }
+
+
+               gpioInitialized = 1;
+               rda_gpio_i2c_enable_32k(RDA5990_FM_32K_FLAG);
+
+               /* FM
+               unsigned short regValue = 0;
+               rda_gpio_i2c_read_1_addr_2_data(0x11, 0x0C, &regValue);
+               printk(KERN_ALERT "####[%s, %d], Addr=%02X, value=%04X\n", __func__, __LINE__, 0x0C, regValue);
+               */
+               /*WIFI
+               unsigned short regValue = 0x0001;
+               rda_gpio_i2c_write_1_addr_2_data(0x14, 0x3f, regValue);
+               regValue = 0;
+               rda_gpio_i2c_read_1_addr_2_data(0x14, 0x3f, &regValue);
+               printk(KERN_ALERT "####[%s, %d], Addr=%02X, value=%04X\n", __func__, __LINE__, 0x3f, regValue);
+               regValue = 0;
+               rda_gpio_i2c_read_1_addr_2_data(0x14, 0x20, &regValue);
+               printk(KERN_ALERT "####[%s, %d], Addr=%02X, value=%04X\n", __func__, __LINE__, 0x20, regValue);
+               */
+
+               return 0;
+       }else{
+               printk("RDA GPIO control for I2C has been initialized.\n");
+               return 0;
+       }
+}
+
+static void __exit rda_gpio_i2c_exit(void)
+{
+       gpioInitialized = 0;
+       rda_gpio_i2c_disable_32k(RDA5990_FM_32K_FLAG);
+}
+
+EXPORT_SYMBOL(rda_gpio_i2c_read_1_addr_2_data);
+EXPORT_SYMBOL(rda_gpio_i2c_write_1_addr_2_data);
+EXPORT_SYMBOL(rda_gpio_i2c_enable_32k);
+EXPORT_SYMBOL(rda_gpio_i2c_disable_32k);
+
+EXPORT_SYMBOL(rda_gpio_i2c_read_4_addr_4_data);
+EXPORT_SYMBOL(rda_gpio_i2c_write_4_addr_4_data);
+
+module_init(rda_gpio_i2c_init); /*  load the module */
+module_exit(rda_gpio_i2c_exit); /*  unload the module */
diff --git a/drivers/net/wireless/rda5990/rda_wlan/Makefile b/drivers/net/wireless/rda5990/rda_wlan/Makefile
new file mode 100755 (executable)
index 0000000..e8c121f
--- /dev/null
@@ -0,0 +1,20 @@
+obj-m := rda5890.o
+rda5890-objs := \
+       rda5890_if_sdio.o \
+       rda5890_dev.o \
+       rda5890_txrx.o \
+       rda5890_wid.o \
+       rda5890_wext.o \
+       rda5890_debugfs.o\
+       rda5890_sdio_patch.o\
+       rda5890_nvram.o\
+  rda5890_scan.o
+
+KERNEL_DIR = /home/dxj/share/rk2928/kernel
+
+all:
+       make -C $(KERNEL_DIR) M=`pwd` modules
+
+clean:
+       rm -f Module.symvers *.o modules.order *.ko wifi_launcher.mod.c
+       rm -rf .wifi* .tmp* wlan.mod.c
\ No newline at end of file
diff --git a/drivers/net/wireless/rda5990/rda_wlan/Makefile_gwl b/drivers/net/wireless/rda5990/rda_wlan/Makefile_gwl
new file mode 100755 (executable)
index 0000000..92c6886
--- /dev/null
@@ -0,0 +1,23 @@
+MODULE_NAME = rda5890
+CONFIG_RDA5890 = m
+$(MODULE_NAME)-y += \
+       rda5890_if_sdio.o \
+       rda5890_dev.o \
+       rda5890_txrx.o \
+       rda5890_wid.o \
+       rda5890_wext.o \
+       rda5890_debugfs.o\
+       rda5890_sdio_patch.o\
+       rda5890_nvram.o\
+    rda5890_scan.o
+       
+obj-$(CONFIG_RDA5890) += $(MODULE_NAME).o
+
+KERNEL_DIR = /home/dxj/share/rk2928/kernel
+
+all:
+       make -C $(KERNEL_DIR) M=`pwd` modules
+
+clean:
+       rm -f Module.symvers *.o modules.order *.ko wifi_launcher.mod.c
+       rm -rf .wifi* .tmp* wlan.mod.c
\ No newline at end of file
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.c b/drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.c
new file mode 100755 (executable)
index 0000000..2ab7b1f
--- /dev/null
@@ -0,0 +1,593 @@
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <net/iw_handler.h>
+
+#include "rda5890_defs.h"
+#include "rda5890_dev.h"
+
+static struct dentry *rda5890_dir = NULL;
+
+void dump_buf(char *data, size_t len)
+{
+    char temp_buf[64];
+    size_t i, off = 0;
+
+    memset(temp_buf, 0, 64);
+    for (i=0;i<len;i++) {
+        if(i%8 == 0) {
+            sprintf(&temp_buf[off], "  ");
+            off += 2;
+        }
+        sprintf(&temp_buf[off], "%02x ", data[i]);
+        off += 3;
+        if((i+1)%16 == 0) {
+            RDA5890_DBGLAP(RDA5890_DA_ALL, RDA5890_DL_TRACE,
+                "%s\n", temp_buf);
+            memset(temp_buf, 0, 64);
+            off = 0;
+        }
+    }
+    RDA5890_DBGLAP(RDA5890_DA_ALL, RDA5890_DL_TRACE, "\n");
+}
+
+static int open_file_generic(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+#if 0
+static ssize_t rda5890_write_file_dummy(struct file *file, const char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+        return -EINVAL;
+}
+#endif
+
+static ssize_t rda5890_debug_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       //struct rda5890_private *priv = file->private_data;
+       size_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+       ssize_t res;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s\n", __func__);
+
+       pos += snprintf(buf+pos, PAGE_SIZE - pos, "state = %s\n",
+                               "LWANG_TESTING");
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+       free_page(addr);
+       return res;
+}
+
+static ssize_t rda5890_debug_write(struct file *file,
+                               const char __user *user_buf, size_t count,
+                               loff_t *ppos)
+{
+       //struct rda5890_private *priv = file->private_data;
+       ssize_t ret;
+       int p1, p2, p3, p4;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s\n", __func__);
+
+       if (copy_from_user(buf, user_buf, count)) {
+               ret = -EFAULT;
+               goto out_unlock;
+       }
+       ret = sscanf(buf, "%d %d %d %d", &p1, &p2, &p3, &p4);
+       if (ret != 4) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "input p1 = %d, p2 = %d, p3 = %d, p4 = %d\n",
+               p1, p2, p3, p4);
+
+       ret = count;
+out_unlock:
+       free_page(addr);
+       return ret;
+}
+
+static ssize_t rda5890_debugarea_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       //struct rda5890_private *priv = file->private_data;
+       size_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+       ssize_t res;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "get debug_area = 0x%x\n",rda5890_dbg_area);
+
+       pos += snprintf(buf+pos, PAGE_SIZE - pos, "%x\n",
+                               rda5890_dbg_area);
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+       free_page(addr);
+       return res;
+}
+
+static ssize_t rda5890_debugarea_write(struct file *file,
+                               const char __user *user_buf, size_t count,
+                               loff_t *ppos)
+{
+       //struct rda5890_private *priv = file->private_data;
+       ssize_t ret;
+       int debug_area;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s\n", __func__);
+
+       if (copy_from_user(buf, user_buf, count)) {
+               ret = -EFAULT;
+               goto out_unlock;
+       }
+       ret = sscanf(buf, "%x", &debug_area);
+       if (ret != 1) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+    rda5890_dbg_area = debug_area;
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "set debug_area = 0x%x\n",rda5890_dbg_area);
+
+       ret = count;
+out_unlock:
+       free_page(addr);
+       return ret;
+}
+
+static ssize_t rda5890_debuglevel_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       //struct rda5890_private *priv = file->private_data;
+       size_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+       ssize_t res;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "get debug_level = 0x%x\n",rda5890_dbg_level);
+
+       pos += snprintf(buf+pos, PAGE_SIZE - pos, "%x\n",
+                               rda5890_dbg_level);
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+       free_page(addr);
+       return res;
+}
+
+static ssize_t rda5890_debuglevel_write(struct file *file,
+                               const char __user *user_buf, size_t count,
+                               loff_t *ppos)
+{
+       //struct rda5890_private *priv = file->private_data;
+       ssize_t ret;
+       int debug_level;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s\n", __func__);
+
+       if (copy_from_user(buf, user_buf, count)) {
+               ret = -EFAULT;
+               goto out_unlock;
+       }
+       ret = sscanf(buf, "%x", &debug_level);
+       if (ret != 1) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+    rda5890_dbg_level = debug_level;
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "set debug_level = 0x%x\n",rda5890_dbg_level);
+
+       ret = count;
+out_unlock:
+       free_page(addr);
+       return ret;
+}
+
+static int debug_read_flag = 0;
+
+static ssize_t rda5890_sdio_read(struct file *file, 
+                               const char __user *user_buf, size_t count,
+                               loff_t *ppos)
+{
+       //struct rda5890_private *priv = file->private_data;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s\n", __func__);
+
+       if (copy_from_user(buf, user_buf, count)) {
+               ret = -EFAULT;
+               goto out_unlock;
+       }
+       ret = sscanf(buf, "%d", &debug_read_flag);
+       if (ret != 1) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+out_unlock:
+       free_page(addr);
+       return count;
+}
+
+static ssize_t rda5890_sdio_write(struct file *file,
+                               const char __user *user_buf, size_t count,
+                               loff_t *ppos)
+{
+       struct rda5890_private *priv = file->private_data;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+       int iter, len, i;
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s\n", __func__);
+
+       if (copy_from_user(buf, user_buf, count)) {
+               ret = -EFAULT;
+               goto out_unlock;
+       }
+       ret = sscanf(buf, "%d %d", &iter, &len);
+       if (ret != 2) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       if (len > 1660) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       for (i=0; i<len; i++) {
+               buf[i] = len - i - 1;
+       }
+
+       for (i=0;i<iter;i++) {
+               //RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               //      "Host to Card, len = %d\n", len);
+
+               ret = priv->hw_host_to_card(priv, buf, len, DATA_REQUEST_PACKET);
+
+               //RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               //      "Host to Card done, ret = %d\n", ret);
+       }
+
+out_unlock:
+       free_page(addr);
+       return count;
+}
+
+#define SDIO_TEST_CMD_MAGIC        0x55664433
+#define SDIO_TEST_CMD_LEN          16
+
+#define SDIO_TEST_CMD_TYPE_H2C_START            1
+#define SDIO_TEST_CMD_TYPE_H2C_STOP             2
+#define SDIO_TEST_CMD_TYPE_H2C_STATUS           3
+#define SDIO_TEST_CMD_TYPE_C2H_START            4
+#define SDIO_TEST_CMD_TYPE_C2H_PILOT            5
+#define SDIO_TEST_CMD_TYPE_C2H_END              6
+
+static int recv_time_start, recv_time_end;
+static int send_time_start, send_time_end;
+
+void rda5890_sdio_test_card_to_host(char *buf, unsigned short len)
+{
+       int i;
+       int cmd, cmd_iter, cmd_len;
+       int time_ms;
+       static int recv_pattern = 0;
+       static int recv_tput_flag = 0;
+       static int recv_pkts = 0;
+       static int recv_bytes = 0;
+
+       //RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+       //      "SDIO TEST Card to Host, len = %d\n", len);
+
+       if (debug_read_flag) {
+               RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+                       "DEBUG RX, len = %d\n", len);
+               dump_buf(buf, len);
+       }
+
+       if ((*(volatile unsigned long *)buf == SDIO_TEST_CMD_MAGIC)
+                       && len == SDIO_TEST_CMD_LEN) {
+               cmd = (int)(*(volatile unsigned long *)(buf + 4));
+               cmd_iter = (int)(*(volatile unsigned long *)(buf + 8));
+               cmd_len = (int)(*(volatile unsigned long *)(buf + 12));
+               //RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               //              "SDIO TEST CMD: cmd = %d, iter = %d, len = %d\n", 
+               //              cmd, cmd_iter, cmd_len);
+               switch (cmd) {
+               case SDIO_TEST_CMD_TYPE_H2C_STATUS:
+                       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+                               "H2C STATUS CMD \n");
+                       time_ms = jiffies_to_msecs(send_time_end - send_time_start);
+                       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+                               "SDIO H2C STATUS: pkts = %d, bytes = %d, time = %d ms\n",
+                               cmd_iter, cmd_len, time_ms);
+                       break;
+               case SDIO_TEST_CMD_TYPE_C2H_PILOT:
+                       //RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+                       //      "C2H PILOT CMD \n");
+                       recv_pattern = 0;
+                       recv_tput_flag = 1;
+                       recv_pkts = 0;
+                       recv_bytes = 0;
+                       recv_time_start = jiffies;
+                       break;
+               case SDIO_TEST_CMD_TYPE_C2H_END:
+                       //RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+                       //      "C2H END CMD \n");
+                       recv_time_end = jiffies;
+                       recv_tput_flag = 0;
+                       time_ms = jiffies_to_msecs(recv_time_end - recv_time_start);
+                       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+                               "SDIO C2H STATUS: pkts = %d, bytes = %d, time = %d ms\n",
+                               recv_pkts, recv_bytes, time_ms);
+                       break;
+               case SDIO_TEST_CMD_TYPE_H2C_START:
+               case SDIO_TEST_CMD_TYPE_H2C_STOP:
+               case SDIO_TEST_CMD_TYPE_C2H_START:
+               default:
+                       RDA5890_ERRP("SDIO TEST CMD: Invalid cmd %d\n", cmd);
+               break;
+               }
+               return;
+       }
+
+       for (i=0;i<len;i++) {
+               if (recv_pattern == 0) {
+                       if (buf[i] != (char)(i)) {
+                               RDA5890_ERRP("data[%d] error, 0x%02x, should be 0x%02x, len = %d\n", 
+                                       i, buf[i], (char)(i), len);
+                               break;
+                       }
+               }
+               else {
+                       if (buf[i] != (char)(len - i - 1)) {
+                               RDA5890_ERRP("data[%d] error, 0x%02x, should be 0x%02x, len = %d\n", 
+                                       i, buf[i], (char)(len - i - 1), len);
+                               break;
+                       }
+               }
+       }
+
+       if (recv_tput_flag)
+               recv_pattern = !recv_pattern;
+
+       if (recv_tput_flag && i==len) {
+               recv_pkts ++;
+               recv_bytes += len;
+       }
+}
+
+static void sdio_tput_test_read(struct rda5890_private *priv, int iter, int len)
+{
+       char cmd[SDIO_TEST_CMD_LEN];
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s, iter = %d, len = %d\n", __func__, iter, len);
+
+       (*(volatile unsigned long *)(cmd + 0)) = SDIO_TEST_CMD_MAGIC;
+       (*(volatile unsigned long *)(cmd + 4)) = SDIO_TEST_CMD_TYPE_C2H_START;
+       (*(volatile unsigned long *)(cmd + 8)) = iter;
+       (*(volatile unsigned long *)(cmd + 12)) = len;
+       ret = priv->hw_host_to_card(priv, cmd, len ,SDIO_TEST_CMD_LEN);
+       if (ret) {
+               RDA5890_ERRP("START cmd send fail, ret = %d\n", ret);
+       }
+}
+
+static void sdio_tput_test_write(struct rda5890_private *priv, int iter, int len)
+{
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf_1 = (char *)addr;
+       char *buf_2 = (char *)addr + PAGE_SIZE/2;
+       char cmd[SDIO_TEST_CMD_LEN];
+       int i;
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s, iter = %d, len = %d\n", __func__, iter, len);
+
+       for (i=0; i<len; i++) {
+               buf_1[i] = len - i - 1;
+               buf_2[i] = i;
+       }
+
+       (*(volatile unsigned long *)(cmd + 0)) = SDIO_TEST_CMD_MAGIC;
+       (*(volatile unsigned long *)(cmd + 4)) = SDIO_TEST_CMD_TYPE_H2C_START;
+       (*(volatile unsigned long *)(cmd + 8)) = iter;
+       (*(volatile unsigned long *)(cmd + 12)) = len;
+       ret = priv->hw_host_to_card(priv, cmd, len,SDIO_TEST_CMD_LEN);
+       if (ret) {
+               RDA5890_ERRP("START cmd send fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       send_time_start = jiffies;
+       for (i=0;i<iter;i++) {
+               if (!(i & 1))
+                       ret = priv->hw_host_to_card(priv, buf_1, len, DATA_REQUEST_PACKET);
+               else
+                       ret = priv->hw_host_to_card(priv, buf_2, len, DATA_REQUEST_PACKET);
+               if (ret) {
+                       RDA5890_ERRP("packet %d send fail, ret = %d\n", i, ret);
+                       goto out;
+               }
+       }
+       send_time_end = jiffies;
+
+       (*(volatile unsigned long *)(cmd + 0)) = SDIO_TEST_CMD_MAGIC;
+       (*(volatile unsigned long *)(cmd + 4)) = SDIO_TEST_CMD_TYPE_H2C_STOP;
+       (*(volatile unsigned long *)(cmd + 8)) = iter;
+       (*(volatile unsigned long *)(cmd + 12)) = len;
+       ret = priv->hw_host_to_card(priv, cmd, len, SDIO_TEST_CMD_LEN);
+       if (ret) {
+               RDA5890_ERRP("START cmd send fail, ret = %d\n", ret);
+               goto out;
+       }
+
+out:
+       free_page(addr);
+}
+
+static ssize_t rda5890_sdio_tput(struct file *file,
+                               const char __user *user_buf, size_t count,
+                               loff_t *ppos)
+{
+       struct rda5890_private *priv = file->private_data;
+       int ret;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+       int wr, iter, len;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s\n", __func__);
+
+       if (copy_from_user(buf, user_buf, count)) {
+               ret = -EFAULT;
+               goto out_unlock;
+       }
+       ret = sscanf(buf, "%d %d %d", &wr, &iter, &len);
+       if (ret != 3) {
+               RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+                       "Error Input, format shall be [wr iter len]\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "input wr = %d, iter = %d, len = %d\n",
+               wr, iter, len);
+
+       if (wr)
+               sdio_tput_test_write(priv, iter, len);
+       else
+               sdio_tput_test_read(priv, iter, len);
+
+       ret = count;
+out_unlock:
+       free_page(addr);
+       return ret;
+}
+
+#define FOPS(fread, fwrite) { \
+       .owner = THIS_MODULE, \
+       .open = open_file_generic, \
+       .read = (fread), \
+       .write = (fwrite), \
+}
+
+struct rda5890_debugfs_files {
+       char *name;
+       int perm;
+       struct file_operations fops;
+};
+
+static struct rda5890_debugfs_files debugfs_files[] = {
+       { "debug", 0444, FOPS(rda5890_debug_read, rda5890_debug_write), },
+       { "debugarea", 0444, FOPS(rda5890_debugarea_read, rda5890_debugarea_write), },
+       { "debuglevel", 0444, FOPS(rda5890_debuglevel_read, rda5890_debuglevel_write), },
+       { "sdioread", 0444, FOPS(NULL, rda5890_sdio_read), },
+       { "sdiowrite", 0444, FOPS(NULL, rda5890_sdio_write), },
+       { "sdiotput", 0444, FOPS(NULL, rda5890_sdio_tput), },
+};
+
+void rda5890_debugfs_init(void)
+{
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s\n", __func__);
+
+       if (!rda5890_dir)
+               rda5890_dir = debugfs_create_dir("rda5890", NULL);
+
+       return;
+}
+
+void rda5890_debugfs_remove(void)
+{
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s\n", __func__);
+
+       if (rda5890_dir)
+                debugfs_remove(rda5890_dir);
+
+       return;
+}
+
+void rda5890_debugfs_init_one(struct rda5890_private *priv)
+{
+       int i;
+       struct rda5890_debugfs_files *files;
+       if (!rda5890_dir)
+               goto exit;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s\n", __func__);
+
+       priv->debugfs_dir = debugfs_create_dir("rda5890_dev", rda5890_dir);
+       if (!priv->debugfs_dir)
+               goto exit;
+
+       for (i=0; i<ARRAY_SIZE(debugfs_files); i++) {
+               files = &debugfs_files[i];
+               priv->debugfs_files[i] = debugfs_create_file(files->name,
+                                                            files->perm,
+                                                            priv->debugfs_dir,
+                                                            priv,
+                                                            &files->fops);
+       }
+
+exit:
+       return;
+}
+
+void rda5890_debugfs_remove_one(struct rda5890_private *priv)
+{
+       int i;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s\n", __func__);
+
+       for(i=0; i<ARRAY_SIZE(debugfs_files); i++)
+               debugfs_remove(priv->debugfs_files[i]);
+       debugfs_remove(priv->debugfs_dir);
+}
+
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.h b/drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.h
new file mode 100755 (executable)
index 0000000..b42bc4e
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _RDA5890_DEBUGFS_H_
+#define _RDA5890_DEBUGFS_H_
+
+void rda5890_debugfs_init(void);
+void rda5890_debugfs_remove(void);
+
+void rda5890_debugfs_init_one(struct rda5890_private *priv);
+void rda5890_debugfs_remove_one(struct rda5890_private *priv);
+
+/* This is for SDIO testing */
+void rda5890_sdio_test_card_to_host(char *buf, unsigned short len);
+
+#endif
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_defs.h b/drivers/net/wireless/rda5990/rda_wlan/rda5890_defs.h
new file mode 100755 (executable)
index 0000000..ef8e114
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef _RDA5890_DEFS_H_
+#define _RDA5890_DEFS_H_
+
+#include <linux/spinlock.h>
+
+#define RDA5890_SDIOWIFI_VER_MAJ     0
+#define RDA5890_SDIOWIFI_VER_MIN     3
+#define RDA5890_SDIOWIFI_VER_BLD     1
+
+#define WIFI_POWER_MANAGER //if need wifi sleep for power save should open this 
+
+
+#define WIFI_UNLOCK_SYSTEM
+#define GET_SCAN_FROM_NETWORK_INFO
+
+#define USE_MAC_DYNAMIC_ONCE
+//#define WIFI_TEST_MODE
+
+#define DEBUG
+
+extern int rda5890_dbg_level;
+extern int rda5890_dbg_area;
+
+typedef enum {
+       RDA5890_DL_ALL   = 0, 
+       RDA5890_DL_CRIT  = 1,
+       RDA5890_DL_TRACE = 2,
+       RDA5890_DL_NORM  = 3,
+       RDA5890_DL_DEBUG = 4,
+       RDA5890_DL_VERB  = 5,
+} RDA5890_DBG_LEVEL;
+
+#define RDA5890_DA_MAIN            (1 << 0)
+#define RDA5890_DA_SDIO            (1 << 1)
+#define RDA5890_DA_ETHER           (1 << 2)
+#define RDA5890_DA_WID             (1 << 3)
+#define RDA5890_DA_WEXT            (1 << 4)
+#define RDA5890_DA_TXRX            (1 << 5)
+#define RDA5890_DA_PM              (1 << 6)
+#define RDA5890_DA_ALL             0x0000007f
+
+#define RDA5890_LOG "RDA5890: "
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+#ifdef DEBUG 
+#define RDA5890_DBGLA(area, lvl)                                             \
+       (((lvl)<=rda5890_dbg_level) && ((area)&rda5890_dbg_area))
+#define RDA5890_DBGLAP(area,lvl, x...)                                       \
+       do{                                                                  \
+               if (((lvl)<=rda5890_dbg_level) && ((area)&rda5890_dbg_area)) \
+                       printk(KERN_INFO RDA5890_LOG x );                    \
+       }while(0)
+#define RDA5890_DBGP(x...)                                                   \
+       do{                                                                  \
+               printk(KERN_INFO RDA5890_LOG x );                            \
+       }while(0)
+#else
+#define RDA5890_DBGLA(area, lvl)    0
+#define RDA5890_DBGLAP(area,lvl, x...)  do {} while (0) 
+#define RDA5890_DBGP(x...)  do {} while (0) 
+#endif
+
+#define RDA5890_ERRP(fmt, args...)                                          \
+       do{                                                                 \
+               printk(KERN_ERR RDA5890_LOG "%s: "fmt, __func__, ## args ); \
+       }while(0)
+
+#endif
+
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.c b/drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.c
new file mode 100755 (executable)
index 0000000..8bea49c
--- /dev/null
@@ -0,0 +1,633 @@
+/**
+  * This file contains the major functions in WLAN
+  * driver. It includes init, exit, open, close and main
+  * thread etc..
+  */
+
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/kthread.h>
+#include <linux/kfifo.h>
+#include <linux/stddef.h>
+#include <linux/mmc/sdio_func.h>
+
+
+#include <net/iw_handler.h>
+#include <linux/jiffies.h>
+
+
+#include "rda5890_defs.h"
+#include "rda5890_dev.h"
+#include "rda5890_ioctl.h"
+#include "rda5890_wid.h"
+#include "rda5890_wext.h"
+#include "rda5890_txrx.h"
+#include "rda5890_if_sdio.h"
+
+int rda5890_sleep_flags = RDA_SLEEP_ENABLE | RDA_SLEEP_PREASSO;
+
+#ifdef WIFI_UNLOCK_SYSTEM
+atomic_t   wake_lock_counter;
+struct wake_lock sleep_worker_wake_lock;
+#endif
+
+
+int rda5890_init_pm(struct rda5890_private *priv)
+{
+#ifdef WIFI_POWER_MANAGER
+    int ret = 0;
+    struct if_sdio_card  *card =  (struct if_sdio_card  *)priv->card;
+    
+#ifdef WIFI_TEST_MODE
+    if(rda_5990_wifi_in_test_mode())
+        return 0;
+#endif
+    if (rda5890_sleep_flags & RDA_SLEEP_ENABLE)
+    {
+        ret = rda5890_set_pm_mode(priv, 2);
+        if(ret < 0)
+            goto err;
+    }
+    if (rda5890_sleep_flags & RDA_SLEEP_PREASSO)
+    {
+        ret = rda5890_set_preasso_sleep(priv, 0x00800080);
+        if(ret < 0)
+            goto err;
+    }
+
+    sdio_claim_host(card->func);
+    sdio_writeb(card->func, 1, IF_SDIO_FUN1_INT_TO_DEV, &ret);
+    sdio_release_host(card->func);
+    if (ret) {
+                RDA5890_ERRP("write FUN1_INT_TO_DEV reg fail\n");
+        }
+
+    atomic_inc(&priv->sleep_flag);
+
+err:
+    return ret;
+#else
+    return 0;
+#endif
+}
+
+
+int rda5890_disable_self_cts(struct rda5890_private *priv)
+{
+    int ret = 0;
+    
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+       "Set rda5890_disable_self_cts 0x%02x\n", 0);
+    
+       ret = rda5890_generic_set_uchar(priv, WID_PTA_MODE, 0);
+        if(ret < 0)
+            goto err;
+       return 0;
+  
+err:
+    return ret;
+}
+
+int rda5890_disable_block_bt(struct rda5890_private *priv)
+{
+    int ret = 0;
+
+    RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+       "Set rda5890_disable_block_bt 0x%02x\n", 0);
+    
+       ret = rda5890_generic_set_uchar(priv, WID_PTA_BLOCK_BT, 0);
+        if(ret < 0)
+            goto err;
+        
+       return 0;
+  
+err:
+    return ret;
+}
+
+//rda5890_set_scan_timeout has defined WID_ACTIVE_SCAN_TIME so if you call that func, not need call this
+int rda5890_set_active_scan_time(struct rda5890_private *priv)
+{
+    int ret = 0;
+       ret= rda5890_generic_set_ushort(priv, WID_ACTIVE_SCAN_TIME, 200);
+        if(ret < 0)
+            goto err;
+       return 0;
+  err:
+    return ret;
+}
+
+/**
+ *  @brief This function opens the ethX or mshX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return       0 or -EBUSY if monitor mode active
+ */
+static int rda5890_dev_open(struct net_device *dev)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int ret = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       if (priv->connect_status == MAC_CONNECTED)
+               netif_carrier_on(dev);
+       else
+               netif_carrier_off(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+       return ret;
+}
+
+/**
+ *  @brief This function closes the ethX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return       0
+ */
+static int rda5890_eth_stop(struct net_device *dev)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *) dev->priv;
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       netif_stop_queue(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+       return 0;
+}
+
+static void rda5890_tx_timeout(struct net_device *dev)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *) dev->priv;
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+}
+
+/**
+ *  @brief This function returns the network statistics
+ *
+ *  @param dev     A pointer to struct lbs_private structure
+ *  @return       A pointer to net_device_stats structure
+ */
+static struct net_device_stats *rda5890_get_stats(struct net_device *dev)
+{
+       struct rda5890_private *priv = (struct rda5890_private *) netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+       return &priv->stats;
+}
+
+static int rda5890_set_mac_address(struct net_device *dev, void *addr)
+{
+       int ret = 0;
+       struct rda5890_private *priv = (struct rda5890_private *) netdev_priv(dev);
+       struct sockaddr *phwaddr = addr;
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       ret = rda5890_set_mac_addr(priv, phwaddr->sa_data);
+       if (ret) {
+               goto done;
+       }
+       memcpy(priv->dev->dev_addr, phwaddr->sa_data, ETH_ALEN); 
+
+done:
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+       return ret;
+}
+
+static void rda5890_set_multicast_list(struct net_device *dev)
+{
+       //struct rda5890_private *priv = dev->priv;
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       //schedule_work(&priv->mcast_work);
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+}
+
+/**
+ *  @brief This function checks the conditions and sends packet to IF
+ *  layer if everything is ok.
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @param skb     A pointer to skb which includes TX packet
+ *  @return       0 or -1
+ */
+int rda5890_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct rda5890_private *priv = (struct rda5890_private*)netdev_priv(dev);
+#ifdef WIFI_TEST_MODE
+  if(rda_5990_wifi_in_test_mode())
+      return 0;
+#endif  //end WIFI_TEST_MODE
+       return rda5890_data_tx(priv, skb, dev);
+}
+
+static int rda5890_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       int ret = 0;
+       struct rda5890_private *priv = netdev_priv(dev);
+       unsigned long value;
+       char in_buf[MAX_CMD_LEN + 4], out_buf[MAX_CMD_LEN + 4];
+       unsigned short in_len, out_len, out_len_ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>, cmd = %x\n", __func__, cmd);
+
+       switch (cmd) {
+       case IOCTL_RDA5890_GET_MAGIC:
+               RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+                       "IOCTL_RDA5890_GET_MAGIC\n");
+               value = RDA5890_MAGIC;
+               if (copy_to_user(rq->ifr_data, &value, sizeof(value)))
+                       ret = -EFAULT; 
+               break;  
+       case IOCTL_RDA5890_GET_DRIVER_VER:
+               RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+                       "IOCTL_RDA5890_GET_DRIVER_VER\n");
+               value = RDA5890_SDIOWIFI_VER_MAJ << 16 | 
+                       RDA5890_SDIOWIFI_VER_MIN << 8 |
+                       RDA5890_SDIOWIFI_VER_BLD;
+               if (copy_to_user(rq->ifr_data, &value, sizeof(value)))
+                       ret = -EFAULT; 
+               break;
+       case IOCTL_RDA5890_MAC_GET_FW_VER:
+               RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+                       "IOCTL_RDA5890_MAC_GET_FW_VER\n");
+               ret = rda5890_get_fw_ver(priv, &value);
+               if (ret)
+                       break;
+               if (copy_to_user(rq->ifr_data, &value, sizeof(value)))
+                       ret = -EFAULT; 
+               break;
+       case IOCTL_RDA5890_MAC_WID:
+               RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+                       "IOCTL_RDA5890_MAC_WID\n");
+               if (copy_from_user(in_buf, rq->ifr_data, 4)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               in_len = (unsigned short)(in_buf[0] + (in_buf[1] << 8));
+               out_len = (unsigned short)(in_buf[2] + (in_buf[3] << 8));
+               RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+                       " in_len = %d, out_len = %d\n", in_len, out_len);
+               if (copy_from_user(in_buf, rq->ifr_data, 4 + in_len)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               out_len_ret = MAX_CMD_LEN;
+               ret = rda5890_wid_request(priv, in_buf + 4, in_len, out_buf + 4, &out_len_ret);
+               if (ret) {
+                       RDA5890_ERRP("rda5890_wid_request, ret = %d\n", ret);
+                       break;
+               }
+               RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+                       "  out_len_ret = %d\n", out_len_ret);
+               if (out_len_ret > out_len) {
+                       RDA5890_ERRP("No enough buf for wid response\n");
+                       ret = -ENOMEM;
+                       break;
+               }
+               out_buf[2] = (char)(out_len_ret&0x00FF);
+               out_buf[3] = (char)((out_len_ret&0xFF00) >> 8);
+               if (copy_to_user(rq->ifr_data, out_buf, 4 + out_len_ret))
+                       ret = -EFAULT; 
+               break;
+       case IOCTL_RDA5890_SET_WAPI_ASSOC_IE:
+               if(copy_from_user(in_buf, rq->ifr_data, 100)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+                       "IOCTL_RDA5890_SET_WAPI_ASSOC_IE is  %x %x %x %x \n",
+                       in_buf[0], in_buf[1],in_buf[2],in_buf[3]);
+               rda5890_generic_set_str(priv, WID_WAPI_ASSOC_IE, in_buf ,100);
+               break;
+       case IOCTL_RDA5890_GET_WAPI_ASSOC_IE:
+               rda5890_generic_get_str(priv, WID_WAPI_ASSOC_IE, out_buf, 100);
+               if (copy_to_user(rq->ifr_data, out_buf, 100))
+               ret = -EFAULT; 
+               break;
+       default:
+               RDA5890_ERRP("unknown cmd 0x%x\n", cmd);
+               ret = -EFAULT; 
+               break;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+       return ret;
+} 
+
+static int rda5890_init_adapter(struct rda5890_private *priv)
+{
+       int ret = 0;
+       size_t bufsize;
+       int i;
+    
+       mutex_init(&priv->wid_lock);
+#ifdef WIFI_POWER_MANAGER    
+       atomic_set(&priv->sleep_flag, 0);
+#endif
+       priv->wid_pending = 0;
+       priv->wid_msg_id = 0;
+
+       /* Allocate buffer to store the BSSID list */
+       bufsize = RDA5890_MAX_NETWORK_NUM * sizeof(struct bss_descriptor);
+       priv->networks = kzalloc(bufsize, GFP_KERNEL);
+       if (!priv->networks) {
+               RDA5890_ERRP("Out of memory allocating beacons\n");
+               ret = -1;
+               goto out;
+       }
+
+       /* Initialize scan result lists */
+       INIT_LIST_HEAD(&priv->network_free_list);
+       INIT_LIST_HEAD(&priv->network_list);
+       for (i = 0; i < RDA5890_MAX_NETWORK_NUM; i++) {
+               list_add_tail(&priv->networks[i].list,
+                             &priv->network_free_list);
+       }
+       priv->scan_running = 0;
+
+       /* Initialize delayed work workers and thread */
+       priv->work_thread = create_singlethread_workqueue("rda5890_worker");
+       INIT_DELAYED_WORK(&priv->scan_work, rda5890_scan_worker);
+       INIT_DELAYED_WORK(&priv->assoc_work, rda5890_assoc_worker);
+       INIT_DELAYED_WORK(&priv->assoc_done_work, rda5890_assoc_done_worker);
+    INIT_DELAYED_WORK(&priv->wlan_connect_work, rda5890_wlan_connect_worker);
+
+       /* Initialize status */
+       priv->connect_status = MAC_DISCONNECTED;
+       memset(&priv->curbssparams, 0, sizeof(priv->curbssparams));
+       memset(&priv->wstats, 0, sizeof(priv->wstats));
+
+       /* Initialize sec related status */
+       priv->assoc_flags = 0;
+       priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+       memset(&priv->wep_keys, 0, sizeof(priv->wep_keys));
+       memset(&priv->wpa_ie[0], 0, sizeof(priv->wpa_ie));
+       priv->wpa_ie_len = 0;
+    priv->first_init = 1;
+
+out:
+       return ret;
+}
+
+static void rda5890_free_adapter(struct rda5890_private *priv)
+{
+       cancel_delayed_work_sync(&priv->assoc_done_work);
+       cancel_delayed_work_sync(&priv->assoc_work);
+       cancel_delayed_work_sync(&priv->scan_work);
+    cancel_delayed_work_sync(&priv->wlan_connect_work);
+       destroy_workqueue(priv->work_thread);
+
+       if(priv->networks)
+           kfree(priv->networks);
+       priv->networks = NULL;
+}
+
+static const struct net_device_ops rda_netdev_ops = {
+       .ndo_open               = rda5890_dev_open,
+       .ndo_stop               = rda5890_eth_stop,
+       .ndo_start_xmit         = rda5890_hard_start_xmit,
+       .ndo_set_mac_address    = rda5890_set_mac_address,
+       .ndo_tx_timeout         = rda5890_tx_timeout,
+       .ndo_get_stats = rda5890_get_stats,
+       .ndo_do_ioctl = rda5890_ioctl
+};
+
+/**
+ * @brief This function adds the card. it will probe the
+ * card, allocate the lbs_priv and initialize the device.
+ *
+ *  @param card    A pointer to card
+ *  @return       A pointer to struct lbs_private structure
+ */
+struct rda5890_private *rda5890_add_card(void *card)
+{
+       struct net_device *dev = NULL;
+       struct rda5890_private *priv = NULL;
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       /* Allocate an Ethernet device and register it */
+       dev = alloc_netdev_mq(sizeof(struct rda5890_private), "wlan%d", ether_setup, 1);
+       if (!dev) {
+               RDA5890_ERRP("alloc_etherdev failed\n");
+               goto done;
+       }
+       priv = netdev_priv(dev);
+
+       if(rda5890_init_adapter(priv)){
+               RDA5890_ERRP("rda5890_init_adapter failed\n");
+               goto err_init_adapter;
+       }
+
+       priv->dev = dev;
+       priv->card = card;
+
+       /* Setup the OS Interface to our functions */
+       dev->netdev_ops = &rda_netdev_ops;
+       dev->watchdog_timeo = msecs_to_jiffies(450); //450ms
+
+       //dev->ethtool_ops = &lbs_ethtool_ops;
+#ifdef WIRELESS_EXT
+       dev->wireless_handlers = (struct iw_handler_def *)&rda5890_wext_handler_def;
+#endif
+       dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+
+       goto done;
+
+err_init_adapter:
+    if(priv)
+           rda5890_free_adapter(priv);
+    if(dev)
+           free_netdev(dev);
+       priv = NULL;
+
+done:
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+       return priv;
+}
+
+void rda5890_remove_card(struct rda5890_private *priv)
+{
+       struct net_device *dev = priv->dev;
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       dev = priv->dev;
+
+       rda5890_free_adapter(priv);
+
+       priv->dev = NULL;
+    if(dev)
+           free_netdev(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+}
+
+int rda5890_start_card(struct rda5890_private *priv)
+{
+       struct net_device *dev = priv->dev;
+       int ret = 0;
+    unsigned char mac_addr[ETH_ALEN];
+
+    RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+       "%s <<<\n", __func__);
+
+#ifdef WIFI_TEST_MODE
+    if(!rda_5990_wifi_in_test_mode())
+    {
+#endif
+
+#ifdef USE_MAC_DYNAMIC_ONCE
+        if(rda5890_read_mac(mac_addr) != ETH_ALEN)
+        {
+            random_ether_addr(mac_addr);
+            rda5890_write_mac(mac_addr);
+        }
+
+#else
+        mac_addr[0] = 0x00;
+        mac_addr[1] = 0xc0;
+        mac_addr[2] = 0x52;
+
+        mac_addr[3] = 0x00;
+        mac_addr[4] = 0xc0;
+        mac_addr[5] = 0x53;    
+#endif
+       ret = rda5890_set_mac_addr(priv, mac_addr);
+       if (ret) 
+        {
+            goto done;
+        }        
+
+       ret = rda5890_get_mac_addr(priv, mac_addr);
+       if (ret) {
+               goto done;
+       }
+       memcpy(priv->dev->dev_addr, mac_addr, ETH_ALEN); 
+
+#ifdef WIFI_TEST_MODE
+    }
+#endif
+
+       if (register_netdev(dev)) {
+               RDA5890_ERRP("register_netdev failed\n");
+               goto done;
+       }
+    
+#ifdef WIFI_TEST_MODE
+    if(!rda_5990_wifi_in_test_mode())
+    {
+#endif
+
+        rda5890_set_preamble(priv, G_AUTO_PREAMBLE);
+        
+       rda5890_indicate_disconnected(priv);
+#ifdef WIFI_TEST_MODE
+    }
+#endif
+
+
+done:
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+       return ret;
+}
+
+void rda5890_stop_card(struct rda5890_private *priv)
+{
+       struct net_device *dev = priv->dev;
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       rda5890_indicate_disconnected(priv);
+       unregister_netdev(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+}
+
+void rda5890_shedule_timeout(int msecs)
+{
+    int timeout = 0, expires = 0;
+    expires = jiffies + msecs_to_jiffies(msecs);
+    timeout = msecs;
+
+    while(timeout)
+    {
+        timeout = schedule_timeout(timeout);
+        
+        if(time_after(jiffies, expires))
+            break;
+    }
+}
+
+#ifdef WIFI_UNLOCK_SYSTEM
+
+void rda5990_wakeLock(void)
+{
+    if(atomic_read(&wake_lock_counter) == 0)
+        wake_lock(&sleep_worker_wake_lock);
+    atomic_inc(&wake_lock_counter);
+}
+
+void rda5990_wakeUnlock(void)
+{
+
+    if(atomic_read(&wake_lock_counter) == 1)
+    {
+        atomic_set(&wake_lock_counter, 0);
+        wake_unlock(&sleep_worker_wake_lock);        
+    }
+    else if(atomic_read(&wake_lock_counter) > 0)
+    {
+        atomic_dec(&wake_lock_counter);
+    }
+    
+}
+
+void rda5990_wakeLock_destroy(void)
+{
+    if(atomic_read(&wake_lock_counter) > 0)
+    {
+        atomic_set(&wake_lock_counter, 0);
+        wake_unlock(&sleep_worker_wake_lock);  
+    }
+    
+    wake_lock_destroy(&sleep_worker_wake_lock);
+    
+}
+
+#endif
+
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.h b/drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.h
new file mode 100755 (executable)
index 0000000..1f36567
--- /dev/null
@@ -0,0 +1,272 @@
+/**
+  * This file contains definitions and data structures specific
+  * to Marvell 802.11 NIC. It contains the Device Information
+  * structure struct lbs_private..
+  */
+#ifndef _RDA5890_DEV_H_
+#define _RDA5890_DEV_H_
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/wakelock.h>
+#include "rda5890_defs.h"
+//#include "hif_sdio.h"
+
+
+#ifndef MAX_WPA_IE_LEN
+#define MAX_WPA_IE_LEN 100
+#endif
+
+#ifndef MAX_RATES
+#define MAX_RATES                      14
+#endif
+
+#define DEV_NAME_LEN                   32
+
+#define RDA5890_MAX_NETWORK_NUM    32
+
+/** RSSI related MACRO DEFINITIONS */
+#define RDA5890_NF_DEFAULT_SCAN_VALUE          (-96)
+#define PERFECT_RSSI ((u8)50)
+#define WORST_RSSI   ((u8)0)
+#define RSSI_DIFF    ((u8)(PERFECT_RSSI - WORST_RSSI))
+
+/** RTS/FRAG related defines */
+#define RDA5890_RTS_MIN_VALUE          0
+#define RDA5890_RTS_MAX_VALUE          2347
+#define RDA5890_FRAG_MIN_VALUE         256
+#define RDA5890_FRAG_MAX_VALUE         2346
+
+#define KEY_LEN_WPA_AES                        16
+#define KEY_LEN_WPA_TKIP               32
+#define KEY_LEN_WEP_104                        13
+#define KEY_LEN_WEP_40                 5
+
+#define BIT7                    (1 << 7)
+#define BIT6                    (1 << 6)
+#define BIT5                    (1 << 5)
+#define BIT4                    (1 << 4)
+#define BIT3                    (1 << 3)
+#define BIT2                    (1 << 2)
+#define BIT1                    (1 << 1)
+#define BIT0                    (1 << 0)
+
+#define RDA_SLEEP_ENABLE        BIT0
+#define RDA_SLEEP_PREASSO       BIT1
+
+//#define WIFI_UNIT_TEST
+
+/** KEY_TYPE_ID */
+enum KEY_TYPE_ID {
+       KEY_TYPE_ID_WEP = 0,
+       KEY_TYPE_ID_TKIP,
+       KEY_TYPE_ID_AES
+};
+
+enum PACKET_TYPE{
+    WID_REQUEST_PACKET,
+    WID_REQUEST_POLLING_PACKET,
+    DATA_REQUEST_PACKET
+};
+
+/** KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */
+enum KEY_INFO_WPA {
+       KEY_INFO_WPA_MCAST = 0x01,
+       KEY_INFO_WPA_UNICAST = 0x02,
+       KEY_INFO_WPA_ENABLED = 0x04
+};
+
+/* RDA5890 defined bss descriptor, 44 bytes for each bss */
+struct rda5890_bss_descriptor {
+       u8 ssid[IW_ESSID_MAX_SIZE + 1];
+       u8 bss_type;
+       u8 channel;
+       u8 dot11i_info;
+       u8 bssid[ETH_ALEN];
+       u8 rssi;
+       u8 auth_info;
+       u8 rsn_cap[2];
+} __attribute__ ((packed));
+
+/**
+ *  @brief Structure used to store information for each beacon/probe response
+ */
+struct bss_descriptor {
+       struct rda5890_bss_descriptor data;
+
+       u8 bssid[ETH_ALEN];
+
+       u8 ssid[IW_ESSID_MAX_SIZE + 1];
+       u8 ssid_len;
+
+       u16 capability;
+       u32 rssi;
+       u32 channel;
+       u16 beaconperiod;
+
+       /* IW_MODE_AUTO, IW_MODE_ADHOC, IW_MODE_INFRA */
+       u8 mode;
+
+       /* zero-terminated array of supported data rates */
+       u8 rates[MAX_RATES + 1];    
+       u8 wpa_ie[MAX_WPA_IE_LEN];
+       size_t wpa_ie_len;
+       u8 rsn_ie[MAX_WPA_IE_LEN];
+       size_t rsn_ie_len;
+
+      u8 wapi_ie[100];
+      size_t wapi_ie_len; //wapi valid payload length
+
+       unsigned long last_scanned;
+
+
+       struct list_head list;
+};
+
+/* Generic structure to hold all key types. */
+struct enc_key {
+       u16 len;
+       u16 flags;  /* KEY_INFO_* from defs.h */
+       u16 type; /* KEY_TYPE_* from defs.h */
+       u8 key[32];
+};
+
+struct rda5890_802_11_security {
+       u8 WPAenabled;
+       u8 WPA2enabled;
+       u8 wep_enabled;
+       u8 auth_mode;
+       u32 key_mgmt;
+    u32 cipther_type;
+};
+
+/** Private structure for the rda5890 device */
+struct rda5890_private {
+       char name[DEV_NAME_LEN];
+
+       void *card;
+       struct net_device *dev;
+
+       struct net_device_stats stats;
+
+       /** current ssid/bssid related parameters and status */
+       int connect_status;
+       struct rda5890_bss_descriptor curbssparams;
+       struct iw_statistics wstats; 
+
+       /** association flags */
+       unsigned char assoc_ssid[IW_ESSID_MAX_SIZE + 1];
+    unsigned char assoc_bssid[6];
+       unsigned char assoc_ssid_len; 
+#define ASSOC_FLAG_SSID                        1
+#define ASSOC_FLAG_CHANNEL             2
+#define ASSOC_FLAG_BAND                        3
+#define ASSOC_FLAG_MODE                        4
+#define ASSOC_FLAG_BSSID               5
+#define ASSOC_FLAG_WEP_KEYS            6
+#define ASSOC_FLAG_WEP_TX_KEYIDX       7
+#define ASSOC_FLAG_WPA_MCAST_KEY       8
+#define ASSOC_FLAG_WPA_UCAST_KEY       9
+#define ASSOC_FLAG_SECINFO             10
+#define ASSOC_FLAG_WPA_IE              11
+#define ASSOC_FLAG_ASSOC_RETRY         12
+#define ASSOC_FLAG_ASSOC_START  13
+#define ASSOC_FLAG_WLAN_CONNECTING  14
+
+       unsigned long assoc_flags;
+       unsigned char imode;
+       unsigned char authtype;
+
+       /** debugfs */
+       struct dentry *debugfs_dir;
+       struct dentry *debugfs_files[6];
+
+       /** for wid request and response */
+       struct mutex wid_lock;
+       struct completion wid_done;
+       int wid_pending;
+       char *wid_rsp;
+       unsigned short wid_rsp_len;
+       char wid_msg_id;
+
+       /** delayed worker */
+       struct workqueue_struct *work_thread;
+       struct delayed_work scan_work;
+       struct delayed_work assoc_work;
+       struct delayed_work assoc_done_work;
+    struct delayed_work   wlan_connect_work;
+
+       /** Hardware access */
+       int (*hw_host_to_card) (struct rda5890_private *priv, u8 *payload, u16 nb, 
+                                   unsigned char packet_type);
+
+       /** Scan results list */
+       int scan_running;
+       struct list_head network_list;
+       struct list_head network_free_list;
+       struct bss_descriptor *networks;
+
+       /** Encryption parameter */
+       struct rda5890_802_11_security secinfo;
+
+       /** WEP keys */
+       struct enc_key wep_keys[4];
+       u16 wep_tx_keyidx;
+
+       /** WPA keys */
+       struct enc_key wpa_mcast_key;
+       struct enc_key wpa_unicast_key;
+
+       /** WPA Information Elements*/
+       u8 wpa_ie[MAX_WPA_IE_LEN];
+       u8 wpa_ie_len;
+
+       u8 is_wapi;
+       /** sleep/awake flag */
+#ifdef WIFI_POWER_MANAGER        
+       atomic_t sleep_flag;
+#endif
+    u8  first_init;
+    u16 version;
+};
+
+extern int rda5890_sleep_flags;
+
+#ifdef WIFI_TEST_MODE
+extern unsigned char rda_5990_wifi_in_test_mode(void);
+#endif
+
+#ifdef WIFI_UNLOCK_SYSTEM
+extern atomic_t     wake_lock_counter;
+extern struct wake_lock sleep_worker_wake_lock;
+extern void rda5990_wakeLock(void);
+extern void rda5990_wakeUnlock(void);
+extern void rda5990_wakeLock_destroy(void);
+#endif
+
+struct rda5890_private *rda5890_add_card(void *card);
+void rda5890_remove_card(struct rda5890_private *priv);
+int  rda5890_start_card(struct rda5890_private *priv);
+void rda5890_stop_card(struct rda5890_private *priv);
+int rda5890_sdio_core_wake_mode(struct rda5890_private *priv);
+int rda5890_sdio_core_sleep_mode(struct rda5890_private *priv);
+int rda5890_sdio_core_wake_mode_polling(struct rda5890_private *priv);
+
+void rda5890_sdio_set_notch_by_channel(struct rda5890_private *priv, unsigned int channel);
+void rda5890_rssi_up_to_200(struct rda5890_private *priv);
+void rda5990_assoc_power_save(struct rda5890_private *priv);
+unsigned char is_sdio_init_complete(void);
+unsigned char is_sdio_patch_complete(void);
+int rda5890_sdio_init(struct rda5890_private *priv);
+int rda5890_init_pm(struct rda5890_private *priv);
+void rda5890_shedule_timeout(int msecs);
+int rda5890_read_mac(char* buf);
+int rda5890_write_mac(char * buf);
+int rda5890_disable_block_bt(struct rda5890_private *priv);
+int rda5890_disable_self_cts(struct rda5890_private *priv);
+int rda5890_set_active_scan_time(struct rda5890_private *priv);
+int rda5890_set_test_mode(struct rda5890_private *priv);
+int rda5890_get_fw_version_polling(struct rda5890_private *priv, unsigned int* version);
+#endif
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.c b/drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.c
new file mode 100755 (executable)
index 0000000..ff738d2
--- /dev/null
@@ -0,0 +1,795 @@
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/sched.h>
+
+#include "rda5890_defs.h"
+#include "rda5890_dev.h"
+#include "rda5890_if_sdio.h"
+#include "rda5890_wid.h"
+#include "rda5890_debugfs.h"
+
+int rda5890_dbg_level = RDA5890_DL_CRIT;
+int rda5890_dbg_area = RDA5890_DA_MAIN
+       | RDA5890_DA_SDIO
+       //| RDA5890_DA_ETHER
+       | RDA5890_DA_WID
+       | RDA5890_DA_WEXT
+       //| RDA5890_DA_TXRX
+       //| RDA5890_DA_PM
+       ;
+
+void export_msdc_clk_always_on(void)
+{
+    // todo...
+}
+
+void export_msdc_clk_always_on_off(void)
+{
+       // todo...
+}
+
+extern void rk29_sdio_irq_enable(int enable);
+void export_wifi_eirq_enable(void)
+{
+       // todo...
+       rk29_sdio_irq_enable(1);
+}
+
+void export_wifi_eirq_disable(void)
+{
+       // todo...
+       rk29_sdio_irq_enable(0);
+}
+
+/* Module parameters */
+module_param_named(debug_level, rda5890_dbg_level, int, 0644); 
+module_param_named(debug_area, rda5890_dbg_area, int, 0644); 
+int sdio_test_flag = 0;
+module_param_named(sdio_test, sdio_test_flag, int, 0644); 
+
+#define SDIO_VENDOR_ID_RDA5890        0x5449
+#define SDIO_DEVICE_ID_RDA5890        0x0145
+
+static const struct sdio_device_id if_sdio_ids[] = {
+       { SDIO_DEVICE(SDIO_VENDOR_ID_RDA5890, SDIO_DEVICE_ID_RDA5890) },
+       { /* end: all zeroes */                                         },
+};
+
+MODULE_DEVICE_TABLE(sdio, if_sdio_ids);
+
+struct if_sdio_packet {
+       struct if_sdio_packet   *next;
+    unsigned char packet_type;
+       u16                     nb;
+       u8                      buffer[0] __attribute__((aligned(4)));
+};
+
+int if_sdio_card_to_host(struct if_sdio_card *card)
+{
+       int ret = 0;
+       struct rda5890_private *priv = card->priv;
+       u8 size_l = 0, size_h = 0;
+       u16 size, chunk;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s <<< \n", __func__);
+
+       size_l = sdio_readb(card->func, IF_SDIO_AHB2SDIO_PKTLEN_L, &ret);
+       if (ret) {
+               RDA5890_ERRP("read PKTLEN_L reg fail\n");
+               goto out;
+       }
+       else
+               RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_NORM,"read PKTLEN_L reg size_l:%d \n", size_l);
+
+       size_h = sdio_readb(card->func, IF_SDIO_AHB2SDIO_PKTLEN_H, &ret);
+       if (ret) {
+               RDA5890_ERRP("read PKTLEN_H reg fail\n");
+               goto out;
+       }
+       else
+               RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_NORM,"read PKTLEN_H reg size_h:%d\n",size_h);        
+
+       size = (size_l | ((size_h & 0x7f) << 8)) * 4;
+       if (size < 4) {
+               RDA5890_ERRP("invalid packet size (%d bytes) from firmware\n", size);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* alignment is handled on firmside */
+       //chunk = sdio_align_size(card->func, size);
+       chunk = size;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_NORM,
+               "if_sdio_card_to_host, size = %d, aligned size = %d\n", size, chunk);
+
+       /* TODO: handle multiple packets here */
+       ret = sdio_readsb(card->func, card->buffer, IF_SDIO_FUN1_FIFO_RD, chunk);
+       if (ret) {
+               RDA5890_ERRP("sdio_readsb fail, ret = %d\n", ret);
+               goto out;
+       }
+
+#if 1
+       if(priv->version == 7)
+       {
+               sdio_writeb(card->func, 0x20 ,IF_SDIO_FUN1_INT_PEND, &ret);
+               if(ret) {
+                       RDA5890_ERRP("clear SDIO Tx Complete flag failed\n");
+                       goto out;
+               }
+       }
+#endif
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_VERB,
+               "if_sdio_card_to_host, read done\n");
+
+       if (sdio_test_flag) {
+               rda5890_sdio_test_card_to_host(card->buffer, chunk);
+               goto out;
+       }
+
+       /* TODO: this chunk size need to be handled here */
+       rda5890_card_to_host(priv, card->buffer, chunk);
+
+out:
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "if_sdio_card_to_host >>>\n");
+       return ret;
+}
+
+#ifdef WIFI_POWER_MANAGER
+void if_sdio_sleep_worker(struct work_struct *work)
+{
+       struct if_sdio_card *card = NULL;
+       int ret = 0;
+    struct rda5890_private *priv = NULL;
+
+       card = container_of(work, struct if_sdio_card,
+                sleep_work.work);
+       
+       if(card)
+               priv = card->priv;
+       else
+               goto out;
+
+       if(!priv)
+               goto out;
+
+       RDA5890_DBGLAP(RDA5890_DA_PM, RDA5890_DL_CRIT, "Sleep\n");
+
+       /* clear IF_SDIO_FUN1_INT_PEND, this allow device to sleep */
+    sdio_claim_host(card->func);
+       sdio_writeb(card->func, IF_SDIO_HOST_TX_FLAG ,IF_SDIO_FUN1_INT_PEND, &ret);
+    sdio_release_host(card->func);
+       if (ret)
+       {
+               RDA5890_ERRP("clear IF_SDIO_HOST_TX_FLAG failed\n");
+       }
+       atomic_inc(&card->priv->sleep_flag); 
+
+out:
+
+    atomic_set(&card->sleep_work_is_active, 0);
+#ifdef WIFI_UNLOCK_SYSTEM
+    rda5990_wakeUnlock();
+#endif
+       return;
+}
+
+static int if_sdio_wakeup_card(struct if_sdio_card *card)
+{
+       struct rda5890_private *priv = card->priv;
+       int ret = 0;
+
+#ifdef WIFI_TEST_MODE
+    if(rda_5990_wifi_in_test_mode())
+        return 0;
+#endif
+      sdio_claim_host(card->func);
+      sdio_writeb(card->func, 1, IF_SDIO_FUN1_INT_TO_DEV, &ret);
+      sdio_release_host(card->func);
+       if (ret) {
+               RDA5890_ERRP("write FUN1_INT_TO_DEV reg fail\n");
+               goto out;
+       }
+    atomic_set(&priv->sleep_flag, 0);
+      
+       RDA5890_DBGLAP(RDA5890_DA_PM, RDA5890_DL_CRIT, "wake up \n");
+       // wait 15ms, hardware need 13ms to wakeup
+    rda5890_shedule_timeout(8);
+out:
+       return ret;
+}
+
+#endif //WIFI_POWER_MANAGER
+
+static void if_sdio_host_to_card_worker(struct work_struct *work)
+{
+       struct if_sdio_card *card = NULL;
+       struct rda5890_private *priv = NULL;
+       struct if_sdio_packet *packet = NULL;
+       int ret;
+       unsigned long flags;
+       u16 size;
+    u32 retries = 500;
+       u8 size_l, size_h, write_status = 0;
+#define SDIO_HOST_WRITE_BATCH_SIZE   512
+       u16 bytes_left, offset, batch;
+
+       card = container_of(work, struct if_sdio_card, packet_worker);
+       priv = card->priv;
+
+
+#ifdef WIFI_POWER_MANAGER    
+    if(is_sdio_init_complete())
+    {   
+        if(atomic_read(&card->sleep_work_is_active))
+        {
+            cancel_delayed_work(&card->sleep_work);
+#ifdef WIFI_UNLOCK_SYSTEM    
+            rda5990_wakeUnlock();
+#endif 
+            atomic_set(&card->sleep_work_is_active, 0);
+        }
+    }
+#endif
+
+       while (1) 
+    {  
+       retries = 500;
+         
+        spin_lock_irqsave(&card->lock, flags);
+        packet = card->packets;
+        if (packet)
+               card->packets = packet->next;
+        spin_unlock_irqrestore(&card->lock, flags);
+
+        if (!packet)
+               break;
+                       
+#ifdef WIFI_POWER_MANAGER              
+               if (atomic_read(&priv->sleep_flag)) 
+        {
+            /* Deivce maybe sleep, wakeup it. */
+            RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_NORM, "Wakeup\n");
+            ret = if_sdio_wakeup_card(card);
+            if (ret) 
+            {
+                RDA5890_ERRP("wakeup card fail\n");
+                goto out;
+            }
+        }
+#endif
+       while(retries && is_sdio_patch_complete()) //check write flow ctrl
+       {
+            sdio_claim_host(card->func);
+            write_status = sdio_readb(card->func, IF_SDIO_FUN1_INT_PEND, &ret);
+            sdio_release_host(card->func);
+            if(ret)
+            {
+                RDA5890_ERRP("read IF_SDIO_FUN1_INT_PEND failed\n");
+                goto release;
+            }
+            if((write_status & IF_SDIO_INT_RXCMPL) == 0)
+            {
+                       //RDA5890_ERRP("**** sdio is busy retry next time \n ");
+                       retries --;
+                       schedule();
+            }
+            else
+            {
+                sdio_claim_host(card->func);
+                sdio_writeb(card->func, IF_SDIO_INT_RXCMPL, IF_SDIO_FUN1_INT_PEND, &ret);
+                sdio_release_host(card->func);
+                if(ret)
+                {
+                        RDA5890_ERRP("write IF_SDIO_FUN1_INT_PEND failed\n");
+                        goto release;
+                }
+                break;
+            }
+        }
+        
+               RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                       "if_sdio_host_to_card_worker, send one packet, size = %d\n", packet->nb);
+               /* write length */
+               size = packet->nb/4;
+               size_l = (u8)(size & 0xff);
+               size_h = (u8)((size >> 8) & 0x7f);
+               size_h |= 0x80;
+
+      sdio_claim_host(card->func);
+       sdio_writeb(card->func, size_l, IF_SDIO_SDIO2AHB_PKTLEN_L, &ret);
+      sdio_release_host(card->func);
+       if (ret) {
+               RDA5890_ERRP("write PKTLEN_L reg fail\n");
+               goto release;
+       }
+        sdio_claim_host(card->func);
+       sdio_writeb(card->func, size_h, IF_SDIO_SDIO2AHB_PKTLEN_H, &ret);
+        sdio_release_host(card->func);
+       if (ret) {
+               RDA5890_ERRP("write PKTLEN_H reg fail\n");
+               goto release;
+       }
+
+               /* write data */
+               bytes_left = packet->nb;
+               offset = 0;
+               while(bytes_left) 
+        {
+                       batch = (bytes_left < SDIO_HOST_WRITE_BATCH_SIZE)?
+                               bytes_left:SDIO_HOST_WRITE_BATCH_SIZE;
+        
+            sdio_claim_host(card->func);
+               ret = sdio_writesb(card->func, IF_SDIO_FUN1_FIFO_WR,
+                       packet->buffer + offset, batch);
+            sdio_release_host(card->func);
+               if (ret) {
+                       RDA5890_ERRP("sdio_writesb fail, ret = %d\n", ret);
+                       goto release;
+               }
+        
+               RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                       "write batch %d, offset = %d\n", batch, offset);
+        
+               offset += batch;
+               bytes_left -= batch;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "if_sdio_host_to_card_worker, send one packet done\n");
+        
+release:
+               kfree(packet);
+        packet = NULL;
+       }
+    
+out:
+    
+       if(is_sdio_init_complete()) //init complete should start sleep_work
+    {
+#ifdef WIFI_UNLOCK_SYSTEM    
+        rda5990_wakeLock();
+#endif   
+
+#ifdef WIFI_POWER_MANAGER
+#ifdef WIFI_TEST_MODE
+        if(rda_5990_wifi_in_test_mode())
+            return;
+#endif  //end WIFI_TEST_MODE
+        atomic_set(&card->sleep_work_is_active, 1);
+           queue_delayed_work(priv->work_thread, &card->sleep_work, HZ/5);  // 100ms
+#endif  //end WIFI_POWER_MANAGER   
+    }
+}
+
+/*******************************************************************/
+/* RDA5890 callbacks                                              */
+/*******************************************************************/
+
+static int if_sdio_host_to_card(struct rda5890_private *priv,
+               u8 *buf, u16 nb, unsigned char packet_type)
+{
+       int ret = 0;
+       struct if_sdio_card *card;
+       struct if_sdio_packet *packet, *cur;
+       u16 size;
+       unsigned long flags;
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       card = priv->card;
+
+       if (nb > (65536 - sizeof(struct if_sdio_packet))) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       //size = sdio_align_size(card->func, nb);
+       size = nb;
+       if(size%4)
+       {
+               size += (4-size%4);
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_NORM,
+               "if_sdio_host_to_card, size = %d, aligned size = %d\n", nb, size);
+
+       packet = kzalloc(sizeof(struct if_sdio_packet) + size,
+                       GFP_ATOMIC);
+       if (!packet) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       packet->next = NULL;
+       packet->nb = size;
+    packet->packet_type = packet_type;
+
+       memcpy(packet->buffer, buf, nb);
+
+       spin_lock_irqsave(&card->lock, flags);
+
+       if (!card->packets)
+               card->packets = packet;
+       else {
+               cur = card->packets;
+               while (cur->next)
+                       cur = cur->next;
+               cur->next = packet;
+       }
+
+       spin_unlock_irqrestore(&card->lock, flags);
+
+    queue_work(card->work_thread, &card->packet_worker);
+
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+
+       ret = 0;
+
+out:
+       return ret;
+}
+
+static void if_sdio_interrupt(struct sdio_func *func)
+{ 
+    int ret = 0;
+    struct if_sdio_card *card;
+    struct rda5890_private *priv;
+    u8 status;
+
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+       "%s >>>\n", __func__); 
+
+    card = sdio_get_drvdata(func);
+    if(!card)
+        return;
+    
+    priv = card->priv;
+       
+    status = sdio_readb(card->func, IF_SDIO_FUN1_INT_STAT, &ret);
+    if (ret)
+       goto out;
+    
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_VERB,
+       "if_sdio_interrupt, status = 0x%02x\n", status);
+
+    if (status & IF_SDIO_INT_AHB2SDIO)
+        if_sdio_card_to_host(card);
+
+    if (status & IF_SDIO_INT_ERROR)
+    {
+        sdio_writeb(card->func, IF_SDIO_INT_ERROR, IF_SDIO_FUN1_INT_PEND, &ret);
+        if (ret) 
+        {
+               RDA5890_ERRP("write FUN1_INT_STAT reg fail\n");
+               goto out;
+        }
+
+         RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+               "%s, INT_ERROR\n", __func__);
+    }
+
+out:
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+       "%s <<< ret=%d \n", __func__, ret);   
+    return ret;
+}
+
+
+static int if_sdio_probe(struct sdio_func *func,
+                                       const struct sdio_device_id *id)
+{   
+    struct if_sdio_card *card = NULL;
+    struct rda5890_private *priv = NULL;
+    struct if_sdio_packet *packet = NULL; 
+    int ret = -1;
+    unsigned long flags;
+
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+       "%s >>>\n", __func__);
+
+       //SMC_MSG("[rda_debug] sunximmc_probe, set clk.\n");
+
+       //mmc_set_clock(mmc, 4000000);
+
+
+    if(id->vendor != 0x5449)
+    {
+           RDA5890_ERRP("rda5890 sdio  not corrent vendor:%x \n", id->vendor);
+           goto out;
+    }
+    
+    card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL);
+    if (!card) {
+       RDA5890_ERRP("kzalloc fail\n");
+       return -ENOMEM;
+    }   
+    
+    card->func = func;
+    spin_lock_init(&card->lock);  
+    atomic_set(&card->wid_complete_flag, 0);
+    INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
+    card->work_thread = create_singlethread_workqueue("rda5890_if_sdio_worker");
+    
+#ifdef WIFI_POWER_MANAGER   
+    atomic_set(&card->sleep_work_is_active, 0);
+    INIT_DELAYED_WORK(&card->sleep_work, if_sdio_sleep_worker); 
+#endif
+
+#ifdef WIFI_UNLOCK_SYSTEM 
+    atomic_set(&wake_lock_counter, 0);
+    wake_lock_init(&sleep_worker_wake_lock, WAKE_LOCK_SUSPEND, "RDA_sleep_worker_wake_lock");
+#endif
+
+    sdio_claim_host(func);
+    ret = sdio_enable_func(func);
+    if (ret) {
+        RDA5890_ERRP("sdio_enable_func fail, ret = %d\n", ret);
+        goto release;
+    }   
+
+    ret = sdio_claim_irq(func, if_sdio_interrupt);
+    if (ret) {
+        RDA5890_ERRP("sdio_claim_irq fail, ret = %d\n", ret);
+        goto disable;
+    }
+
+    sdio_release_host(func);
+    sdio_set_drvdata(func, card);
+    
+    priv = rda5890_add_card(card);
+       if (!priv) {
+               RDA5890_ERRP("rda5890_add_card fail\n");
+               ret = -ENOMEM;
+               goto release_int;
+       }
+    rda5890_debugfs_init_one(priv);
+
+    card->priv = priv;
+    priv->card = card;
+    priv->hw_host_to_card = if_sdio_host_to_card;
+
+    /*
+     * Enable interrupts now that everything is set up
+     */
+    sdio_claim_host(func);
+    sdio_writeb(func, 0x7, IF_SDIO_FUN1_INT_MASK, &ret); 
+    sdio_release_host(func);
+    if (ret) {
+       RDA5890_ERRP("enable func interrupt fail\n");
+       goto remove_card;
+    }
+#ifdef WIFI_TEST_MODE
+    if(!rda_5990_wifi_in_test_mode())
+        {
+#endif        
+            ret = rda5890_sdio_init(priv);
+            if(ret < 0)
+                goto remove_card;
+            
+               ret=rda5890_disable_self_cts(priv);   
+            if(ret < 0)
+                goto remove_card;
+
+               ret=rda5890_disable_block_bt(priv);
+            if(ret < 0)
+                goto remove_card;
+
+            ret = rda5890_set_scan_timeout(priv);
+            if (ret) {
+                       RDA5890_ERRP("rda5890_set_scan_timeout fail, ret = %d\n", ret);
+                       goto remove_card;
+            }
+                       
+            ret= rda5890_set_listen_interval(priv, 0x06);
+            if(ret < 0)
+                goto remove_card;
+
+            ret = rda5890_set_link_loss_threshold(priv, 0x06);
+            if(ret < 0)
+                goto remove_card;
+            
+            ret = rda5890_init_pm(priv);
+            if(ret < 0)
+                goto remove_card;
+            
+#ifdef WIFI_TEST_MODE            
+        }
+    else
+        {
+            ret = rda5890_set_test_mode(priv);
+            if(ret < 0)
+                goto remove_card;
+        }
+#endif
+
+    if (sdio_test_flag) {
+        unsigned char mac_addr[6];
+        RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_TRACE,
+        "SDIO TESTING MODE\n");
+
+
+        ret = rda5890_get_mac_addr(priv, mac_addr);
+        printk(KERN_INFO "Test rda5890_get_mac_addr %x:%x:%x:%x:%x:%x", mac_addr[0],mac_addr[1],mac_addr[2],mac_addr[3],mac_addr[4],mac_addr[5]);
+        goto done;
+    }
+
+    ret = rda5890_start_card(priv);
+    if (ret) {
+       RDA5890_ERRP("rda5890_start_card fail, ret = %d\n", ret);
+       goto remove_card;
+    }
+
+done:
+    printk(KERN_INFO "RDA5890: SDIO card started\n");
+
+out:
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+       "%s ret:%d <<<\n", __func__, ret);
+    return ret;
+
+release_int:
+       sdio_claim_host(func);
+       sdio_release_irq(func);
+disable:
+       sdio_disable_func(func);
+release:
+       sdio_release_host(func);
+
+remove_card:   
+    
+    if (atomic_read(&card->wid_complete_flag) && priv)
+    {
+        complete(&priv->wid_done);
+        printk(KERN_INFO "*****RDA5890: probe send wid complete\n");
+    }
+    flush_work(&card->packet_worker);
+    cancel_work_sync(&card->packet_worker);
+    
+#ifdef WIFI_POWER_MANAGER
+    cancel_delayed_work(&card->sleep_work);
+#endif
+    destroy_workqueue(card->work_thread);
+    
+    if(priv)
+        rda5890_remove_card(priv);
+
+    while (card->packets) {
+       packet = card->packets;
+       card->packets = card->packets->next;
+       kfree(packet);  
+    }
+    
+    kfree(card);
+    goto out;
+}
+
+static void if_sdio_remove(struct sdio_func *func)
+{
+    struct if_sdio_card *card;
+    struct if_sdio_packet *packet;
+    unsigned char count = 20; 
+
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+       "%s >>>\n", __func__);
+    
+    printk(KERN_INFO "RDA5890: SDIO card detached\n");
+
+    card = sdio_get_drvdata(func);
+    if(!card)
+        return;
+    while(card->priv->scan_running && count--)
+    {
+        rda5890_shedule_timeout(100);
+        printk("remove card wait scan complete \n");
+    }
+
+#ifdef WIFI_POWER_MANAGER
+    cancel_delayed_work_sync(&card->sleep_work);
+#endif
+    cancel_work_sync(&card->packet_worker);
+    
+    if (atomic_read(&card->wid_complete_flag) && card->priv)
+    {
+        complete(&card->priv->wid_done);
+        printk(KERN_INFO "*****RDA5890: send wid complete\n");
+    }
+
+    netif_stop_queue(card->priv->dev);
+    netif_carrier_off(card->priv->dev);
+    rda5890_stop_card(card->priv);
+    rda5890_debugfs_remove_one(card->priv);
+    destroy_workqueue(card->work_thread);
+    rda5890_remove_card(card->priv);
+    
+    while (card->packets) {
+       packet = card->packets;
+       card->packets = card->packets->next;
+       kfree(packet);
+    } 
+    
+    kfree(card);
+
+#ifdef WIFI_UNLOCK_SYSTEM 
+    rda5990_wakeLock_destroy();
+#endif  
+
+    printk(KERN_INFO "RDA5890: SDIO card removed\n");
+
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+       "%s <<<\n", __func__); 
+    return;   
+}
+
+static struct sdio_driver if_sdio_driver = {
+       .name           = "rda5890_sdio",
+       .id_table       = if_sdio_ids,
+       .probe          = if_sdio_probe,
+       .remove         = if_sdio_remove,
+};
+
+/*******************************************************************/
+/* Module functions                                                */
+/*******************************************************************/
+extern void mmc_rescan_slot(int id);
+
+static int __init if_sdio_init_module(void)
+{
+    int ret = 0;
+
+    printk(KERN_INFO "\nRDA5890 SDIO WIFI Driver for st_linux \n");
+    printk(KERN_INFO "Ver: %d.%d.%d\n\n", 
+       RDA5890_SDIOWIFI_VER_MAJ, 
+       RDA5890_SDIOWIFI_VER_MIN, 
+       RDA5890_SDIOWIFI_VER_BLD);
+
+       printk("@@@@@@@@@@@@@@@22222 rda5990 start\n");
+
+       msleep(1000);
+
+       printk("@@@@@@@@@@@@@@ mmc_rescan_slot\n");
+       mmc_rescan_slot(0);
+       msleep(1000);
+
+       rda5890_debugfs_init();
+       ret = sdio_register_driver(&if_sdio_driver);
+
+       if (ret)
+       {
+               printk(KERN_INFO "\nif_sdio_init_module: register fail. \n");
+               //sunximmc_rescan_card(SDIOID,0);
+       }
+
+       printk(KERN_INFO "\n if_sdio_init_module: register successful. \n");
+
+       return ret;
+}
+
+extern void mmc_remove(int id);
+static void __exit if_sdio_exit_module(void)
+{
+    printk(KERN_INFO "\n if_sdio_exit_module. \n");
+               mmc_remove(0);
+    rda5890_debugfs_remove();
+    sdio_unregister_driver(&if_sdio_driver);
+}
+
+
+module_init(if_sdio_init_module);
+module_exit(if_sdio_exit_module);
+
+MODULE_DESCRIPTION("RDA5890 SDIO WLAN Driver");
+MODULE_AUTHOR("lwang");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.h b/drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.h
new file mode 100755 (executable)
index 0000000..6c5a758
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef _RDA5890_IF_SDIO_H
+#define _RDA5890_IF_SDIO_H
+
+#define IF_SDIO_SDIO2AHB_PKTLEN_L              0x00
+#define IF_SDIO_SDIO2AHB_PKTLEN_H              0x01
+
+#define IF_SDIO_AHB2SDIO_PKTLEN_L              0x02
+#define IF_SDIO_AHB2SDIO_PKTLEN_H              0x03
+
+#define IF_SDIO_FUN1_INT_MASK  0x04
+#define IF_SDIO_FUN1_INT_PEND  0x05
+#define IF_SDIO_FUN1_INT_STAT  0x06
+
+#define   IF_SDIO_INT_AHB2SDIO 0x01
+#define   IF_SDIO_INT_ERROR            0x04
+#define   IF_SDIO_INT_SLEEP            0x10
+#define   IF_SDIO_INT_AWAKE            0x20
+#define   IF_SDIO_INT_RXCMPL   0x40
+#define   IF_SDIO_HOST_TX_FLAG 0x80
+
+#define IF_SDIO_FUN1_FIFO_WR   0x07
+#define IF_SDIO_FUN1_FIFO_RD   0x08
+
+#define IF_SDIO_FUN1_INT_TO_DEV        0x09
+
+struct if_sdio_card {
+        struct sdio_func        *func;
+        struct rda5890_private  *priv;
+
+        u8                      buffer[2048];
+
+        spinlock_t              lock;
+        struct if_sdio_packet   *packets;
+        struct work_struct      packet_worker;
+        
+        struct workqueue_struct *work_thread;
+        atomic_t wid_complete_flag;
+#ifdef WIFI_POWER_MANAGER
+        atomic_t              sleep_work_is_active;
+        struct delayed_work   sleep_work;
+#endif
+};
+
+void export_msdc_clk_always_on(void);
+void export_msdc_clk_always_on_off(void);
+void export_wifi_eirq_enable(void);
+void export_wifi_eirq_disable(void);
+
+#endif
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_ioctl.h b/drivers/net/wireless/rda5990/rda_wlan/rda5890_ioctl.h
new file mode 100755 (executable)
index 0000000..0dc1d4e
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _RDA5890_IOCTL_H_\r
+#define _RDA5890_IOCTL_H_\r
+\r
+#include "linux/sockios.h"\r
+\r
+#define IOCTL_RDA5890_BASE                SIOCDEVPRIVATE\r
+#define IOCTL_RDA5890_GET_MAGIC           IOCTL_RDA5890_BASE\r
+#define IOCTL_RDA5890_GET_DRIVER_VER      (IOCTL_RDA5890_BASE + 1)\r
+#define IOCTL_RDA5890_MAC_GET_FW_VER      (IOCTL_RDA5890_BASE + 2)\r
+#define IOCTL_RDA5890_MAC_WID             (IOCTL_RDA5890_BASE + 3)\r
+#define IOCTL_RDA5890_SET_WAPI_ASSOC_IE   (IOCTL_RDA5890_BASE + 4)\r
+#define IOCTL_RDA5890_GET_WAPI_ASSOC_IE   (IOCTL_RDA5890_BASE + 5)\r
+\r
+#define RDA5890_MAGIC 0x5890face\r
+\r
+#endif /* _RDA5890_IOCTL_H_  */\r
\r
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_nvram.c b/drivers/net/wireless/rda5990/rda_wlan/rda5890_nvram.c
new file mode 100755 (executable)
index 0000000..fb93a2e
--- /dev/null
@@ -0,0 +1,108 @@
+#include <linux/fs.h>\r
+#include <asm/uaccess.h>\r
+\r
+#define WIFI_NVRAM_FILE_NAME "/data/nvram/APCFG/APRDEB/RDAWIFI"\r
+\r
+static int nvram_read(char *filename, char *buf, ssize_t len, int offset)\r
+{      \r
+    struct file *fd;\r
+    //ssize_t ret;\r
+    int retLen = -1;\r
+    \r
+    mm_segment_t old_fs = get_fs();\r
+    set_fs(KERNEL_DS);\r
+    \r
+    fd = filp_open(filename, O_WRONLY|O_CREAT, 0644);\r
+    \r
+    if(IS_ERR(fd)) {\r
+        printk("[rda5890][nvram_read] : failed to open!!\n");\r
+        return -1;\r
+    }\r
+    do{\r
+        if ((fd->f_op == NULL) || (fd->f_op->read == NULL))\r
+               {\r
+                printk("[rda5890][nvram_read] : file can not be read!!\n");\r
+                break;\r
+               } \r
+               \r
+        if (fd->f_pos != offset) {\r
+            if (fd->f_op->llseek) {\r
+                           if(fd->f_op->llseek(fd, offset, 0) != offset) {\r
+                                               printk("[rda5890][nvram_read] : failed to seek!!\n");\r
+                                           break;\r
+                           }\r
+                 } else {\r
+                           fd->f_pos = offset;\r
+                 }\r
+        }              \r
+        \r
+               retLen = fd->f_op->read(fd,\r
+                                                                         buf,\r
+                                                                         len,\r
+                                                                         &fd->f_pos);                  \r
+               \r
+    }while(false);\r
+    \r
+    filp_close(fd, NULL);\r
+    \r
+    set_fs(old_fs);\r
+    \r
+    return retLen;\r
+}\r
+\r
+static int nvram_write(char *filename, char *buf, ssize_t len, int offset)\r
+{      \r
+    struct file *fd;\r
+    int retLen = -1;\r
+        \r
+    mm_segment_t old_fs = get_fs();\r
+    set_fs(KERNEL_DS);\r
+    \r
+    fd = filp_open(filename, O_WRONLY|O_CREAT, 0644);\r
+    \r
+    if(IS_ERR(fd)) {\r
+        printk("[rda5890][nvram_write] : failed to open!!\n");\r
+        return -1;\r
+    }\r
+    do{\r
+        if ((fd->f_op == NULL) || (fd->f_op->write == NULL))\r
+               {\r
+                printk("[rda5890][nvram_write] : file can not be write!!\n");\r
+                break;\r
+               } /* End of if */\r
+               \r
+        if (fd->f_pos != offset) {\r
+            if (fd->f_op->llseek) {\r
+                   if(fd->f_op->llseek(fd, offset, 0) != offset) {\r
+                                   printk("[rda5890][nvram_write] : failed to seek!!\n");\r
+                    break;\r
+                }\r
+            } else {\r
+                fd->f_pos = offset;\r
+            }\r
+        }                      \r
+        \r
+        retLen = fd->f_op->write(fd,\r
+                                 buf,\r
+                                 len,\r
+                                 &fd->f_pos);                  \r
+               \r
+    }while(false);\r
+    \r
+    filp_close(fd, NULL);\r
+    \r
+    set_fs(old_fs);\r
+    \r
+    return retLen;\r
+}\r
+\r
+int rda5890_read_mac(char* buf)\r
+{\r
+    return nvram_read(WIFI_NVRAM_FILE_NAME, buf, 6, 0);\r
+}\r
+\r
+int rda5890_write_mac(char * buf)\r
+{\r
+    return nvram_write(WIFI_NVRAM_FILE_NAME, buf, 6, 0);\r
+}\r
+\r
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_scan.c b/drivers/net/wireless/rda5990/rda_wlan/rda5890_scan.c
new file mode 100755 (executable)
index 0000000..207595c
--- /dev/null
@@ -0,0 +1,369 @@
+#include <linux/types.h>\r
+#include <linux/kernel.h>\r
+#include <linux/ieee80211.h>
+#include <net/iw_handler.h>
+#include <linux/etherdevice.h>
+
+\r
+#include "rda5890_dev.h"\r
+#include "rda5890_defs.h"
+\r
+//#define SCAN_RESULT_DEBUG\r
+\r
+//added by xiongzhi for wapi\r
+#ifndef FCS_LEN\r
+#define FCS_LEN                 4
+#endif\r
+\r
+/* Element ID  of various Information Elements */
+typedef enum {ISSID         = 0,   /* Service Set Identifier   */
+              ISUPRATES     = 1,   /* Supported Rates          */
+              IFHPARMS      = 2,   /* FH parameter set         */
+              IDSPARMS      = 3,   /* DS parameter set         */
+              ICFPARMS      = 4,   /* CF parameter set         */
+              ITIM          = 5,   /* Traffic Information Map  */
+              IIBPARMS      = 6,   /* IBSS parameter set       */
+              ICTEXT        = 16,  /* Challenge Text           */
+              IERPINFO      = 42,  /* ERP Information          */
+              IEXSUPRATES   = 50,   /* Extended Supported Rates */            
+              IWAPI               =68          
+} ELEMENTID_T;
+
+/* Capability Information field bit assignments  */
+typedef enum {ESS           = 0x01,   /* ESS capability               */
+              IBSS          = 0x02,   /* IBSS mode                    */
+              POLLABLE      = 0x04,   /* CF Pollable                  */
+              POLLREQ       = 0x08,   /* Request to be polled         */
+              PRIVACY       = 0x10,   /* WEP encryption supported     */
+              SHORTPREAMBLE  = 0x20,   /* Short Preamble is supported  */
+              SHORTSLOT      = 0x400,  /* Short Slot is supported      */
+              PBCC          = 0x40,   /* PBCC                         */
+              CHANNELAGILITY = 0x80,   /* Channel Agility              */
+              SPECTRUM_MGMT = 0x100,  /* Spectrum Management          */
+              DSSS_OFDM      = 0x2000  /* DSSS-OFDM                    */
+} CAPABILITY_T;
+
+/* BSS type */
+typedef enum {INFRASTRUCTURE  = 1,
+              INDEPENDENT     = 2,
+              ANY_BSS         = 3
+} BSSTYPE_T;
+\r
+unsigned char* get_ie_elem(unsigned char* msa, ELEMENTID_T elm_id,
+                                            unsigned short rx_len,unsigned short tag_param_offset)
+{
+    unsigned short index = 0;
+
+    /*************************************************************************/
+    /*                       Beacon Frame - Frame Body                       */
+    /* --------------------------------------------------------------------- */
+    /* |Timestamp |BeaconInt |CapInfo |SSID |SupRates |DSParSet |TIM elm   | */
+    /* --------------------------------------------------------------------- */
+    /* |8         |2         |2       |2-34 |3-10     |3        |4-256     | */
+    /* --------------------------------------------------------------------- */
+    /*                                                                       */
+    /*************************************************************************/
+
+    index = tag_param_offset;
+
+    /* Search for the TIM Element Field and return if the element is found */
+    while(index < (rx_len - FCS_LEN))
+    {
+        if(msa[index] == elm_id)
+        {
+            return(&msa[index]);
+        }
+        else
+        {
+            index += (2 + msa[index + 1]);
+        }
+    }
+
+    return(0);
+}
+
+/* This function extracts the 'from ds' bit from the MAC header of the input */
+/* frame.                                                                    */
+/* Returns the value in the LSB of the returned value.                       */
+unsigned char get_from_ds(unsigned char* header)
+{
+    return ((header[1] & 0x02) >> 1);
+}
+
+/* This function extracts the 'to ds' bit from the MAC header of the input   */
+/* frame.                                                                    */
+/* Returns the value in the LSB of the returned value.                       */
+unsigned char get_to_ds(unsigned char* header)
+{
+    return (header[1] & 0x01);
+}
+
+/* This function extracts the MAC Address in 'address1' field of the MAC     */
+/* header and updates the MAC Address in the allocated 'addr' variable.      */
+void get_address1(unsigned char* msa, unsigned char* addr)
+{
+    memcpy(addr, msa + 4, 6);
+}
+
+/* This function extracts the MAC Address in 'address2' field of the MAC     */
+/* header and updates the MAC Address in the allocated 'addr' variable.      */
+void get_address2(unsigned char* msa, unsigned char* addr)
+{
+    memcpy(addr, msa + 10, 6);
+}
+
+/* This function extracts the MAC Address in 'address3' field of the MAC     */
+/* header and updates the MAC Address in the allocated 'addr' variable.      */
+void get_address3(unsigned char* msa, unsigned char* addr)
+{
+    memcpy(addr, msa + 16, 6);
+}
+
+/* This function extracts the BSSID from the incoming WLAN packet based on   */
+/* the 'from ds' bit, and updates the MAC Address in the allocated 'addr'    */
+/* variable.                                                                 */
+void get_BSSID(unsigned char* data, unsigned char* bssid)
+{
+    if(get_from_ds(data) == 1)
+        get_address2(data, bssid);
+    else if(get_to_ds(data) == 1)
+        get_address1(data, bssid);
+    else
+        get_address3(data, bssid);
+}
+
+extern int is_same_network(struct bss_descriptor *src,
+                  struct bss_descriptor *dst);
+extern void clear_bss_descriptor(struct bss_descriptor *bss);
+void rda5890_network_information(struct rda5890_private *priv, 
+               char *info, unsigned short info_len)
+{\r
+    union iwreq_data wrqu;\r
+    struct bss_descriptor *iter_bss;\r
+    struct bss_descriptor *safe;\r
+    struct bss_descriptor bss_desc;\r
+    struct bss_descriptor * bss = &bss_desc;\r
+\r
+    unsigned char  *pos, *end, *p;\r
+    unsigned char n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0;\r
+    struct ieee_ie_country_info_set *pcountryinfo;\r
+    int ret;\r
+    unsigned char* msa = &info[9];
+    unsigned short msa_len = info[6] | (info[7] << 8);
+\r
+    if(!priv->scan_running)\r
+        goto done;\r
+    \r
+    if((msa_len - 1 + 9 ) != info_len)
+        {
+            RDA5890_ERRP("rda5890_network_information verify lengh feild failed \n");
+        }
+
+    memset(bss, 0, sizeof (struct bss_descriptor));\r
+    bss->rssi = info[8];\r
+    msa_len -= 1; // has rssi\r
+
+    get_BSSID(msa, bss->bssid);\r
+\r
+    end = msa + msa_len;\r
+    \r
+    //mac head\r
+    pos = msa + 24;\r
+    //time stamp\r
+    pos += 8;\r
+    //beacon\r
+    bss->beaconperiod = *(pos) | (*(pos + 1) << 8);\r
+    pos += 2 ;\r
+    //capability\r
+    bss->capability = *(pos) | (*(pos + 1) << 8);\r
+    pos += 2;\r
+\r
+    if (bss->capability & WLAN_CAPABILITY_IBSS)\r
+        bss->mode = IW_MODE_ADHOC;\r
+    else\r
+        bss->mode = IW_MODE_INFRA;\r
+\r
+  /* process variable IE */\r
+       while (pos + 2 <= end) {\r
+               if (pos + pos[1] > end) {\r
+#ifdef SCAN_RESULT_DEBUG            \r
+                       RDA5890_DBGP("process_bss: error in processing IE, "\r
+                                    "bytes left < IE length\n");\r
+#endif\r
+                       break;\r
+               }\r
+\r
+               switch (pos[0]) {\r
+               case WLAN_EID_SSID:\r
+                       bss->ssid_len = min_t(int, IEEE80211_MAX_SSID_LEN, pos[1]);\r
+                       memcpy(bss->ssid, pos + 2, bss->ssid_len);\r
+#ifdef SCAN_RESULT_DEBUG            \r
+                       RDA5890_DBGP("got SSID IE: '%s', len %u %d\n",\r
+                                    bss->ssid,\r
+                                    bss->ssid_len, pos[1]);\r
+#endif\r
+                       break;\r
+\r
+               case WLAN_EID_SUPP_RATES:\r
+                       n_basic_rates = min_t(uint8_t, MAX_RATES, pos[1]);\r
+                       memcpy(bss->rates, pos + 2, n_basic_rates);\r
+                       got_basic_rates = 1;\r
+#ifdef SCAN_RESULT_DEBUG            \r
+                       RDA5890_DBGP("got RATES IE\n");\r
+#endif\r
+                       break;\r
+\r
+               case WLAN_EID_FH_PARAMS:\r
+#ifdef SCAN_RESULT_DEBUG            \r
+                       RDA5890_DBGP("got FH IE\n");\r
+#endif\r
+                       break;\r
+\r
+               case WLAN_EID_DS_PARAMS:\r
+#ifdef SCAN_RESULT_DEBUG            \r
+                       RDA5890_DBGP("got DS IE\n");\r
+#endif\r
+                       break;\r
+\r
+               case WLAN_EID_CF_PARAMS:\r
+#ifdef SCAN_RESULT_DEBUG            \r
+                       RDA5890_DBGP("got CF IE\n");\r
+#endif\r
+                       break;\r
+\r
+               case WLAN_EID_IBSS_PARAMS:\r
+#ifdef SCAN_RESULT_DEBUG            \r
+                       RDA5890_DBGP("got IBSS IE\n");\r
+#endif\r
+                       break;\r
+\r
+               case WLAN_EID_COUNTRY:\r
+                       pcountryinfo = (struct ieee_ie_country_info_set *) pos;\r
+#ifdef SCAN_RESULT_DEBUG            \r
+                       RDA5890_DBGP("got COUNTRY IE\n");\r
+#endif\r
+                       break;\r
+\r
+               case WLAN_EID_EXT_SUPP_RATES:\r
+                       /* only process extended supported rate if data rate is\r
+                        * already found. Data rate IE should come before\r
+                        * extended supported rate IE\r
+                        */\r
+#ifdef SCAN_RESULT_DEBUG                        \r
+                       RDA5890_DBGP("got RATESEX IE\n");\r
+#endif\r
+                       if (!got_basic_rates) {\r
+#ifdef SCAN_RESULT_DEBUG                \r
+                               RDA5890_DBGP("... but ignoring it\n");\r
+#endif\r
+                               break;\r
+                       }\r
+\r
+                       n_ex_rates = pos[1];\r
+                       if (n_basic_rates + n_ex_rates > MAX_RATES)\r
+                               n_ex_rates = MAX_RATES - n_basic_rates;\r
+\r
+                       p = bss->rates + n_basic_rates;\r
+                       memcpy(p, pos + 2, n_ex_rates);\r
+                       break;\r
+\r
+               case WLAN_EID_GENERIC:\r
+                       if (pos[1] >= 4 &&\r
+                           pos[2] == 0x00 && pos[3] == 0x50 &&\r
+                           pos[4] == 0xf2 && pos[5] == 0x01) {\r
+                               bss->wpa_ie_len = min(pos[1] + 2, MAX_WPA_IE_LEN);\r
+                               memcpy(bss->wpa_ie, pos, bss->wpa_ie_len);\r
+#ifdef SCAN_RESULT_DEBUG                \r
+                               RDA5890_DBGP("got WPA IE \n");\r
+#endif\r
+                       }
+                       else {\r
+#ifdef SCAN_RESULT_DEBUG                \r
+                               RDA5890_DBGP("got generic IE: %02x:%02x:%02x:%02x, len %d\n",\r
+                                       pos[2], pos[3],\r
+                                       pos[4], pos[5],\r
+                                       pos[1]);\r
+#endif\r
+                       }\r
+                       break;\r
+\r
+               case WLAN_EID_RSN:\r
+#ifdef SCAN_RESULT_DEBUG            \r
+                       RDA5890_DBGP("got RSN IE\n");\r
+#endif\r
+                       bss->rsn_ie_len = min(pos[1] + 2, MAX_WPA_IE_LEN);\r
+                       memcpy(bss->rsn_ie, pos, bss->rsn_ie_len);\r
+                       break;\r
+\r
+            case IWAPI:\r
+#ifdef SCAN_RESULT_DEBUG                \r
+                    RDA5890_DBGP("got WAPI IE\n");\r
+#endif\r
+            bss->wapi_ie_len = min(pos[1] + 2, 100);\r
+            memcpy(bss->wapi_ie, pos, bss->wapi_ie_len);\r
+            break;\r
+\r
+               default:\r
+               break;\r
+               }\r
+\r
+               pos += pos[1] + 2;\r
+       }\r
+    \r
+      bss->last_scanned = jiffies;\r
+\r
+    /* add scaned bss into list */\r
+       if (1) {\r
+                       struct bss_descriptor *found = NULL;\r
+                       struct bss_descriptor *oldest = NULL;\r
+\r
+                       /* Try to find this bss in the scan table */\r
+                       list_for_each_entry (iter_bss, &priv->network_list, list) {\r
+                       if (is_same_network(iter_bss, bss)) {\r
+                               found = iter_bss;
+                               break;
+                       }
+
+                       if ((oldest == NULL) ||
+                           (iter_bss->last_scanned < oldest->last_scanned))
+                               oldest = iter_bss;
+               }
+\r
+               if (found) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "FOUND SAME %s, update\n", found->ssid);
+                       /* found, clear it */
+                       clear_bss_descriptor(found);
+               } else if (!list_empty(&priv->network_free_list)) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "FOUND NEW %s, add\n", bss->ssid);\r
+                       /* Pull one from the free list */
+                       found = list_entry(priv->network_free_list.next,
+                                          struct bss_descriptor, list);
+                       list_move_tail(&found->list, &priv->network_list);
+               } else if (oldest) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "FOUND NEW %s, no space, replace oldest %s\n", 
+                               bss->ssid, oldest->ssid);
+                       /* If there are no more slots, expire the oldest */
+                       found = oldest;
+                       clear_bss_descriptor(found);
+                       list_move_tail(&found->list, &priv->network_list);
+               } else {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "FOUND NEW but no space to store\n");
+               }
+
+               /* Copy the locally created newbssentry to the scan table */
+               memcpy(found, bss, offsetof(struct bss_descriptor, list));\r
+       }\r
+\r
+done:\r
+#ifdef SCAN_RESULT_DEBUG    \r
+       RDA5890_DBGP("rda5890_network_information ret %d \n", ret);\r
+#endif\r
+    return;\r
+}
+\r
+\r
+\r
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_sdio_patch.c b/drivers/net/wireless/rda5990/rda_wlan/rda5890_sdio_patch.c
new file mode 100755 (executable)
index 0000000..f72d44f
--- /dev/null
@@ -0,0 +1,1406 @@
+#include "rda5890_dev.h"
+#include "rda5890_wid.h"
+#include "rda5890_defs.h"
+
+#ifdef WIFI_TEST_MODE
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/nfs_mount.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/tty.h>
+#include <linux/syscalls.h>
+#include <asm/termbits.h>
+#include <linux/serial.h>
+#endif
+
+static u8 sdio_patch_complete = 0;
+static u8 sdio_init_complete = 0;
+
+//#define dig_access_pmu_cancel 
+const u32 wifi_core_patch_data_32[][2] =
+{
+       {0x00108000, 0xEA03DF9C},
+       {0x00108004, 0xE59F101C},
+       {0x00108008, 0xE3A00040},
+       {0x0010800C, 0xE5C10038},
+       {0x00108010, 0xE1A0F00E},
+       {0x00108014, 0xEA03DF95},
+       {0x00108018, 0xE59F1008},
+       {0x0010801C, 0xE3A00040},
+       {0x00108020, 0xE5C10038},
+       {0x00108024, 0xE1A0F00E},
+       {0x00108028, 0x50300000},
+       {0x0010802C, 0xEB03D6F2},
+       {0x00108030, 0xE1A00B84},
+       {0x00108034, 0xE1B00BA0},
+       {0x00108038, 0x11A00B84},
+       {0x0010803C, 0x11A00BA0},
+       {0x00108040, 0x12600F80},
+       {0x00108044, 0x10804004},
+       {0x00108048, 0xE1A00124},
+       {0x0010804C, 0xE92D0011},
+       {0x00108050, 0xE51F4030},
+       {0x00108054, 0xE3A00020},
+       {0x00108058, 0xE5C40038},
+       {0x0010805C, 0xE8BD0011},
+       {0x00108060, 0xE1A0F00E},
+       {0x00108064, 0xEA03D3D2},
+       {0x00108068, 0xE3A00001},
+       {0x0010806C, 0xE1A0F00E},
+       {0x00108070, 0xEA03D6CD},
+       {0x00108074, 0xE3A00001},
+       {0x00108078, 0xE1A0F00E},
+       {0x0010807C, 0xEB03C786},
+       {0x00108080, 0xE51F0060},
+       {0x00108084, 0xE5D00038},
+       {0x00108088, 0xE3100080},
+       {0x0010808C, 0x1A000001},
+       {0x00108090, 0xE3A00001},
+       {0x00108094, 0xE1A0F00E},
+       {0x00108098, 0xE3A00000},
+       {0x0010809C, 0xE1A0F00E},
+       {0x001080A0, 0xEB03EADE},
+       {0x001080A4, 0xE51F0084},
+       {0x001080A8, 0xE5D00038},
+       {0x001080AC, 0xE3100080},
+       {0x001080B0, 0x1A000001},
+       {0x001080B4, 0xE3A00001},
+       {0x001080B8, 0xE1A0F00E},
+       {0x001080BC, 0xE3A00000},
+       {0x001080C0, 0xE1A0F00E},
+       {0x001080C4, 0xEB03D89D},
+       {0x001080C8, 0xE51F00A8},
+       {0x001080CC, 0xE3A01080},
+       {0x001080D0, 0xE5C01038},
+       {0x001080D4, 0xE1A0F00E},
+       {0x001080D8, 0xEB03D714},
+       {0x001080DC, 0xE51F10BC},
+       {0x001080E0, 0xE5D10038},
+       {0x001080E4, 0xE3100080},
+       {0x001080E8, 0x159F001C},
+       {0x001080EC, 0x059F0014},
+       {0x001080F0, 0xE59F100C},
+       {0x001080F4, 0xE581003C},
+       {0x001080F8, 0xE51F10D8},
+       {0x001080FC, 0xE1D101B4},
+       {0x00108100, 0xE1A0F00E},
+       {0x00108104, 0x30010000},
+       {0x00108108, 0x2E00A100},
+       {0x0010810C, 0x2E00A000},
+       {0x00108110, 0xEB03B485},
+       {0x00108114, 0x13100010},
+       {0x00108118, 0x13A00002},
+       {0x0010811C, 0x15C50067},
+       {0x00108120, 0xE1A0F00E},
+       {0x00108124, 0xEA03D804},
+       {0x00108128, 0xE51F1108},
+       {0x0010812C, 0xE5D10038},
+       {0x00108130, 0xE2000020},
+       {0x00108134, 0xE3500000},
+       {0x00108138, 0x1AFFFFFB},
+       {0x0010813C, 0xE3A01004},
+       {0x00108140, 0xE3A00B48},
+       {0x00108144, 0xE280FF45},
+       {0x00108148, 0xEA0402C5},
+       {0x0010814C, 0xE59F600C},
+       {0x00108150, 0xE3A04000},
+       {0x00108154, 0xE5C64000},
+       {0x00108158, 0xE59F6004},
+       {0x0010815C, 0xE59FF004},
+       {0x00108160, 0x0010200B},
+       {0x00108164, 0x0010121D},
+       {0x00108168, 0x00007634},
+       {0x0010816C, 0xEAFFFFEC},
+       {0x00108170, 0xEA03E0FF},
+       {0x00108174, 0x13A00000},
+       {0x00108178, 0x1B000001},
+       {0x0010817C, 0x15C57005},
+       {0x00108180, 0xE59FF004},
+       {0x00108184, 0xE51FF004},
+       {0x00108188, 0x000024DC},
+       {0x0010818C, 0x0000FD74},
+       {0x001087B0, 0xEA0403A4},
+       {0x001087B4, 0x08BD4010},
+       {0x001087B8, 0x0A000002},
+       {0x001087BC, 0x13A00001},
+       {0x001087C0, 0x18BD4010},
+       {0x001087C4, 0x1A000001},
+       {0x001087C8, 0xE51FF004},
+       {0x001087CC, 0x0000D470},
+       {0x001087D0, 0xE51FF004},
+       {0x001087D4, 0x0000D2C0},
+       {0x20040004, 0x0001018C},
+       {0x20040024, 0x00108000},
+       {0x20040008, 0x000101BC},
+       {0x20040028, 0x00108014},
+       {0x2004000C, 0x00012460},
+       {0x2004002C, 0x0010802C},
+       {0x20040010, 0x00013118},
+       {0x20040030, 0x00108064},
+       {0x20040014, 0x00012538},
+       {0x20040034, 0x00108070},
+       {0x20040018, 0x00016260},
+       {0x20040038, 0x0010807C},
+       {0x2004001C, 0x0000D524},
+       {0x2004003C, 0x001080A0},
+       {0x20040020, 0x00011E4C},
+       {0x20040040, 0x001080C4},
+       {0x20040100, 0x00012484},
+       {0x20040120, 0x001080D8},
+       {0x20040104, 0x0001AEF8},
+       {0x20040124, 0x00108110},
+       {0x20040108, 0x00012110},
+       {0x20040128, 0x00108124},
+       {0x2004010C, 0x00007630},
+       {0x2004012C, 0x00108148},
+       {0x20040110, 0x00017158},
+       {0x20040130, 0x0010816C},
+       {0x20040114, 0x0000FD70},
+       {0x20040134, 0x00108170},
+       {0x20040118, 0x0000791C},
+       {0x20040138, 0x001087B0},
+       {0x20040000, 0x00007FFF},
+
+};
+
+const u32 wifi_core_patch_data_32_E[][2] =
+{
+    {0x00108000, 0xEA04012F},
+    {0x00108004, 0x1A000001},
+    {0x00108008, 0xE8BD4010},
+    {0x0010800C, 0xEA000001},
+    {0x00108010, 0xE51FF004},
+    {0x00108014, 0x0000D4AC},
+    {0x00108018, 0xE51FF004},
+    {0x0010801C, 0x0000D654},
+    {0x00108020, 0xEA0401F4},
+    {0x00108024, 0xE59F600C},
+    {0x00108028, 0xE3A04000},
+    {0x0010802C, 0xE5C64000},
+    {0x00108030, 0xE59F6004},
+    {0x00108034, 0xE59FF004},
+    {0x00108038, 0x00101CE7},
+    {0x0010803C, 0x00101039},
+    {0x00108040, 0x00007850},
+    {0x00108044, 0xEAFFFFEC},
+    {0x00108048, 0xEA03E034},
+    {0x0010804C, 0x13A00000},
+    {0x00108050, 0x1B000001},
+    {0x00108054, 0x15C57005},
+    {0x00108058, 0xE59FF004},
+    {0x0010805C, 0xE51FF004},
+    {0x00108060, 0x000024E0},
+    {0x00108064, 0x0000FF78},
+    {0x20040004, 0x00007B40},
+    {0x20040024, 0x00108000},
+    {0x20040008, 0x0000784C},
+    {0x20040028, 0x00108020},
+    {0x2004000C, 0x00017518},
+    {0x2004002C, 0x00108044},
+    {0x20040010, 0x0000FF74},
+    {0x20040030, 0x00108048},
+    {0x20040000, 0x0000000D},
+
+};
+
+const u8 wifi_core_patch_data_8[][2] =
+{
+    { 0x28, 0x1a} ,
+    { 0x29, 0x0d},
+    { 0x35, 0x1e},
+    { 0x4c, 0x90},
+    { 0x4d, 0x38},
+    { 0x39, 0x07},
+    { 0xe4, 0xf5},
+    { 0x21, 0x00},  //default 0
+    { 0x23, 0x10},
+    { 0x48, 0x0e},
+    { 0x25, 0x00},
+    { 0x20, 0xa8},
+    { 0x3f, 0x05},
+    { 0x41, 0x37},
+    { 0x42, 0x40},
+    { 0x5b, 0xa9},
+};
+
+//#define   WF_PAT_CFG_2012_04_15
+
+
+/*if define FORCE_WF, wf is not allow of any activity, antenna switch is also forced to bt*/
+//#define   FORCE_WF
+
+/*if define FORCE_WF_TX ,wf is not allow to do tx and pa is also disabled, but antenna is not forced*/
+//#define   FORCE_WF_TX
+
+/*if define FORCE_WF_RX ,wf is not allow to do rx but antenna is not forced*/
+//#define   FORCE_WF_RX
+
+
+/*if define FORCE_WF_RX_TX wf is not allow to do any tx and rx , pa disabled , but  antenna is not forced*/
+//#define   FORCE_WF_RX_TX
+
+#define WF_PAT_CFG_2012_05_19
+
+const u32 wifi_core_init_data_32[][2] =
+{
+#ifdef   FORCE_WF_RX_TX
+    {0x50000800,0xFC003E05},   
+    {0x50000804,0x00000000},   
+    {0x50000808,0xA5000013},   //no pre_active protect
+    {0x5000080c,0x000001C0},   
+    {0x50000810,0xFFCC0F01},   
+    {0x50000814,0x00000000},  // not grant to rx and tx
+    {0x50000818,0x00FF0001},   
+    {0x5000081C,0xFF000F00},   
+    {0x50000820,0x00000000},   
+    {0x50000824,0x0000F0FE},   
+    {0x50000828,0x00100F10},   
+    {0x50000838,0xFFFFFFFF},   
+    {0x5000083C,0xFFFFFFFF},   
+#endif
+
+
+
+#ifdef   FORCE_WF_RX
+    {0x50000800,0xFC003E05},   
+    {0x50000804,0x00000000},   
+    {0x50000808,0xA5000013},   //no pre_active protect
+    {0x5000080c,0x000001C0},   
+    {0x50000810,0xFFCC0F01},   
+    {0x50000814,0xFF000F00},   //wf not grant to rx
+    {0x50000818,0x00FF0001},   
+    {0x5000081C,0xFF000F00},   
+    {0x50000820,0xFF000F00},   
+    {0x50000824,0x0000F0FE},   
+    {0x50000828,0x00100F10},   
+    {0x50000838,0xFFFFFFFF},   
+    {0x5000083C,0xFFFFFFFF},   
+#endif
+
+
+#ifdef FORCE_WF_TX
+    {0x50000800,0xFC003E05},   
+    {0x50000804,0x00000000},   
+    {0x50000808,0xA5000013},   //no pre_active protect
+    {0x5000080c,0x000001C0},   
+    {0x50000810,0xFFCC0F01},   
+    {0x50000814,0x00FF0033},   //wf not grant to tx
+    {0x50000818,0x00FF0001},   
+    {0x5000081C,0xFF000F00},   
+    {0x50000820,0x00000000},   
+    {0x50000824,0x0000F0FE},   
+    {0x50000828,0x00100F10},   
+    {0x50000838,0xFFFFFFFF},   
+    {0x5000083C,0xFFFFFFFF},   
+#endif
+
+
+#ifdef WF_PAT_CFG_2012_05_19
+    {0x50000800,0xFC003E05},   
+    {0x50000804,0x00000000},   
+    {0x50000808,0xA5000013},   //no pre_active protect
+    {0x5000080c,0x000001C0},   
+    {0x50000810,0xFFCC0F01},   
+    {0x50000814,0xFFFF0F03},   //0xFFFF0F33
+    {0x50000818,0x00FF0001},   
+    {0x5000081C,0xFF000F00},   
+    {0x50000820,0xFF000F00},   
+    {0x50000824,0x0000F0FE},   
+    {0x50000828,0x00100F10},   
+    {0x50000838,0xFFFFFFFF},   
+    {0x5000083C,0xFFFFFFFF},   
+#endif
+
+
+#ifdef  FORCE_WF
+    {0x50000800,0xFC003E05},
+    {0x50000804,0x00000000},
+    {0x50000838,0xF8003f2A},
+    {0x5000083c,0x00000003},
+    {0x50000808,0xfe00001b},
+    {0x50000810,0x00000000},
+    {0x50000814,0x00000000},
+    {0x50000818,0x00000000},
+    {0x5000081C,0x00000000},
+    {0x50000820,0x00000000},
+    {0x50000824,0xffffffff},
+    {0x50000828,0x00100F10},
+#endif
+
+#ifdef WF_PAT_CFG_2012_04_15    /*pta config*/                
+    {0x50000800,0xFC003E05}, //tx_pri hi bits ctrl&mgmt package
+    {0x50000804,0x00000000}, //tx_pri hi bits                                                                                                                                                                                                                           as hi pri
+    {0x50000808,0xA500001B}, //sig_mode and protect time                                                                                                                                                                                                                               
+    {0x5000080c,0x000001C0}, //sigWire mode                                                                                                                                                                                                                            
+    {0x50000810,0xFFCC0F01}, //Lut bt                                                                                                                                                                                                                          
+    {0x50000814,0xFFFF0F33}, //Lut wf                                                                                                                                                                                                                          
+    {0x50000818,0x00FF0001}, //antSel0 for wl_rx                                                                                                                                                                                                                               
+    {0x5000081C,0xFF000F00}, //antSel1 for wl_tx                                                                                                                                                                                                                               
+    {0x50000820,0xFF000F00}, //antSel2 for wl_pa       
+    //{0x50000838,0xFFFFFFFF}, //rx_pri low bits as high pri
+       //{0x5000083C,0xFFFFFFFF}, //rx_pri high  bits as high pri                                                                                                                                                                                                                      
+#endif                                                                                                                                                                                                                                                         
+                                                                                                                                                                                                                       
+/*end pta config*/
+    {  0x00106b6c, 0x00000002 }, // scan channel 13
+    {  0x30010004, 0x0000f77c },    //intn config
+    {  0x30010010, 0x00007dff },    //intn config
+    //item111:ver_b_wf_dig_2011_10_09
+    {  0x30010000, 0x780369AF },   //disable tports wait  100ms;
+    {  0x30000010, 0x7000FFFF },//wait 500ms;
+    {  0x50090054, 0x00000001 },//enable update
+    {  0x50090200, 0x00000000 },
+    {  0x50090204, 0x00000000 },
+    {  0x50090208, 0x00000002 },
+    {  0x5009020c, 0x00000004 },
+    {  0x50090210, 0x00000006 },
+    {  0x50090214, 0x00000008 },
+    {  0x50090218, 0x0000000a },
+    {  0x5009021c, 0x00000040 },
+    {  0x50090220, 0x00000042 },
+    {  0x50090224, 0x00000044 },
+    {  0x50090228, 0x00000046 },
+    {  0x5009022c, 0x00000048 },
+    {  0x50090230, 0x0000004a },
+    {  0x50090234, 0x00000080 },
+    {  0x50090238, 0x00000082 },
+    {  0x5009023c, 0x00000084 },
+    {  0x50090240, 0x00000086 },
+    {  0x50090244, 0x00000088 },
+    {  0x50090248, 0x0000008a },
+    {  0x5009024c, 0x000000c0 },
+    {  0x50090250, 0x000000c2 },
+    {  0x50090254, 0x000000c4 },
+    {  0x50090258, 0x000000c6 },
+    {  0x5009025c, 0x000000c8 },
+    {  0x5009025c, 0x000000c8 },
+    {  0x50090260, 0x000000ca },
+    {  0x50090264, 0x00000100 },
+    {  0x50090268, 0x00000102 },
+    {  0x5009026c, 0x00000104 },
+    {  0x50090270, 0x00000106 },
+    {  0x50090274, 0x00000108 },
+    {  0x50090278, 0x00000140 },
+    {  0x5009027c, 0x00000142 },//lna =0 end
+    {  0x50090280, 0x00000080 },
+    {  0x50090284, 0x00000082 },
+    {  0x50090288, 0x00000084 },
+    {  0x5009028c, 0x00000086 },
+    {  0x50090290, 0x00000088 },
+    {  0x50090294, 0x0000008a },
+    {  0x50090298, 0x000000c0 },
+    {  0x5009029c, 0x000000c2 },
+    {  0x500902a0, 0x000000c4 },
+    {  0x500902a4, 0x000000c6 },
+    {  0x500902a8, 0x000000c8 },
+    {  0x500902ac, 0x000000ca },
+    {  0x500902b0, 0x00000100 },
+    {  0x500902b4, 0x00000102 },
+    {  0x500902b8, 0x00000104 },
+    {  0x500902bc, 0x00000106 },
+    {  0x500902c0, 0x00000108 },
+    {  0x500902c4, 0x00000140 },
+    {  0x500902c8, 0x00000142 },
+    {  0x500902cc, 0x00000144 },
+    {  0x500902d0, 0x00000146 },
+    {  0x500902d4, 0x00000148 },
+    {  0x500902d8, 0x00000180 },
+    {  0x500902dc, 0x00000182 },
+    {  0x500902e0, 0x00000184 },
+    {  0x500902e4, 0x000001c0 },
+    {  0x500902e8, 0x000001c2 },
+    {  0x500902ec, 0x000001c4 },
+    {  0x500902f0, 0x000001c6 },
+    {  0x500902f4, 0x000001c8 },
+    {  0x500902f8, 0x000001ca },
+    {  0x500902fc, 0x000001cc },// lna = 01  end
+    {  0x50090300, 0x00000102 },
+    {  0x50090304, 0x00000104 },
+    {  0x50090308, 0x00000106 },
+    {  0x5009030c, 0x00000108 },
+    {  0x50090310, 0x00000140 },
+    {  0x50090314, 0x00000142 },
+    {  0x50090318, 0x00000144 },
+    {  0x5009031c, 0x00000146 },
+    {  0x50090320, 0x00000148 },
+    {  0x50090324, 0x00000180 },
+    {  0x50090328, 0x00000182 },
+    {  0x5009032c, 0x00000184 },
+    {  0x50090330, 0x000001c0 },
+    {  0x50090334, 0x000001c2 },
+    {  0x50090338, 0x000001c4 },
+    {  0x5009033c, 0x000001c6 },
+    {  0x50090340, 0x000001c8 },
+    {  0x50090344, 0x000001c9 },
+    {  0x50090348, 0x000001c9 },
+    {  0x5009034c, 0x000001c9 },
+    {  0x50090350, 0x000001c9 },
+    {  0x50090354, 0x000001c9 },
+    {  0x50090358, 0x000001c9 },
+    {  0x5009035c, 0x000001c9 },
+    {  0x50090360, 0x000001c9 },
+    {  0x50090364, 0x000001c9 },
+    {  0x50090368, 0x000001c9 },
+    {  0x5009036c, 0x000001c9 },
+    {  0x50090370, 0x000001c9 },
+    {  0x50090374, 0x000001c9 },
+    {  0x50090378, 0x000001c9 },
+    {  0x5009037c, 0x000001c9 },
+    {  0x50090054, 0x00000000 },//disable update
+
+    {  0x5000050c, 0x00008000 },// for association power save
+    
+    //{  0x50000808, 0x65000013 }, // disable prerx_priority;pta config
+    //{  0x50000810, 0xFFCD0F01 },  //rx beacon priority
+
+};
+
+const u32 wifi_core_init_data_32_E[][2] =
+{
+#ifdef   FORCE_WF_RX_TX
+    {0x50000800,0xFC003E05},   
+    {0x50000804,0x00000000},   
+    {0x50000808,0xA5000013},   //no pre_active protect
+    {0x5000080c,0x000001C0},   
+    {0x50000810,0xFFCC0F01},   
+    {0x50000814,0x00000000},  // not grant to rx and tx
+    {0x50000818,0x00FF0001},   
+    {0x5000081C,0xFF000F00},   
+    {0x50000820,0x00000000},   
+    {0x50000824,0x0000F0FE},   
+    {0x50000828,0x00100F10},   
+    {0x50000838,0xFFFFFFFF},   
+    {0x5000083C,0xFFFFFFFF},   
+#endif
+
+#ifdef   FORCE_WF_RX
+    {0x50000800,0xFC003E05},   
+    {0x50000804,0x00000000},   
+    {0x50000808,0xA5000013},   //no pre_active protect
+    {0x5000080c,0x000001C0},   
+    {0x50000810,0xFFCC0F01},   
+    {0x50000814,0xFF000F00},   //wf not grant to rx
+    {0x50000818,0x00FF0001},   
+    {0x5000081C,0xFF000F00},   
+    {0x50000820,0xFF000F00},   
+    {0x50000824,0x0000F0FE},   
+    {0x50000828,0x00100F10},   
+    {0x50000838,0xFFFFFFFF},   
+    {0x5000083C,0xFFFFFFFF},   
+#endif
+
+#ifdef FORCE_WF_TX
+    {0x50000800,0xFC003E05},   
+    {0x50000804,0x00000000},   
+    {0x50000808,0xA5000013},   //no pre_active protect
+    {0x5000080c,0x000001C0},   
+    {0x50000810,0xFFCC0F01},   
+    {0x50000814,0x00FF0033},   //wf not grant to tx
+    {0x50000818,0x00FF0001},   
+    {0x5000081C,0xFF000F00},   
+    {0x50000820,0x00000000},   
+    {0x50000824,0x0000F0FE},   
+    {0x50000828,0x00100F10},   
+    {0x50000838,0xFFFFFFFF},   
+    {0x5000083C,0xFFFFFFFF},   
+#endif
+
+#ifdef WF_PAT_CFG_2012_05_19
+    {0x50000800,0xFC003E05},   
+    {0x50000804,0x00000000},   
+    {0x50000808,0xA5000013},   //no pre_active protect
+    {0x5000080c,0x000001C0},   
+    {0x50000810,0xFFCC0F01},   
+    {0x50000814,0xFFFF0F03},   //0xFFFF0F33
+    {0x50000818,0x00FF0001},   
+    {0x5000081C,0xFF000F00},   
+    {0x50000820,0xFF000F00},   
+    {0x50000824,0x0000F0FE},   
+    {0x50000828,0x00100F10},   
+    {0x50000838,0xFFFFFFFF},   
+    {0x5000083C,0xFFFFFFFF},   
+#endif
+
+#ifdef  FORCE_WF
+    {0x50000800,0xFC003E05},
+    {0x50000804,0x00000000},
+    {0x50000838,0xF8003f2A},
+    {0x5000083c,0x00000003},
+    {0x50000808,0xfe00001b},
+    {0x50000810,0x00000000},
+    {0x50000814,0x00000000},
+    {0x50000818,0x00000000},
+    {0x5000081C,0x00000000},
+    {0x50000820,0x00000000},
+    {0x50000824,0xffffffff},
+    {0x50000828,0x00100F10},
+#endif
+
+#ifdef WF_PAT_CFG_2012_04_15    /*pta config*/                
+    {0x50000800,0xFC003E05}, //tx_pri hi bits ctrl&mgmt package
+    {0x50000804,0x00000000}, //tx_pri hi bits                                                                                                                                                                                                                           as hi pri
+    {0x50000808,0xA500001B}, //sig_mode and protect time                                                                                                                                                                                                                               
+    {0x5000080c,0x000001C0}, //sigWire mode                                                                                                                                                                                                                            
+    {0x50000810,0xFFCC0F01}, //Lut bt                                                                                                                                                                                                                          
+    {0x50000814,0xFFFF0F33}, //Lut wf                                                                                                                                                                                                                          
+    {0x50000818,0x00FF0001}, //antSel0 for wl_rx                                                                                                                                                                                                                               
+    {0x5000081C,0xFF000F00}, //antSel1 for wl_tx                                                                                                                                                                                                                               
+    {0x50000820,0xFF000F00}, //antSel2 for wl_pa       
+    //{0x50000838,0xFFFFFFFF}, //rx_pri low bits as high pri
+       //{0x5000083C,0xFFFFFFFF}, //rx_pri high  bits as high pri                                                                                                                                                                                                                      
+#endif                                                 
+
+    {  0x30010004, 0x0000f77c },    //intn config
+    {  0x30010010, 0x00007dff },    //intn config
+    //item111:ver_b_wf_dig_2011_10_09
+    {  0x30010000, 0x780369AF },   //disable tports wait  100ms;
+    {  0x30000010, 0x7000FFFF },//wait 500ms;
+    {  0x5000050c, 0x00008000 },// for association power save
+};
+
+u32 wifi_notch_data[][2] =
+{
+    //ch 1
+    {0x001008d0, 0x50090040},   
+    {0x001008d4, 0x057213a2},   
+    {0x001008d8, 0x50090044},   
+    {0x001008dc, 0x10000000},  
+    //ch 2 
+    {0x00100910, 0x50090040},   
+    {0x00100914, 0x10000000},   
+    {0x00100918, 0x50090044},   
+    {0x0010091c, 0x10000000},   
+    //ch 3
+    {0x00100950, 0x50090040},   
+    {0x00100954, 0x10000000},   
+    {0x00100958, 0x50090044},   
+    {0x0010095c, 0x10000000},   
+    //ch 4
+    {0x00100990, 0x50090040},   
+    {0x00100994, 0x10000000},   
+    {0x00100998, 0x50090044},   
+    {0x0010099c, 0x10000000},   
+    //ch 5
+    {0x001009d0, 0x50090040},   
+    {0x001009d4, 0x076794b4},   
+    {0x001009d8, 0x50090044},   
+    {0x001009dc, 0x10000000},   
+    //ch 6
+    {0x00100a10, 0x50090040},   
+    {0x00100a14, 0x077c71de},   
+    {0x00100a18, 0x50090044},   
+    {0x00100a1c, 0x046d242e},   
+    //ch 7
+    {0x00100a50, 0x50090040},   
+    {0x00100a54, 0x10000000},   
+    {0x00100a58, 0x50090044},   
+    {0x00100a5c, 0x057e7ec0},   
+    //ch 8
+    {0x00100a90, 0x50090040},   
+    {0x00100a94, 0x077c7e22},   
+    {0x00100a98, 0x50090044},   
+    {0x00100a9c, 0x046d2bd2},   
+    //ch 9
+    {0x00100ad0, 0x50090040},   
+    {0x00100ad4, 0x10000000},   
+    {0x00100ad8, 0x50090044},   
+    {0x00100adc, 0x10000000},   
+    //ch 10
+    {0x00100b10, 0x50090040},   
+    {0x00100b14, 0x10000000},   
+    {0x00100b18, 0x50090044},   
+    {0x00100b1c, 0x10000000},   
+    //ch 11
+    {0x00100b50, 0x50090040},   
+    {0x00100b54, 0x10000000},   
+    {0x00100b58, 0x50090044},   
+    {0x00100b5c, 0x10000000},   
+    //ch 12
+    {0x00100b90, 0x50090040},   
+    {0x00100b94, 0x07764310},   
+    {0x00100b98, 0x50090044},   
+    {0x00100b9c, 0x10000000},   
+    //ch 13
+    {0x00100bd0, 0x50090040},   
+    {0x00100bd4, 0x056794b4},   
+    {0x00100bd8, 0x50090044},   
+    {0x00100bdc, 0x10000000},   
+    //ch 14
+    {0x00100c10, 0x50090040},   
+    {0x00100c14, 0x0779c279},   
+    {0x00100c18, 0x50090044},   
+    {0x00100c1c, 0x0779cd87},   
+};
+
+u32 wifi_notch_data_E[][2] =
+{
+  // For Verion E
+    //ch 1
+    {0x001007CC, 0x50090040},   
+    {0x001007D0, 0x057213a2},   
+    {0x001007D4, 0x50090044},   
+    {0x001007D8, 0x10000000},  
+    //ch 2 
+    {0x001007FC, 0x50090040},   
+    {0x00100800, 0x10000000},   
+    {0x00100804, 0x50090044},   
+    {0x00100808, 0x10000000},   
+    //ch 3
+    {0x0010082C, 0x50090040},   
+    {0x00100830, 0x10000000},   
+    {0x00100834, 0x50090044},   
+    {0x00100838, 0x10000000},   
+    //ch 4
+    {0x0010085C, 0x50090040},   
+    {0x00100860, 0x10000000},   
+    {0x00100864, 0x50090044},   
+    {0x00100868, 0x10000000},   
+    //ch 5
+    {0x0010088C, 0x50090040},   
+    {0x00100890, 0x076794b4},   
+    {0x00100894, 0x50090044},   
+    {0x00100898, 0x10000000},   
+    //ch 6
+    {0x001008BC, 0x50090040},   
+    {0x001008C0, 0x077c71de},   
+    {0x001008C4, 0x50090044},   
+    {0x001008C8, 0x046d242e},   
+    //ch 7
+    {0x001008EC, 0x50090040},   
+    {0x001008F0, 0x10000000}, 
+    {0x001008F4, 0x50090044},   
+    {0x001008F8, 0x057e7140},   
+    //ch 8
+    {0x0010091C, 0x50090040},   
+    {0x00100920, 0x077c7e22},   
+    {0x00100924, 0x50090044},   
+    {0x00100928, 0x046d2bd2},   
+    //ch 9
+    {0x0010094C, 0x50090040},   
+    {0x00100950, 0x10000000},   
+    {0x00100954, 0x50090044},   
+    {0x00100958, 0x10000000},   
+    //ch 10
+    {0x0010097C, 0x50090040},   
+    {0x00100980, 0x10000000},   
+    {0x00100984, 0x50090044},   
+    {0x00100988, 0x10000000},   
+    //ch 11
+    {0x001009AC, 0x50090040},   
+    {0x001009B0, 0x10000000},   
+    {0x001009B4, 0x50090044},   
+    {0x001009B8, 0x10000000},   
+    //ch 12
+    {0x001009DC, 0x50090040},   
+    {0x001009E0, 0x07764310},   
+    {0x001009E4, 0x50090044},   
+    {0x001009E8, 0x10000000},   
+    //ch 13
+    {0x00100A0C, 0x50090040},   
+    {0x00100A10, 0x056794b4},   
+    {0x00100A14, 0x50090044},   
+    {0x00100A18, 0x10000000},
+    //ch 14
+    {0x00100A3C, 0x50090040},   
+    {0x00100A40, 0x0779c279},   
+    {0x00100A44, 0x50090044},   
+    {0x00100A4c, 0x0779cd87},
+};
+
+//common sdio clock open  
+const u32 wifi_core_data_wake[][2] = 
+{
+    {0x3001003c, 0x2e00a000},
+};
+//sleep sdio switch
+const u32 wifi_core_data_sleep[][2] =
+{
+    {0x3001003c, 0x2e00a100},
+};
+
+int rda5890_get_fw_version_polling(struct rda5890_private *priv, unsigned int* version)
+{
+    int ret = -1;
+    char wid_req[255];
+    unsigned short wid_req_len = 6;
+    char wid_rsp[32];
+    unsigned short wid_rsp_len = 32;
+    unsigned short wid;
+    char wid_msg_id = priv->wid_msg_id++;
+    unsigned char *ptr_payload;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+       "%s <<< \n", __func__);
+
+    wid_req[0] = 'Q';
+    wid_req[1] = wid_msg_id;
+
+    wid_req[2] = (char)(wid_req_len&0x00FF);
+    wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+    wid = WID_SYS_FW_VER;
+    wid_req[4] = (char)(wid&0x00FF);
+    wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "__func__ <<<\n" );
+
+       ret = rda5890_wid_request_polling(priv, wid_req, wid_req_len, 
+                                        wid_rsp, &wid_rsp_len);
+       if (ret) {
+               goto out;
+       }
+
+    ret = rda5890_check_wid_response(priv->wid_rsp, priv->wid_rsp_len, wid, wid_msg_id, 
+                4, &ptr_payload);
+
+    if(!ret)
+    {
+        memcpy((unsigned char *)version, ptr_payload, 4);
+    }
+    else
+        *version = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "__func__ version: %x >>> \n" , *version);
+
+out:
+       return ret;
+}
+
+
+int rda5890_write_sdio32_polling(struct rda5890_private *priv,const unsigned int (*data)[2], unsigned int size)
+{
+       int count = size, index = 0;
+       int ret = 0;
+       
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                   "%s >>> \n", __func__);     
+       
+       for(index = 0; index < count/8; index ++)  //each time write five init data
+       {
+               ret = rda5890_set_core_init_polling(priv, data[8*index], 8);
+        if(ret < 0)
+        goto err;
+       }
+
+       if(count%8 > 0)
+       {
+               ret = rda5890_set_core_init_polling(priv, data[8*index], count%8);
+        if(ret < 0)
+        goto err;
+       }       
+err:
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s :ret=%d <<< \n", __func__, ret);
+       return ret;
+}
+
+int rda5890_write_sdio32(struct rda5890_private *priv, const unsigned int (*data)[2], unsigned int size)
+{
+       int count = size, index = 0;
+       int ret = 0;
+       
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                   "%s >>> \n", __func__);     
+       
+       for(index = 0; index < count/8; index ++)  //each time write five init data
+       {
+               ret = rda5890_set_core_init(priv, data[8*index], 8);
+        if(ret < 0)
+            goto err;
+       }
+       
+       if(count%8 > 0)
+       {
+               ret = rda5890_set_core_init(priv, data[8*index], count%8);
+        if(ret < 0)
+            goto err;
+       }       
+err:
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s :ret=%d <<< \n", __func__, ret);
+       return ret;
+}
+
+int rda5890_write_sdio8_polling(struct rda5890_private *priv, const unsigned char (*data)[2], unsigned int size)
+{
+       int count = size, index = 0;
+       int ret = 0;
+       
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                   "%s >>> \n", __func__);     
+       
+       for(index = 0; index < count/8; index ++)  //each time write five init data
+       {
+               ret = rda5890_set_core_patch_polling(priv, data[8*index], 8);
+           if(ret < 0)
+               goto err;
+       }
+
+       if(count%8 > 0)
+       {
+               ret = rda5890_set_core_patch_polling(priv, data[8*index], count%8);
+           if(ret < 0)
+               goto err;
+       }       
+err:
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s :ret=%d <<< \n", __func__, ret);
+       return ret;
+}
+
+
+int rda5890_write_sdio8(struct rda5890_private *priv ,const unsigned char (*data)[2], unsigned int size)
+{
+       int count = size, index = 0;
+       int ret = 0;
+       
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                   "%s >>> \n", __func__);     
+       
+       for(index = 0; index < count/8; index ++)  //each time write five init data
+       {
+               ret = rda5890_set_core_patch(priv, data[8*index], 8);
+         if(ret < 0)
+             goto err;
+       }
+       
+       if(count%8 > 0)
+       {
+               ret = rda5890_set_core_patch(priv, data[8*index], count%8);
+         if(ret < 0)
+             goto err;
+       }       
+err:
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s :ret=%d <<< \n", __func__, ret);
+       return ret;
+}
+
+int rda5890_sdio_patch_core_32(struct rda5890_private *priv)
+{
+    int ret = 0;
+
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                "%s >>> \n", __func__);        
+
+    if(priv->version == 7)
+    ret = rda5890_write_sdio32_polling(priv,wifi_core_patch_data_32, 
+                                                                       sizeof(wifi_core_patch_data_32)/sizeof(wifi_core_patch_data_32[0]));
+    else if(priv->version == 4 || priv->version == 5)
+        ret = rda5890_write_sdio32_polling(priv,wifi_core_patch_data_32_E, 
+                                                                       sizeof(wifi_core_patch_data_32_E)/sizeof(wifi_core_patch_data_32_E[0]));
+
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+            "%s <<< \n", __func__);
+    return ret;
+}
+
+
+int  rda5890_sdio_init_core(struct rda5890_private *priv)
+{
+       int ret = 0;
+         
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                     "%s <<< \n", __func__);   
+    if(priv->version == 7)
+       ret = rda5890_write_sdio32(priv, wifi_core_init_data_32, 
+                                                                                                                       sizeof(wifi_core_init_data_32)/sizeof(wifi_core_init_data_32[0]));  
+       else if(priv->version == 4 || priv->version == 5)
+        ret = rda5890_write_sdio32(priv, wifi_core_init_data_32_E, 
+                                                                  sizeof(wifi_core_init_data_32_E)/sizeof(wifi_core_init_data_32_E[0]));   
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+               "%s >>> \n", __func__);
+       
+       return ret;
+}
+
+int rda5890_sdio_init_core_polling(struct rda5890_private *priv)
+{
+       int ret = 0;
+         
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                     "%s <<< \n", __func__);   
+       if(priv->version == 7)              
+       ret = rda5890_write_sdio32_polling(priv, wifi_core_init_data_32, 
+                                          sizeof(wifi_core_init_data_32)/sizeof(wifi_core_init_data_32[0]));  
+       else if(priv->version == 4 || priv->version == 5)
+        ret = rda5890_write_sdio32_polling(priv, wifi_core_init_data_32_E, 
+                                            sizeof(wifi_core_init_data_32_E)/sizeof(wifi_core_init_data_32_E[0]));  
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                 "%s >>> \n", __func__);
+         return ret;
+}
+
+
+int rda5890_sdio_patch_core_8(struct rda5890_private *priv)
+{
+    int ret = 0;
+    //for patch in byte mode
+    
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                "%s <<< \n", __func__);        
+
+         ret = rda5890_write_sdio8(priv, wifi_core_patch_data_8, 
+                                   sizeof(wifi_core_patch_data_8)/sizeof(wifi_core_patch_data_8[0]));
+  
+    //for patch in wake continue clock mode
+    ret = rda5890_sdio_core_wake_mode(priv);
+    if(ret < 0)
+        goto err;
+
+err:
+    
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+            "%s >>> \n", __func__);
+    return ret;
+}
+
+int rda5890_sdio_patch_core_8_polling(struct rda5890_private *priv)
+{
+    int ret = 0;
+    //for patch in byte mode
+    
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                "%s <<< \n", __func__);        
+
+       ret = rda5890_write_sdio8_polling(priv, wifi_core_patch_data_8,
+                                      sizeof(wifi_core_patch_data_8)/sizeof(wifi_core_patch_data_8[0]));
+    if(ret < 0)
+        goto err; 
+          
+    //for patch in wake continue clock mode
+    ret = rda5890_sdio_core_wake_mode_polling(priv);
+    if(ret < 0)
+        goto err;  
+
+err:
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+            "%s >>> \n", __func__);    
+    return ret;
+}
+
+int rda5890_sdio_set_default_notch(struct rda5890_private *priv)
+{
+    int ret = 0;
+
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                "%s <<< \n", __func__);        
+
+    if(priv->version == 7) 
+         ret = rda5890_write_sdio32(priv, wifi_notch_data, 
+                                    sizeof(wifi_notch_data)/sizeof(wifi_notch_data[0]));
+    else if(priv->version == 4 || priv->version == 5) 
+        ret = rda5890_write_sdio32(priv, wifi_notch_data_E, 
+                                    sizeof(wifi_notch_data_E)/sizeof(wifi_notch_data_E[0]));
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                "%s <<< \n", __func__);           
+    return ret;
+}
+
+int rda5890_sdio_set_default_notch_polling(struct rda5890_private *priv)
+{
+       int ret = 0;
+       
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                   "%s <<< \n", __func__);     
+       if(priv->version == 7) 
+       ret = rda5890_write_sdio32_polling(priv, wifi_notch_data, 
+                                          sizeof(wifi_notch_data)/sizeof(wifi_notch_data[0]));
+       else if(priv->version == 4 || priv->version == 5)
+        ret = rda5890_write_sdio32_polling(priv, wifi_notch_data_E, 
+                                          sizeof(wifi_notch_data_E)/sizeof(wifi_notch_data_E[0]));
+       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                   "%s <<< \n", __func__);        
+       return ret;
+}
+
+
+//
+unsigned char rssi_switch_default_data_u8[][2] =
+{
+        {0x25, 0x00}
+};
+
+void rda5890_sdio_set_notch_by_channel(struct rda5890_private *priv, unsigned int channel)
+{
+#if 0
+    int count = 0, index = 0;
+
+    if(priv->version == 7)
+        count  = sizeof(wifi_notch_data)/(sizeof(wifi_notch_data[0]) * 4);
+    else if(priv->version == 4 || priv->version == 5)
+        count  = sizeof(wifi_notch_data_E)/(sizeof(wifi_notch_data_E[0]) * 4);
+    channel = channel % count;
+       
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                "%s >>> \n", __func__);
+
+    if(channel > 1)
+           channel -= 1;
+
+    if(priv->version == 7)
+        {
+            rda5890_set_core_init(priv, wifi_notch_data[4*channel], 4);
+            rda5890_set_core_patch(priv, rssi_switch_default_data_u8, 1);
+        }
+    else if(priv->version == 4 || priv->version == 5)
+        {
+            rda5890_set_core_init(priv, wifi_notch_data_E[4*channel], 4);
+            rda5890_set_core_patch(priv, rssi_switch_default_data_u8, 1);
+        }
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                "%s <<< ch = %d \n", __func__, channel + 1);
+#endif
+}
+
+int rda5890_sdio_core_wake_mode(struct rda5890_private *priv)
+{
+    int ret = 0;
+
+    ret = rda5890_write_sdio32(priv, wifi_core_data_wake, 
+                               sizeof(wifi_core_data_wake)/sizeof(wifi_core_data_wake[0]));
+   
+    return ret;
+}
+
+int rda5890_sdio_core_wake_mode_polling(struct rda5890_private *priv)
+{
+    int ret = 0;
+
+    ret = rda5890_write_sdio32_polling(priv, wifi_core_data_wake, 
+                               sizeof(wifi_core_data_wake)/sizeof(wifi_core_data_wake[0]));   
+    return ret;
+}
+
+int rda5890_sdio_core_sleep_mode(struct rda5890_private *priv)
+{
+    int ret = 0;
+
+    ret = rda5890_write_sdio32(priv,wifi_core_data_sleep, 
+                               sizeof(wifi_core_data_sleep)/sizeof(wifi_core_data_sleep[0]));
+    return ret;
+}
+
+extern void export_wifi_eirq_enable();
+int rda5890_sdio_init(struct rda5890_private *priv)
+{
+    int ret = 0;
+    unsigned long para = 0;
+
+    sdio_init_complete = 0;
+    sdio_patch_complete = 0;
+
+    printk(KERN_INFO " rda5890_sdio_init <<< \n");     
+
+    ret = rda5890_sdio_patch_core_32(priv);
+    if(ret < 0)
+        goto err;
+    
+    printk(KERN_INFO "sdio_patch_complete wid_msg =  %d \n", priv->wid_msg_id);
+    rda5890_shedule_timeout(10);   //10ms delay
+    sdio_patch_complete = 1;  
+
+#if 0
+    ret = rda5890_sdio_init_core(priv);
+    if(ret < 0)
+        goto err;
+
+    ret = rda5890_sdio_patch_core_8(priv);
+    if(ret < 0)
+        goto err;
+    
+    ret = rda5890_sdio_set_default_notch(priv);
+    if(ret < 0)
+        goto err;
+#else
+
+    ret = rda5890_sdio_init_core_polling(priv);
+    if(ret < 0)
+        goto err;
+
+    ret = rda5890_sdio_patch_core_8_polling(priv);
+    if(ret < 0)
+        goto err;
+        
+    ret = rda5890_sdio_set_default_notch_polling(priv);
+    if(ret < 0)
+        goto err;
+    
+#endif
+    export_wifi_eirq_enable();
+    
+#if 0
+       rda5890_generic_set_ulong(priv, WID_MEMORY_ADDRESS, 0x30010008);        
+       rda5890_generic_get_ulong(priv, WID_MEMORY_ACCESS_32BIT, &para);
+       printk(KERN_INFO "rda5890_sdio_init para = %x \n", para);
+       para |= 0x200;
+       rda5890_generic_set_ulong(priv, WID_MEMORY_ACCESS_32BIT, para);
+       
+       rda5890_generic_set_ulong(priv, WID_MEMORY_ADDRESS, 0x30010010);
+       rda5890_generic_get_ulong(priv, WID_MEMORY_ACCESS_32BIT, &para);
+       printk(KERN_INFO "rda5890_sdio_init para1 = %x \n", para);
+       para &= ~0x200;
+       rda5890_generic_set_ulong(priv, WID_MEMORY_ACCESS_32BIT, para);
+#endif
+
+    sdio_init_complete = 1;
+
+err:
+    return ret;
+}
+
+//rssi > 200
+u32 rssi_switch_data_u32[][2] = 
+{
+    {0x50090040, 0x10000000},
+    {0x50090044, 0x10000000},
+};
+
+u8 rssi_switch_data_u8[][2] = 
+{
+    {0x25, 0x18}
+};
+
+void rda5890_rssi_up_to_200(struct rda5890_private *priv)
+{
+#if 0
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                "%s >>> \n", __func__);
+    rda5890_set_core_init(priv, rssi_switch_data_u32, 2);
+    rda5890_set_core_patch(priv, rssi_switch_data_u8, 1);      
+
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_DEBUG,
+                "%s <<< \n", __func__);
+#endif    
+}
+
+u32 rda5990_assoc_power_save_data32[][2] =
+{
+    {  0x5000050c, 0x00008000 }// for association power save
+};
+
+void rda5990_assoc_power_save(struct rda5890_private *priv)
+{
+    rda5890_set_core_init(priv, rda5990_assoc_power_save_data32, 1);
+}
+
+unsigned char is_sdio_init_complete(void)
+{
+    return sdio_init_complete;
+}
+unsigned char is_sdio_patch_complete(void) //after patch complete need check write flow
+{
+    return sdio_patch_complete;
+}
+
+#ifdef WIFI_TEST_MODE
+
+static unsigned int wifi_test_mode_rx_notch_32[][2] =
+{
+       //item:notch_filter_5
+       {0x50090040,0x076794b4},//8m
+       {0x50090044,0x10000000},
+       //item:notch_filter_6
+       {0x50090040,0x077c71de},//3m
+       {0x50090044,0x046d242e},//7m
+       //item:notch_filter_7
+       {0x50090040,0x077e7ec0},//2m
+       {0x50090044,0x077e7140},//-2m
+       //item:notch_filter_8
+       {0x50090040,0x077c7e22},//3m
+       {0x50090044,0x046d2bd2},//7m
+       //item:notch_filter_c
+       {0x50090040,0x07764310},//5m
+       {0x50090044,0x10000000},
+       //item:notch_filter_e
+       {0x50090040,0x0779c279},//4m
+       {0x50090044,0x0779cd87},//-4m
+       //item:disable_notch
+       {0x50090040,0x10000000},
+       {0x50090044,0x10000000},
+};
+
+static unsigned int wifi_test_mode_agc_patch32[][2] =
+{
+       {0x50000600,0x0000501a},//write 1a(52) to 28h hightolow                   
+       {0x50000600,0x0000520d},//write 0d(26) to 29h hightomid                   
+       {0x50000600,0x00006a1e},//35h reg coarse2 upper window from 0d to 1a for l
+       //;50000600H,32'h00009890;//4ch reg unlock upper threshold from 70 to 90  
+       {0x50000600,0x00009a38},//4dh reg unlock lower threshold from 78 to 38    
+       {0x50000600,0x00007207},//39h reg change vga gain ,9 -> 7 for big signal  
+       {0x50000600,0x0001c8f5},//e4h reg change hpf coeff to f5                  
+       {0x50000600,0x00004200},//21h reg add fine gain 0db                       
+       {0x50000600,0x00004610},//23h reg change maxgain index as agc table       
+       {0x50000600,0x0000900e},//48h reg unlock lower threshold change from 0a to
+       {0x50000600,0x00004a00},//25h reg pecket dection threshold                
+       {0x50000600,0x000040a8},//20h reg add  fine itr2 98->a8                   
+       {0x50000600,0x00007e05},//3f reg rssi window for fine itr2 0->5           
+       {0x50000600,0x00008237},//41 reg fine itr1 nextstate 4->3                 
+       {0x50000600,0x00008440},//42 reg fine itr2 nextstate 0->4 settle time 0->d
+       {0x50000600,0x0000b6a9},//5b reg change GreatN rssi avg count from 1 to 8 
+} ;
+
+static unsigned int rda5990_test_mode_digital32[][2] =
+{ 
+       //item111:ver_D_wf_dig_20120208               
+       {0x30010000,0x780369AF},   //disable tports   
+       //wait 100ms;                                 
+       {0x30000010,0x7000FFFF},                      
+       //item:agc_table_20110921                  
+       {0x50090054,0x00000001},//enable update       
+       {0x50090200,0x00000000},                      
+       {0x50090204,0x00000000},                      
+       {0x50090208,0x00000002},                      
+       {0x5009020c,0x00000004},                      
+       {0x50090210,0x00000006},                      
+       {0x50090214,0x00000008},                      
+       {0x50090218,0x0000000a},                      
+       {0x5009021c,0x00000040},                      
+       {0x50090220,0x00000042},                      
+       {0x50090224,0x00000044},                      
+       {0x50090228,0x00000046},                      
+       {0x5009022c,0x00000048},                      
+       {0x50090230,0x0000004a},                      
+       {0x50090234,0x00000080},                      
+       {0x50090238,0x00000082},                      
+       {0x5009023c,0x00000084},                      
+       {0x50090240,0x00000086},                      
+       {0x50090244,0x00000088},                      
+       {0x50090248,0x0000008a},                      
+       {0x5009024c,0x000000c0},                      
+       {0x50090250,0x000000c2},                      
+       {0x50090254,0x000000c4},                      
+       {0x50090258,0x000000c6},                      
+       {0x5009025c,0x000000c8},                      
+       {0x50090260,0x000000ca},                      
+       {0x50090264,0x00000100},                      
+       {0x50090268,0x00000102},                      
+       {0x5009026c,0x00000104},                      
+       {0x50090270,0x00000106},                      
+       {0x50090274,0x00000108},                      
+       {0x50090278,0x00000140},                      
+       {0x5009027c,0x00000142},//lna =0 end          
+       {0x50090280,0x00000080},                      
+       {0x50090284,0x00000082},                      
+       {0x50090288,0x00000084},                      
+       {0x5009028c,0x00000086},                      
+       {0x50090290,0x00000088},                      
+       {0x50090294,0x0000008a},                      
+       {0x50090298,0x000000c0},                      
+       {0x5009029c,0x000000c2},                      
+       {0x500902a0,0x000000c4},                      
+       {0x500902a4,0x000000c6},                      
+       {0x500902a8,0x000000c8},                      
+       {0x500902ac,0x000000ca},                      
+       {0x500902b0,0x00000100},                      
+       {0x500902b4,0x00000102},                      
+       {0x500902b8,0x00000104},                      
+       {0x500902bc,0x00000106},                      
+       {0x500902c0,0x00000108},                      
+       {0x500902c4,0x00000140},                      
+       {0x500902c8,0x00000142},                      
+       {0x500902cc,0x00000144},                      
+       {0x500902d0,0x00000146},                      
+       {0x500902d4,0x00000148},                      
+       {0x500902d8,0x00000180},                      
+       {0x500902dc,0x00000182},                      
+       {0x500902e0,0x00000184},                      
+       {0x500902e4,0x000001c0},                      
+       {0x500902e8,0x000001c2},                      
+       {0x500902ec,0x000001c4},                      
+       {0x500902f0,0x000001c6},                      
+       {0x500902f4,0x000001c8},                      
+       {0x500902f8,0x000001ca},                      
+       {0x500902fc,0x000001cc},// lna = 01  end      
+       {0x50090300,0x00000102},                      
+       {0x50090304,0x00000104},                      
+       {0x50090308,0x00000106},                      
+       {0x5009030c,0x00000108},                      
+       {0x50090310,0x00000140},                      
+       {0x50090314,0x00000142},                      
+       {0x50090318,0x00000144},                      
+       {0x5009031c,0x00000146},                      
+       {0x50090320,0x00000148},                      
+       {0x50090324,0x00000180},                      
+       {0x50090328,0x00000182},                      
+       {0x5009032c,0x00000184},                      
+       {0x50090330,0x000001c0},                      
+       {0x50090334,0x000001c2},                      
+       {0x50090338,0x000001c4},                      
+       {0x5009033c,0x000001c6},                      
+       {0x50090340,0x000001c8},                      
+       {0x50090344,0x000001c9},                      
+       {0x50090348,0x000001c9},                      
+       {0x5009034c,0x000001c9},                      
+       {0x50090350,0x000001c9},                      
+       {0x50090354,0x000001c9},                      
+       {0x50090358,0x000001c9},                      
+       {0x5009035c,0x000001c9},                      
+       {0x50090360,0x000001c9},                      
+       {0x50090364,0x000001c9},                      
+       {0x50090368,0x000001c9},                      
+       {0x5009036c,0x000001c9},                      
+       {0x50090370,0x000001c9},                      
+       {0x50090374,0x000001c9},                      
+       {0x50090378,0x000001c9},                      
+       {0x5009037c,0x000001c9},                      
+       {0x50090054,0x00000000},//disable update      
+       {0x50000808,0x65000013}, // disable prerx_prio
+       //pta config                                  
+       {0x50000810,0xFFCD0F01},  //rx beacon priority
+};
+int rda5890_set_test_mode(struct rda5890_private *priv)
+{
+       int ret = 0;
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_CRIT,
+                "%s >>> \n", __func__);
+
+    sdio_init_complete = 0;
+    sdio_patch_complete = 0;
+    ret = rda5890_sdio_patch_core_32(priv);
+    if(ret < 0)
+        goto err;
+    sdio_patch_complete = 1;
+  
+       ret = rda5890_write_sdio8_polling(priv, wifi_core_patch_data_8, 
+                                     sizeof(wifi_core_patch_data_8)/sizeof(wifi_core_patch_data_8[0]));
+    if(ret < 0)
+        goto err; 
+    
+    ret = rda5890_write_sdio32_polling(priv, rda5990_test_mode_digital32, 
+                                       sizeof(rda5990_test_mode_digital32)/sizeof(rda5990_test_mode_digital32[0]));
+         if(ret < 0)
+        goto err;
+        
+    ret = rda5890_write_sdio32_polling(priv, wifi_test_mode_agc_patch32, 
+                                      sizeof(wifi_test_mode_agc_patch32)/sizeof(wifi_test_mode_agc_patch32[0]));
+         if(ret < 0)
+        goto err;
+        
+    ret = rda5890_write_sdio32_polling(priv, wifi_test_mode_rx_notch_32, 
+                                       sizeof(wifi_test_mode_rx_notch_32)/sizeof(wifi_test_mode_rx_notch_32[0]));
+    if(ret < 0)
+        goto err;
+    
+
+    ret = rda5890_sdio_set_default_notch_polling(priv);
+    if(ret < 0)
+        goto err;    
+
+    export_wifi_eirq_enable();
+    sdio_init_complete = 1;
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_CRIT,
+                "%s <<< \n", __func__);
+    return ret;
+    
+err:
+    RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_CRIT, "rda5890_set_test_mode err!! \n" ); 
+    return ret;
+} 
+#endif
+
\ No newline at end of file
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.c b/drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.c
new file mode 100755 (executable)
index 0000000..ad01fea
--- /dev/null
@@ -0,0 +1,139 @@
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/etherdevice.h>
+#include <net/iw_handler.h>
+
+#include "rda5890_defs.h"
+#include "rda5890_dev.h"
+#include "rda5890_wid.h"
+#include "rda5890_wext.h"
+
+void rda5890_data_rx(struct rda5890_private *priv, 
+               char *data, unsigned short data_len)
+{
+       struct sk_buff *skb;
+       char *pkt_data;
+
+       RDA5890_DBGLAP(RDA5890_DA_TXRX, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       skb = dev_alloc_skb(data_len + NET_IP_ALIGN);
+       if (!skb) {
+               priv->stats.rx_dropped++;
+               return;
+       }
+       skb_reserve(skb, NET_IP_ALIGN);
+       pkt_data = skb_put(skb, data_len);
+       memcpy(pkt_data, data, data_len);
+       skb->dev = priv->dev;
+       skb->protocol = eth_type_trans(skb, priv->dev);
+       skb->ip_summed = CHECKSUM_NONE;
+
+       RDA5890_DBGLAP(RDA5890_DA_TXRX, RDA5890_DL_TRACE,
+               "netif rx, len %d\n", skb->len);
+       RDA5890_DBGLAP(RDA5890_DA_TXRX, RDA5890_DL_TRACE,
+               "%02x %02x %02x %02x ... ... %02x %02x %02x %02x\n",
+               skb->data[0], skb->data[1], skb->data[2], skb->data[3], 
+               skb->data[skb->len - 4], skb->data[skb->len - 3],
+               skb->data[skb->len - 2], skb->data[skb->len - 1]);
+
+       priv->stats.rx_packets++;
+       priv->stats.rx_bytes += data_len;
+
+       if (in_interrupt())
+               netif_rx(skb);
+       else
+               netif_rx_ni(skb);
+
+       RDA5890_DBGLAP(RDA5890_DA_TXRX, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+}
+
+
+int rda5890_host_to_card(struct rda5890_private *priv, 
+               char *packet, unsigned short packet_len, unsigned char packet_type)
+{
+       int ret = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s <<< \n", __func__);
+
+       ret = priv->hw_host_to_card(priv, packet, packet_len, packet_type);
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s >>> \n", __func__);
+
+       return ret;
+}
+
+
+int rda5890_data_tx(struct rda5890_private *priv, 
+               struct sk_buff *skb, struct net_device *dev)
+{
+       int ret;
+       char *pkt_data;
+       uint16_t pkt_len;
+       char buf[ETH_FRAME_LEN + 2];
+       uint16_t data_len;
+
+       RDA5890_DBGLAP(RDA5890_DA_TXRX, RDA5890_DL_DEBUG,
+               "%s <<< \n", __func__);
+
+       ret = NETDEV_TX_OK;
+
+       if (!skb->len || (skb->len > ETH_FRAME_LEN)) {
+               priv->stats.tx_dropped++;
+               priv->stats.tx_errors++;
+               goto free;
+       }
+
+       pkt_data = skb->data;
+       pkt_len = skb->len;
+
+       RDA5890_DBGLAP(RDA5890_DA_TXRX, RDA5890_DL_TRACE,
+               "netif tx len %d\n", pkt_len);
+       RDA5890_DBGLAP(RDA5890_DA_TXRX, RDA5890_DL_TRACE,
+               "%02x %02x %02x %02x ... ... %02x %02x %02x %02x\n",
+               skb->data[0], skb->data[1], skb->data[2], skb->data[3], 
+               skb->data[skb->len - 4], skb->data[skb->len - 3],
+               skb->data[skb->len - 2], skb->data[skb->len - 1]);
+
+       /* FIXME: we can save this memcpy by adding header inside the sdio driver */
+       memcpy(buf + 2, pkt_data, pkt_len);
+       data_len = pkt_len + 2;
+       buf[0] = (char)(data_len&0xFF);
+       buf[1] = (char)((data_len>>8)&0x0F);
+       buf[1] |= 0x10;  // for DataOut 0x1
+
+       //RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_NORM,
+       //      "sdio tx len %d\n", data_len);
+       //RDA5890_DBGLAP(RDA5890_DA_ETHER, RDA5890_DL_NORM,
+       //      "%02x %02x %02x %02x ... ... %02x %02x %02x %02x\n",
+       //      buf[0], buf[1], buf[2], buf[3], 
+       //      buf[data_len - 4], buf[data_len - 3],
+       //      buf[data_len - 2], buf[data_len - 1]);
+
+       ret = rda5890_host_to_card(priv, buf, data_len, DATA_REQUEST_PACKET);
+       if (ret) {
+               RDA5890_ERRP("host_to_card send failed, ret = %d\n", ret);
+               goto free;
+       }
+
+       priv->stats.tx_packets++;
+       priv->stats.tx_bytes += skb->len;
+
+       dev->trans_start = jiffies;
+
+ free:
+       /* free right away, since we do copy */
+       dev_kfree_skb_any(skb);
+
+       RDA5890_DBGLAP(RDA5890_DA_TXRX, RDA5890_DL_DEBUG,
+               "%s >>> \n", __func__);
+       return ret;
+}
+
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.h b/drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.h
new file mode 100755 (executable)
index 0000000..6f161b9
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _RDA5890_TXRX_H_
+#define _RDA5890_TXRX_H_
+
+int rda5890_host_to_card(struct rda5890_private *priv, 
+               char *packet, unsigned short packet_len, unsigned char packet_type);
+
+void rda5890_data_rx(struct rda5890_private *priv, 
+               char *data, unsigned short data_len);
+int rda5890_data_tx(struct rda5890_private *priv, 
+               struct sk_buff *skb, struct net_device *dev);
+
+#endif
+
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.c b/drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.c
new file mode 100755 (executable)
index 0000000..ae06196
--- /dev/null
@@ -0,0 +1,2562 @@
+/**
+  * This file contains ioctl functions
+  */
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/bitops.h>
+
+#include <net/iw_handler.h>
+#include <linux/etherdevice.h>
+
+#include "rda5890_defs.h"
+#include "rda5890_dev.h"
+#include "rda5890_ioctl.h"
+#include "rda5890_wid.h"
+#include "rda5890_wext.h"
+
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+#define WLAN_AUTH_FT 2
+#define WLAN_AUTH_LEAP 128
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_ESS            (1<<0)
+#define WLAN_CAPABILITY_IBSS           (1<<1)
+#define WLAN_CAPABILITY_CF_POLLABLE    (1<<2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST        (1<<3)
+#define WLAN_CAPABILITY_PRIVACY                (1<<4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
+#define WLAN_CAPABILITY_PBCC           (1<<6)
+#define WLAN_CAPABILITY_CHANNEL_AGILITY        (1<<7)
+#define IW_AUTH_ALG_WAPI      0x08
+#define IW_ENCODE_ALG_WAPI    0x80
+
+static int rda5890_get_name(struct net_device *dev, struct iw_request_info *info,
+                        char *cwrq, char *extra)
+{
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       /* We could add support for 802.11n here as needed. Jean II */
+       snprintf(cwrq, IFNAMSIZ, "IEEE 802.11b/g");
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_freq(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_freq *fwrq, char *extra)
+{
+       //struct rda5890_private *priv =  netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       fwrq->m = (long)2437 * 100000;
+       fwrq->e = 1;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_wap(struct net_device *dev, struct iw_request_info *info,
+                       struct sockaddr *awrq, char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *) netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       if (priv->connect_status == MAC_CONNECTED) {
+               memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN);
+       } else {
+               memset(awrq->sa_data, 0, ETH_ALEN);
+       }
+       awrq->sa_family = ARPHRD_ETHER;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_set_nick(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_point *dwrq, char *extra)
+{
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_nick(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_point *dwrq, char *extra)
+{
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_set_rts(struct net_device *dev, struct iw_request_info *info,
+                       struct iw_param *vwrq, char *extra)
+{
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_rts(struct net_device *dev, struct iw_request_info *info,
+                       struct iw_param *vwrq, char *extra)
+{
+       //struct rda5890_private *priv = netdev_priv(dev);
+       int ret = 0;
+       u16 val = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       val = 600;
+
+       vwrq->value = val;
+       vwrq->disabled = val > RDA5890_RTS_MAX_VALUE; /* min rts value is 0 */
+       vwrq->fixed = 1;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return ret;
+}
+
+static int rda5890_set_frag(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_param *vwrq, char *extra)
+{
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_frag(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_param *vwrq, char *extra)
+{
+       //struct rda5890_private *priv = netdev_priv(dev);
+       int ret = 0;
+       u16 val = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       val = 1460;
+
+       vwrq->value = val;
+       vwrq->disabled = ((val < RDA5890_FRAG_MIN_VALUE)
+                         || (val > RDA5890_FRAG_MAX_VALUE));
+       vwrq->fixed = 1;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return ret;
+}
+
+static int rda5890_get_mode(struct net_device *dev,
+                        struct iw_request_info *info, u32 * uwrq, char *extra)
+{
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       *uwrq = IW_MODE_INFRA;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_txpow(struct net_device *dev,
+                         struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       //struct rda5890_private *priv = netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       vwrq->value = 20;  // in dbm
+       vwrq->fixed = 1;
+       vwrq->disabled = 0;
+       vwrq->flags = IW_TXPOW_DBM;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_set_retry(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_retry(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       //struct rda5890_private *priv = netdev_priv(dev);
+       int ret = 0;
+       u16 val = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       vwrq->disabled = 0;
+
+       if (vwrq->flags & IW_RETRY_LONG) {
+               val = 7;
+
+               /* Subtract 1 to convert try count to retry count */
+               vwrq->value = val - 1;
+               vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+       } else {
+               val = 6;
+
+               /* Subtract 1 to convert try count to retry count */
+               vwrq->value = val - 1;
+               vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return ret;
+}
+
+/** 
+ * 802.11b/g supported bitrates (in 500Kb/s units) 
+ */
+u8 rda5890_bg_rates[MAX_RATES] =
+       { 0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 
+       0x24, 0x30, 0x48, 0x60, 0x6c,0x00, 0x00 }; 
+
+u16 rda5890_nr_chan = 11;
+
+/**
+ *  @brief Get Range Info
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 --success, otherwise fail
+ */
+static int rda5890_get_range(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_point *dwrq, char *extra)
+{
+       //struct rda5890_private *priv = netdev_priv(dev);
+       struct iw_range *range = (struct iw_range *)extra;
+       int i;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       dwrq->length = sizeof(struct iw_range);
+       memset(range, 0, sizeof(struct iw_range));
+
+       range->min_nwid = 0;
+       range->max_nwid = 0;
+
+       range->num_bitrates = sizeof(rda5890_bg_rates);
+       for (i = 0; i < range->num_bitrates; i++)
+               range->bitrate[i] = rda5890_bg_rates[i] * 500000;
+       range->num_bitrates = i;
+
+       range->num_frequency = 0;
+
+       range->scan_capa = IW_SCAN_CAPA_ESSID;
+
+       for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+                    && (i < rda5890_nr_chan); i++) {
+                       range->freq[range->num_frequency].i = (long)(i + 1);
+                       range->freq[range->num_frequency].m =
+                           (long)((2412 + 5 * i) * 100000);
+                       range->freq[range->num_frequency].e = 1;
+                       range->num_frequency++;
+       }
+
+       range->num_channels = range->num_frequency;
+
+       /*
+        * Set an indication of the max TCP throughput in bit/s that we can
+        * expect using this interface
+        */
+       range->throughput = 5000 * 1000;
+
+       range->min_rts = RDA5890_RTS_MIN_VALUE;
+       range->max_rts = RDA5890_RTS_MAX_VALUE;
+       range->min_frag = RDA5890_FRAG_MIN_VALUE;
+       range->max_frag = RDA5890_FRAG_MAX_VALUE;
+
+       range->encoding_size[0] = 5;
+       range->encoding_size[1] = 13;
+       range->num_encoding_sizes = 2;
+       range->max_encoding_tokens = 4;
+
+       /*
+        * Right now we support only "iwconfig ethX power on|off"
+        */
+       range->pm_capa = IW_POWER_ON;
+
+       /*
+        * Minimum version we recommend
+        */
+       range->we_version_source = 15;
+
+       /*
+        * Version we are compiled with
+        */
+       range->we_version_compiled = WIRELESS_EXT;
+
+       range->retry_capa = IW_RETRY_LIMIT;
+       range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+
+       range->min_retry = 0;
+       range->max_retry = 14;
+
+       /*
+        * Set the qual, level and noise range values
+        */
+       range->max_qual.qual = 100;
+       range->max_qual.level = 0;
+       range->max_qual.noise = 0;
+       range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+       range->avg_qual.qual = 70;
+       /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
+       range->avg_qual.level = 0;
+       range->avg_qual.noise = 0;
+       range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+       range->sensitivity = 0;
+
+       /* Setup the supported power level ranges */
+       memset(range->txpower, 0, sizeof(range->txpower));
+       range->txpower_capa = IW_TXPOW_DBM | IW_TXPOW_RANGE;
+       range->txpower[0] = 0;
+       range->txpower[1] = 20;
+       range->num_txpower = 2;
+
+       range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+                               IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+                               IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+       range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
+       range->enc_capa =   IW_ENC_CAPA_WPA
+                                 | IW_ENC_CAPA_WPA2
+                                 | IW_ENC_CAPA_CIPHER_TKIP
+                                 | IW_ENC_CAPA_CIPHER_CCMP;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_set_power(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       //struct rda5890_private *priv = netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_power(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       //struct rda5890_private *priv = netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       vwrq->value = 0;
+       vwrq->flags = 0;
+       vwrq->disabled = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_update_bss_stats(struct rda5890_private *priv)
+{
+       int ret = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+       
+       memcpy(priv->curbssparams.ssid, 
+               priv->assoc_ssid, sizeof(priv->curbssparams.ssid));
+
+    if(priv->scan_running == 1)
+        return ret;
+
+       ret = rda5890_get_bssid(priv, priv->curbssparams.bssid);
+       if (ret) {
+               RDA5890_ERRP("rda5890_get_bssid, ret = %d\n", ret);
+               goto out;
+       }
+
+#if 0
+       ret = rda5890_get_channel(priv, &priv->curbssparams.channel);
+       if (ret) {
+               RDA5890_ERRP("rda5890_get_channel, ret = %d\n", ret);
+               goto out;
+       }
+#endif
+
+       ret = rda5890_get_rssi(priv, &priv->curbssparams.rssi);
+       if (ret) {
+               RDA5890_ERRP("rda5890_get_rssi, ret = %d\n", ret);
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<< ch = %d  rssi = %d\n", __func__, priv->curbssparams.channel, priv->curbssparams.rssi);
+
+out:
+       return ret;
+}
+
+static struct iw_statistics *rda5890_get_wireless_stats(struct net_device *dev)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int stats_valid = 0;
+       u8 snr;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       if (priv->connect_status != MAC_CONNECTED)
+               goto out;
+
+       rda5890_update_bss_stats(priv);
+
+       priv->wstats.miss.beacon = 0;
+       priv->wstats.discard.retries = 0;
+       priv->wstats.qual.level = priv->curbssparams.rssi > 127? priv->curbssparams.rssi - 271
+        :priv->curbssparams.rssi - 15;
+    
+       snr = priv->wstats.qual.level - RDA5890_NF_DEFAULT_SCAN_VALUE;
+       priv->wstats.qual.qual =
+               (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - snr) *
+                (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - snr))) /
+               (RSSI_DIFF * RSSI_DIFF);
+       if (priv->wstats.qual.qual > 100)
+               priv->wstats.qual.qual = 100;
+       priv->wstats.qual.noise = RDA5890_NF_DEFAULT_SCAN_VALUE;
+       priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+       stats_valid = 1;
+
+out:
+       if (!stats_valid) {
+               priv->wstats.miss.beacon = 0;
+               priv->wstats.discard.retries = 0;
+               priv->wstats.qual.qual = 0;
+               priv->wstats.qual.level = 0;
+               priv->wstats.qual.noise = 0;
+               priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED;
+               priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID |
+                   IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return &priv->wstats;
+}
+
+static int rda5890_set_freq(struct net_device *dev, struct iw_request_info *info,
+                 struct iw_freq *fwrq, char *extra)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_set_rate(struct net_device *dev, struct iw_request_info *info,
+                 struct iw_param *vwrq, char *extra)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_rate(struct net_device *dev, struct iw_request_info *info,
+                 struct iw_param *vwrq, char *extra)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       vwrq->fixed = 0;
+       vwrq->value = 108*500000;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_set_mode(struct net_device *dev,
+                 struct iw_request_info *info, u32 * uwrq, char *extra)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+
+/**
+ *  @brief Get Encryption key
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 --success, otherwise fail
+ */
+static int rda5890_get_encode(struct net_device *dev,
+                          struct iw_request_info *info,
+                          struct iw_point *dwrq, u8 * extra)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+/**
+ *  @brief Set Encryption key
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 --success, otherwise fail
+ */
+static int rda5890_set_encode(struct net_device *dev,
+                   struct iw_request_info *info,
+                   struct iw_point *dwrq, char *extra)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+    
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+/**
+ *  @brief Set Encryption key (internal)
+ *
+ *  @param priv                        A pointer to private card structure
+ *  @param key_material                A pointer to key material
+ *  @param key_length          length of key material
+ *  @param index               key index to set
+ *  @param set_tx_key          Force set TX key (1 = yes, 0 = no)
+ *  @return                    0 --success, otherwise fail
+ */
+static int copy_wep_key(struct rda5890_private *priv,
+                           const char *key_material,
+                           u16 key_length,
+                           u16 index,
+                           int set_tx_key)
+{
+       int ret = 0;
+       struct enc_key *pkey;
+
+       /* Paranoid validation of key index */
+       if (index > 3) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* validate max key length */
+       if (key_length > KEY_LEN_WEP_104) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (key_length == KEY_LEN_WEP_40) {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "WEP40 : %02x%02x%02x%02x%02x\n",
+                       key_material[0], key_material[1], key_material[2],
+                       key_material[3], key_material[4]);
+       }
+       else if (key_length == KEY_LEN_WEP_104) {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "WEP104 : %02x%02x%02x%02x%02x"
+                       " %02x%02x%02x%02x%02x"
+                       " %02x%02x%02x\n",
+                       key_material[0], key_material[1], key_material[2],
+                       key_material[3], key_material[4], key_material[5],
+                       key_material[6], key_material[7], key_material[8],
+                       key_material[9], key_material[10], key_material[11],
+                       key_material[12]);
+       }
+       else {
+               RDA5890_ERRP("Error in WEP Key length %d\n", key_length);
+       }
+
+       pkey = &priv->wep_keys[index];
+
+       if (key_length > 0) {
+               memset(pkey, 0, sizeof(struct enc_key));
+               pkey->type = KEY_TYPE_ID_WEP;
+
+               /* Standardize the key length */
+               pkey->len = (key_length > KEY_LEN_WEP_40) ?
+                               KEY_LEN_WEP_104 : KEY_LEN_WEP_40;
+               memcpy(pkey->key, key_material, key_length);
+       }
+
+       if (set_tx_key) {
+               /* Ensure the chosen key is valid */
+               if (!pkey->len) {
+                       RDA5890_ERRP("key not set, so cannot enable it\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+               priv->wep_tx_keyidx = index;
+       }
+
+       priv->secinfo.wep_enabled = 1;
+
+out:
+       return ret;
+}
+
+static int validate_key_index(u16 def_index, u16 raw_index,
+                             u16 *out_index, u16 *is_default)
+{
+       if (!out_index || !is_default)
+               return -EINVAL;
+
+       /* Verify index if present, otherwise use default TX key index */
+       if (raw_index > 0) {
+               if (raw_index > 4)
+                       return -EINVAL;
+               *out_index = raw_index - 1;
+       } else {
+               *out_index = def_index;
+               *is_default = 1;
+       }
+       return 0;
+}
+
+static void disable_wep(struct rda5890_private *priv)
+{
+       int i;
+
+       /* Set Open System auth mode */
+       priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+
+       /* Clear WEP keys and mark WEP as disabled */
+       priv->secinfo.wep_enabled = 0;
+       for (i = 0; i < 4; i++)
+               priv->wep_keys[i].len = 0;
+
+       set_bit(ASSOC_FLAG_SECINFO, &priv->assoc_flags);
+       set_bit(ASSOC_FLAG_WEP_KEYS, &priv->assoc_flags);
+}
+
+static void disable_wpa(struct rda5890_private *priv)
+{
+       memset(&priv->wpa_mcast_key, 0, sizeof (struct enc_key));
+       priv->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST;
+       set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &priv->assoc_flags);
+
+       memset(&priv->wpa_unicast_key, 0, sizeof (struct enc_key));
+       priv->wpa_unicast_key.flags = KEY_INFO_WPA_UNICAST;
+       set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &priv->assoc_flags);
+
+       priv->secinfo.WPAenabled = 0;
+       priv->secinfo.WPA2enabled = 0;
+    priv->secinfo.cipther_type = 0;
+    priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+       set_bit(ASSOC_FLAG_SECINFO, &priv->assoc_flags);
+}
+
+/**
+ *  @brief Get Extended Encryption key (WPA/802.1x and WEP)
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 on success, otherwise failure
+ */
+static int rda5890_get_encodeext(struct net_device *dev,
+                             struct iw_request_info *info,
+                             struct iw_point *dwrq,
+                             char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int ret = -EINVAL;
+       struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+       int index, max_key_len;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       max_key_len = dwrq->length - sizeof(*ext);
+       if (max_key_len < 0)
+               goto out;
+
+       index = dwrq->flags & IW_ENCODE_INDEX;
+       if (index) {
+               if (index < 1 || index > 4)
+                       goto out;
+               index--;
+       } else {
+               index = priv->wep_tx_keyidx;
+       }
+
+       if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
+           ext->alg != IW_ENCODE_ALG_WEP) {
+               if (index != 0)
+                       goto out;
+       }
+
+       dwrq->flags = index + 1;
+       memset(ext, 0, sizeof(*ext));
+
+       if (   !priv->secinfo.wep_enabled
+           && !priv->secinfo.WPAenabled
+           && !priv->secinfo.WPA2enabled) {
+               ext->alg = IW_ENCODE_ALG_NONE;
+               ext->key_len = 0;
+               dwrq->flags |= IW_ENCODE_DISABLED;
+       } else {
+               u8 *key = NULL;
+
+               if (   priv->secinfo.wep_enabled
+                   && !priv->secinfo.WPAenabled
+                   && !priv->secinfo.WPA2enabled) {
+                       /* WEP */
+                       ext->alg = IW_ENCODE_ALG_WEP;
+                       ext->key_len = priv->wep_keys[index].len;
+                       key = &priv->wep_keys[index].key[0];
+               } else if (   !priv->secinfo.wep_enabled
+                          && (priv->secinfo.WPAenabled ||
+                              priv->secinfo.WPA2enabled)) {
+                       /* WPA */
+                       struct enc_key * pkey = NULL;
+
+                       if (   priv->wpa_mcast_key.len
+                           && (priv->wpa_mcast_key.flags & KEY_INFO_WPA_ENABLED))
+                               pkey = &priv->wpa_mcast_key;
+                       else if (   priv->wpa_unicast_key.len
+                                && (priv->wpa_unicast_key.flags & KEY_INFO_WPA_ENABLED))
+                               pkey = &priv->wpa_unicast_key;
+
+                       if (pkey) {
+                               if (pkey->type == KEY_TYPE_ID_AES) {
+                                       ext->alg = IW_ENCODE_ALG_CCMP;
+                               } else {
+                                       ext->alg = IW_ENCODE_ALG_TKIP;
+                               }
+                               ext->key_len = pkey->len;
+                               key = &pkey->key[0];
+                       } else {
+                               ext->alg = IW_ENCODE_ALG_TKIP;
+                               ext->key_len = 0;
+                       }
+               } else {
+                       goto out;
+               }
+
+               if (ext->key_len > max_key_len) {
+                       ret = -E2BIG;
+                       goto out;
+               }
+
+               if (ext->key_len)
+                       memcpy(ext->key, key, ext->key_len);
+               else
+                       dwrq->flags |= IW_ENCODE_NOKEY;
+               dwrq->flags |= IW_ENCODE_ENABLED;
+       }
+       ret = 0;
+
+out:
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+/**
+ *  @brief Set Encryption key Extended (WPA/802.1x and WEP)
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 --success, otherwise fail
+ */
+static int rda5890_set_encodeext(struct net_device *dev,
+                             struct iw_request_info *info,
+                             struct iw_point *dwrq,
+                             char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int ret = 0;
+       struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+       int alg = ext->alg;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "NO SEC\n");
+        if(test_bit(ASSOC_FLAG_ASSOC_START ,&priv->assoc_flags))
+        {
+            if(priv->imode != 3 && priv->imode != 5)
+                   disable_wep (priv);
+        }
+               disable_wpa (priv);
+       } else if (alg == IW_ENCODE_ALG_WEP) {
+               u16 is_default = 0, index, set_tx_key = 0;
+
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "WEP, flags = 0x%04x\n", dwrq->flags);
+
+               ret = validate_key_index(priv->wep_tx_keyidx,
+                                        (dwrq->flags & IW_ENCODE_INDEX),
+                                        &index, &is_default);
+               if (ret)
+                       goto out;
+
+               /* If WEP isn't enabled, or if there is no key data but a valid
+                * index, or if the set-TX-key flag was passed, set the TX key.
+                */
+               if (   !priv->secinfo.wep_enabled
+                   || (dwrq->length == 0 && !is_default)
+                   || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
+                       set_tx_key = 1;
+
+               /* Copy key to driver */
+               ret = copy_wep_key(priv, ext->key, ext->key_len, index, set_tx_key);
+               if (ret)
+                       goto out;
+
+               /* Set Key to Mac */
+               /* Move to assoc_helper_secinfo(), wep_key need to be set after imode */
+               //ret = rda5890_set_wepkey(priv, index, ext->key, ext->key_len);
+               //if (ret)
+               //      goto out;
+
+               if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+                       priv->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
+               } else if (dwrq->flags & IW_ENCODE_OPEN) {
+                       priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+               }
+
+               /* Mark the various WEP bits as modified */
+               set_bit(ASSOC_FLAG_SECINFO, &priv->assoc_flags);
+               if (dwrq->length)
+                       set_bit(ASSOC_FLAG_WEP_KEYS, &priv->assoc_flags);
+               if (set_tx_key)
+                       set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &priv->assoc_flags);
+       } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
+               struct enc_key * pkey;
+
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "TKIP or CCMP, flags = 0x%04x, alg = %d\n", dwrq->flags, alg);
+
+               /* validate key length */
+               if (((alg == IW_ENCODE_ALG_TKIP)
+                       && (ext->key_len != KEY_LEN_WPA_TKIP))
+                   || ((alg == IW_ENCODE_ALG_CCMP)
+                       && (ext->key_len != KEY_LEN_WPA_AES))) {
+                               RDA5890_ERRP("invalid size %d for key of alg, type %d\n",
+                                      ext->key_len, alg);
+                               ret = -EINVAL;
+                               goto out;
+               }
+
+               /* Copy key to driver */
+               if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+                       pkey = &priv->wpa_mcast_key;
+                       set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &priv->assoc_flags);
+               } else {
+                       pkey = &priv->wpa_unicast_key;
+                       set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &priv->assoc_flags);
+               }
+
+               memset(pkey, 0, sizeof (struct enc_key));
+               memcpy(pkey->key, ext->key, ext->key_len);
+               pkey->len = ext->key_len;
+               if (pkey->len)
+                       pkey->flags |= KEY_INFO_WPA_ENABLED;
+
+               /* Do this after zeroing key structure */
+               if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+                       pkey->flags |= KEY_INFO_WPA_MCAST;
+               } else {
+                       pkey->flags |= KEY_INFO_WPA_UNICAST;
+               }
+
+               if (alg == IW_ENCODE_ALG_TKIP) {
+                       pkey->type = KEY_TYPE_ID_TKIP;
+                       if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+                           && !(priv->imode & (BIT6))) {
+                               RDA5890_ERRP("imode [0x%x] not match with cipher alg TKIP\n",
+                                       priv->imode);
+                       }
+               } else if (alg == IW_ENCODE_ALG_CCMP) {
+                       pkey->type = KEY_TYPE_ID_AES;
+                       if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+                           && !(priv->imode & (BIT5))) {
+                               RDA5890_ERRP("imode [0x%x] not match with cipher alg CCMP\n",
+                                       priv->imode);
+                       }
+               }
+
+               /* If WPA isn't enabled yet, do that now */
+               if (   priv->secinfo.WPAenabled == 0
+                   && priv->secinfo.WPA2enabled == 0) {
+                       priv->secinfo.WPAenabled = 1;
+                       priv->secinfo.WPA2enabled = 1;
+                       set_bit(ASSOC_FLAG_SECINFO, &priv->assoc_flags);
+               }
+
+               /* Set Keys to MAC*/
+               if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+                       /* Set GTK */
+                       /* 
+                        *Always use key_id = 1 for now
+                        * need to toggle among 1, 2, 3
+                        */
+                       ret = rda5890_set_gtk(priv, 1, ext->tx_seq, IW_ENCODE_SEQ_MAX_SIZE,
+                               pkey->key, pkey->len);
+                       if (ret)
+                               goto out;
+               } else {
+                       pkey->flags |= KEY_INFO_WPA_UNICAST;
+                       /* Set PTK */
+                       ret = rda5890_set_ptk(priv, pkey->key, pkey->len);
+                       if (ret)
+                               goto out;
+               }
+
+               /* Only disable wep if necessary: can't waste time here. */
+               disable_wep(priv);
+       } else if (alg == IW_ENCODE_ALG_WAPI) { //wapi
+               if(ext->key_len != 32)
+                       goto out;
+
+               priv->is_wapi = 1;
+
+               /* Set Keys to MAC*/
+               if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+                       unsigned char tmp[8];
+                       /* Set GTK */
+                       /* 
+                        * Always use key_id = 1 for now
+                        * need to toggle among 1, 2, 3
+                        */
+                       ret = rda5890_set_gtk(priv, 1, tmp, IW_ENCODE_SEQ_MAX_SIZE,
+                               ext->key, ext->key_len);
+                       if (ret)
+                               goto out;
+               } else {
+                       /* Set PTK */
+                       ret = rda5890_set_ptk(priv, ext->key, ext->key_len);
+                       if (ret)
+                               goto out;
+               }
+       }
+
+out:
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+/**
+ *  @brief PMKSA cache operation (WPA/802.1x and WEP)
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 on success, otherwise failure
+ */
+static int rda5890_set_pmksa(struct net_device *dev,
+                             struct iw_request_info *info,
+                             struct iw_point *dwrq,
+                             char *extra)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+static int rda5890_set_genie(struct net_device *dev,
+                         struct iw_request_info *info,
+                         struct iw_point *dwrq,
+                         char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int ret = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       if(extra[0] == 0x44) //wapi ie
+       {
+               unsigned char ie_len = extra[1] + 2;
+               rda5890_generic_set_str(priv, WID_WAPI_ASSOC_IE, extra ,ie_len);
+               goto out;
+       }
+          
+       if (dwrq->length > MAX_WPA_IE_LEN ||
+           (dwrq->length && extra == NULL)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (dwrq->length) {
+               memcpy(&priv->wpa_ie[0], extra, dwrq->length);
+               priv->wpa_ie_len = dwrq->length;
+       } else {
+               memset(&priv->wpa_ie[0], 0, sizeof(priv->wpa_ie));
+               priv->wpa_ie_len = 0;
+       }
+
+out:
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return ret;
+}
+
+static int rda5890_get_genie(struct net_device *dev,
+                         struct iw_request_info *info,
+                         struct iw_point *dwrq,
+                         char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int ret = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       if (priv->wpa_ie_len == 0) {
+               dwrq->length = 0;
+               goto out;
+       }
+
+       if (dwrq->length < priv->wpa_ie_len) {
+               ret = -E2BIG;
+               goto out;
+       }
+
+       dwrq->length = priv->wpa_ie_len;
+       memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len);
+
+out:
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return ret;
+}
+
+static int rda5890_set_auth(struct net_device *dev,
+                        struct iw_request_info *info,
+                        struct iw_param *dwrq,
+                        char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int ret = 0;
+       int updated = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+               "flags = 0x%04x, value = 0x%x\n", dwrq->flags, dwrq->value);
+
+       switch (dwrq->flags & IW_AUTH_INDEX) {
+       case IW_AUTH_CIPHER_PAIRWISE:
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+        if (dwrq->value & (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) 
+        {
+             RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                   "WEP Selected \n");
+                       priv->secinfo.wep_enabled = 1;
+               if(dwrq->value & IW_AUTH_CIPHER_WEP104)
+                   priv->secinfo.cipther_type |= IW_AUTH_CIPHER_WEP104;
+               else if(dwrq->value & IW_AUTH_CIPHER_WEP40)
+                  priv->secinfo.cipther_type |= IW_AUTH_CIPHER_WEP40;
+               }
+               if (dwrq->value & IW_AUTH_CIPHER_TKIP) {
+                    RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "IW_AUTH_CIPHER_TKIP \n");
+                     priv->secinfo.cipther_type |= IW_AUTH_CIPHER_TKIP;
+               }
+               if (dwrq->value & IW_AUTH_CIPHER_CCMP) {
+                    RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "IW_AUTH_CIPHER_CCMP \n");
+                    priv->secinfo.cipther_type |= IW_AUTH_CIPHER_CCMP;
+               }
+               if (dwrq->value & IW_AUTH_CIPHER_NONE) {
+                    RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "OPEN System \n");
+            priv->secinfo.cipther_type = IW_AUTH_CIPHER_NONE;
+               }
+    break;
+#endif
+       case IW_AUTH_TKIP_COUNTERMEASURES:
+       case IW_AUTH_CIPHER_GROUP:
+       case IW_AUTH_DROP_UNENCRYPTED:
+               /*
+                * rda5890 does not use these parameters
+                */
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "DO NOT USE\n");
+               break;
+
+       case IW_AUTH_KEY_MGMT:
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "KEY_MGMT, val = %d\n", dwrq->value);
+               priv->secinfo.key_mgmt = dwrq->value;
+               updated = 1;
+               break;
+
+       case IW_AUTH_WPA_VERSION:
+               if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "WPA_VERSION, DISABLED\n");
+                       priv->secinfo.WPAenabled = 0;
+                       priv->secinfo.WPA2enabled = 0;
+                       disable_wpa (priv);
+               }
+               if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "WPA_VERSION, WPA\n");
+                       priv->secinfo.WPAenabled = 1;
+                       priv->secinfo.wep_enabled = 0;
+                       priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+               }
+               if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "WPA_VERSION, WPA2\n");
+                       priv->secinfo.WPA2enabled = 1;
+                       priv->secinfo.wep_enabled = 0;
+                       priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+               }
+               updated = 1;
+               break;
+
+       case IW_AUTH_80211_AUTH_ALG:
+               if (dwrq->value & IW_AUTH_ALG_SHARED_KEY || 
+            dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) 
+        {
+            if(dwrq->value & IW_AUTH_ALG_SHARED_KEY )
+            {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "80211_AUTH_ALG, SHARED_KEY\n");
+                       priv->secinfo.auth_mode |= IW_AUTH_ALG_SHARED_KEY;
+               }
+               if(dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "80211_AUTH_ALG, OPEN\n");
+                       priv->secinfo.auth_mode |= IW_AUTH_ALG_OPEN_SYSTEM;
+               }
+        }
+               else if(dwrq->value & IW_AUTH_ALG_LEAP) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "80211_AUTH_ALG, LEAP\n");
+                       priv->secinfo.auth_mode = IW_AUTH_ALG_LEAP;
+               }
+        else if(dwrq->value & IW_AUTH_ALG_WAPI) {
+                       priv->secinfo.auth_mode = IW_AUTH_ALG_WAPI;
+               }      
+               else{
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "80211_AUTH_ALG, unknown\n");
+                       ret = -EINVAL;
+               }
+               updated = 1;
+               break;
+
+       case IW_AUTH_WPA_ENABLED:
+               if (dwrq->value) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "WPA_ENABLED, value = 0x%x\n", dwrq->value);
+                       if (!priv->secinfo.WPAenabled &&
+                           !priv->secinfo.WPA2enabled) {
+                               priv->secinfo.WPAenabled = 1;
+                               priv->secinfo.WPA2enabled = 1;
+                               priv->secinfo.wep_enabled = 0;
+                               priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+                       }
+               } else {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "WPA_ENABLED, value = ZERO\n");
+                       priv->secinfo.WPAenabled = 0;
+                       priv->secinfo.WPA2enabled = 0;
+                       disable_wpa (priv);
+               }
+               updated = 1;
+               break;
+
+       default:
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "NOT SUPPORT\n");
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       if (ret == 0) {
+               if (updated) {
+                       set_bit(ASSOC_FLAG_SECINFO, &priv->assoc_flags);
+               }
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return ret;
+}
+
+static int rda5890_get_auth(struct net_device *dev,
+                        struct iw_request_info *info,
+                        struct iw_param *dwrq,
+                        char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int ret = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+
+       switch (dwrq->flags & IW_AUTH_INDEX) {
+       case IW_AUTH_KEY_MGMT:
+               dwrq->value = priv->secinfo.key_mgmt;
+               break;
+
+       case IW_AUTH_WPA_VERSION:
+               dwrq->value = 0;
+               if (priv->secinfo.WPAenabled)
+                       dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
+               if (priv->secinfo.WPA2enabled)
+                       dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
+               if (!dwrq->value)
+                       dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
+               break;
+
+       case IW_AUTH_80211_AUTH_ALG:
+               dwrq->value = priv->secinfo.auth_mode;
+               break;
+
+       case IW_AUTH_WPA_ENABLED:
+               if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled)
+                       dwrq->value = 1;
+               break;
+
+       default:
+               ret = -EOPNOTSUPP;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+       return ret;
+}
+
+static int rda5890_set_txpow(struct net_device *dev, struct iw_request_info *info,
+                  struct iw_param *vwrq, char *extra)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+       return 0;
+}
+
+static int rda5890_get_essid(struct net_device *dev, struct iw_request_info *info,
+                  struct iw_point *dwrq, char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+
+
+       memcpy(extra, priv->curbssparams.ssid,
+              strlen(priv->curbssparams.ssid));
+       dwrq->length = strlen(priv->curbssparams.ssid);
+    extra[dwrq->length] = '\0';
+
+       /*
+        * If none, we may want to get the one that was set
+        */
+
+       dwrq->flags = 1;        /* active */
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>> \n", __func__);
+       return 0;
+}
+
+void rda5890_indicate_disconnected(struct rda5890_private *priv)
+{
+       union iwreq_data wrqu;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_NORM, "%s <<<\n", __func__);
+
+       memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+
+    /*report disconnect to upper layer*/
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_NORM, "%s >>>\n", __func__);   
+}
+
+void rda5890_indicate_connected(struct rda5890_private *priv)
+{
+       union iwreq_data wrqu;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_NORM, "%s <<<\n", __func__);
+
+       memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_NORM, "%s >>>\n", __func__);
+}
+
+void rda5890_assoc_done_worker(struct work_struct *work)
+{
+    u8 bssid[6], zero_bssid[6];
+       struct rda5890_private *priv = container_of(work, struct rda5890_private,
+               assoc_done_work.work);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<< \n", __func__);
+
+    memset(bssid, 0, sizeof(bssid));
+    memset(zero_bssid, 0, sizeof(zero_bssid));
+    rda5890_get_bssid(priv, bssid);
+
+    if(memcmp(bssid, zero_bssid, sizeof(zero_bssid)))
+    {
+        memcpy(priv->curbssparams.bssid, bssid, sizeof(bssid));
+    }
+
+    rda5890_get_rssi(priv, &priv->curbssparams.rssi);
+    priv->curbssparams.rssi = priv->curbssparams.rssi > 127? 
+        priv->curbssparams.rssi - 271: priv->curbssparams.rssi - 15;
+    
+    rda5990_assoc_power_save(priv);
+    clear_bit(ASSOC_FLAG_ASSOC_START, &priv->assoc_flags);
+    
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+}
+
+void rda5890_wlan_connect_worker(struct work_struct *work)
+{
+       struct rda5890_private *priv = container_of(work, struct rda5890_private,
+       wlan_connect_work.work);
+
+    rda5890_set_txrate(priv, 0);
+
+    if(priv)
+        clear_bit(ASSOC_FLAG_WLAN_CONNECTING ,&priv->assoc_flags);
+}
+
+//imode
+/* BIT0: 1 -> Security ON              0 -> OFF                          */
+/* BIT1: 1 -> WEP40  cypher supported  0 -> Not supported                */
+/* BIT2: 1 -> WEP104 cypher supported  0 -> Not supported                */
+/* BIT3: 1 -> WPA mode      supported  0 -> Not supported                */
+/* BIT4: 1 -> WPA2 (RSN)    supported  0 -> Not supported                */
+/* BIT5: 1 -> AES-CCMP cphr supported  0 -> Not supported                */
+/* BIT6: 1 -> TKIP   cypher supported  0 -> Not supported                */
+/* BIT7: 1 -> TSN           supported  0 -> Not supported                */
+
+//authtype
+/* BIT0: 1 -> OPEN SYSTEM  */
+/* BIT1: 1 -> SHARED KEY  */
+/* BIT3: 1 -> WPA RSN  802.1x*/
+/* BIT7: 1 -> WAPI   */
+static int assoc_helper_secinfo(struct rda5890_private *priv, 
+               struct bss_descriptor *assoc_bss)
+{
+       int ret = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+
+       /* set imode and key */
+       if (   !priv->secinfo.wep_enabled
+           && !priv->secinfo.WPAenabled
+           && !priv->secinfo.WPA2enabled) {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "%s, NO SEC\n", __func__);
+               priv->imode = 0;
+       } else {
+               u16 key_len = 0;
+               u16 i;
+               if (   priv->secinfo.wep_enabled
+                   && !priv->secinfo.WPAenabled
+                   && !priv->secinfo.WPA2enabled) {
+                       /* WEP */
+                       key_len = priv->wep_keys[0].len;
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "%s, WEP, len = %d\n", __func__, key_len * 8);
+                       if (key_len == KEY_LEN_WEP_40) {
+                               priv->imode = BIT0 | BIT1;
+                       }
+                       else if (key_len == KEY_LEN_WEP_104) {
+                               priv->imode = BIT0 | BIT2;
+                       }
+                       else {
+                               RDA5890_ERRP("Invalide WEP Key length %d\n", key_len);
+                               ret = -EINVAL;
+                               goto out;
+                       }
+               } else if (   !priv->secinfo.wep_enabled
+                          && (priv->secinfo.WPAenabled ||
+                              priv->secinfo.WPA2enabled)) {
+                       /* WPA */
+                       struct enc_key * pkey = NULL;
+
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "%s, WPA cp:%x \n", __func__, priv->secinfo.cipther_type);
+
+                       if (   priv->wpa_mcast_key.len
+                           && (priv->wpa_mcast_key.flags & KEY_INFO_WPA_ENABLED))
+                               pkey = &priv->wpa_mcast_key;
+                       else if (   priv->wpa_unicast_key.len
+                                && (priv->wpa_unicast_key.flags & KEY_INFO_WPA_ENABLED))
+                               pkey = &priv->wpa_unicast_key;
+
+                       //priv->imode = assoc_bss->data.dot11i_info;
+                       priv->imode = 0;
+                       /* turn on security */
+                       priv->imode |= (BIT0);
+                       priv->imode &= ~(BIT3 | BIT4);
+                       if (priv->secinfo.WPA2enabled)
+                               priv->imode |= (BIT4);
+                       else if (priv->secinfo.WPAenabled)
+                               priv->imode |= (BIT3);
+                       /* 
+                        * we don't know the cipher type by now
+                        * use dot11i_info to decide
+                        * and use CCMP if possible
+                        */
+                       priv->imode &= ~(BIT5 | BIT6);
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+                       if (priv->secinfo.cipther_type & IW_AUTH_CIPHER_CCMP)
+                               priv->imode |= BIT5;
+                       else if (priv->secinfo.cipther_type & IW_AUTH_CIPHER_TKIP)
+                               priv->imode |= BIT6;
+#else
+                       if (assoc_bss->data.dot11i_info & (BIT5))
+                               priv->imode |= BIT5;
+                       else if (assoc_bss->data.dot11i_info & (BIT6))
+                               priv->imode |= BIT6;
+#endif            
+               } else {
+                       RDA5890_ERRP("WEP and WPA/WPA2 enabled simutanously\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       /* set authtype */
+       if (priv->secinfo.auth_mode & IW_AUTH_ALG_OPEN_SYSTEM 
+        || priv->secinfo.auth_mode & IW_AUTH_ALG_SHARED_KEY)
+    {
+
+        if (priv->secinfo.auth_mode & IW_AUTH_ALG_OPEN_SYSTEM)
+            {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "%s, Open Auth, KEY_MGMT = %d, AUTH_ALG mode:%x\n", __func__, priv->secinfo.key_mgmt, priv->secinfo.auth_mode);
+                       if (priv->secinfo.key_mgmt == 0x01) {
+                               /* for 802.1x, set auth type to 0x04 */
+                               priv->authtype = BIT3;                          
+                       }
+                       else 
+                {
+                               priv->authtype = BIT0;
+                   }
+           }
+        else if(priv->secinfo.auth_mode & IW_AUTH_ALG_SHARED_KEY) 
+           {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "%s, Shared-Key Auth AUTH_ALG mode:%x \n", __func__, priv->secinfo.auth_mode);
+                       priv->authtype = BIT1;
+               }
+    }
+    else if (priv->secinfo.auth_mode == IW_AUTH_ALG_WAPI) {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "%s, Shared-Key Auth\n", __func__);
+               priv->authtype = IW_AUTH_ALG_WAPI;
+       }
+       else if (priv->secinfo.auth_mode == IW_AUTH_ALG_LEAP) {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "%s, LEAP Auth, not supported\n", __func__);
+               ret = -EINVAL;
+               goto out;
+       }
+       else {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "%s, Unknown Auth\n", __func__);
+               ret = -EINVAL;
+               goto out;
+       }
+
+out:
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>> \n", __func__);
+       return ret;
+}
+
+static struct bss_descriptor *get_bss_desc_from_scanlist(
+               struct rda5890_private *priv, unsigned char *ssid)
+{
+       struct bss_descriptor *iter_bss;
+       struct bss_descriptor *ret_bss = NULL;
+       /* report all bss to upper layer */
+       list_for_each_entry (iter_bss, &priv->network_list, list) {
+#ifdef         GET_SCAN_FROM_NETWORK_INFO
+        if (strcmp(iter_bss->ssid, ssid) == 0) {
+#else
+               if (strcmp(iter_bss->data.ssid, ssid) == 0) {
+#endif            
+                       ret_bss = iter_bss;
+                       break;
+               }
+       }
+       return ret_bss;
+}
+
+void rda5890_assoc_worker(struct work_struct *work)
+{
+    static char old_imode = 0xff, old_bssid[6], assoc_count = 0;
+       struct rda5890_private *priv = container_of(work, struct rda5890_private,
+               assoc_work.work);
+       int ret = 0;
+       struct bss_descriptor *assoc_bss;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       assoc_bss = get_bss_desc_from_scanlist(priv, priv->assoc_ssid);
+       if (assoc_bss == NULL) {
+               RDA5890_ERRP("****fail to find bss in the scan list\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+#ifdef  GET_SCAN_FROM_NETWORK_INFO
+       if(assoc_bss->rssi > 200)
+       {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "assoc_bss rssi =%d > 200\n", assoc_bss->rssi);
+           rda5890_rssi_up_to_200(priv);       
+       }
+#else
+       if(assoc_bss->data.rssi > 200)
+       {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "assoc_bss rssi =%d > 200\n", assoc_bss->data.rssi);
+           rda5890_rssi_up_to_200(priv);       
+       }
+#endif    
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+    priv->curbssparams.channel = assoc_bss->channel; 
+    memcpy(priv->curbssparams.bssid, assoc_bss->bssid, ETH_ALEN);
+    memcpy(priv->curbssparams.ssid, assoc_bss->ssid,IW_ESSID_MAX_SIZE + 1);
+#else
+    priv->curbssparams.channel = assoc_bss->data.channel; 
+    memcpy(priv->curbssparams.bssid, assoc_bss->data.bssid, ETH_ALEN);
+    memcpy(priv->curbssparams.ssid, assoc_bss->data.ssid,IW_ESSID_MAX_SIZE + 1);
+#endif
+       ret = assoc_helper_secinfo(priv, assoc_bss);
+       if (ret) {
+               RDA5890_ERRP("assoc_helper_secinfo fail, ret = %d\n", ret);
+               goto out;
+       }
+
+    //if the bssid is same and the association is start then break out
+    if((old_imode == priv->imode) && !memcmp(old_bssid, priv->assoc_bssid,6))
+    {
+        //WEP THE Second retry should change to shared key
+        if((old_imode == 3 || old_imode ==  5) && assoc_count%2)  
+        {
+            priv->authtype = BIT1;
+        }
+        assoc_count ++;        
+    }
+    else
+    {
+        //save old bssid para
+        old_imode = priv->imode;
+        memcpy(old_bssid, priv->assoc_bssid, 6);
+        assoc_count = 0;
+    }
+
+    set_bit(ASSOC_FLAG_ASSOC_START ,&priv->assoc_flags);    
+    set_bit(ASSOC_FLAG_ASSOC_RETRY, &priv->assoc_flags);
+
+    if((priv->imode == 3) || (priv->imode ==5))
+        {
+            if(assoc_count > 5)
+            {
+                clear_bit(ASSOC_FLAG_ASSOC_RETRY, &priv->assoc_flags);
+                clear_bit(ASSOC_FLAG_ASSOC_START ,&priv->assoc_flags);
+                old_imode = 0xff;
+            }
+        }
+    else
+        {
+            if(assoc_count)
+            {
+                clear_bit(ASSOC_FLAG_ASSOC_RETRY, &priv->assoc_flags);
+                clear_bit(ASSOC_FLAG_ASSOC_START ,&priv->assoc_flags);
+                old_imode = 0xff;
+            }
+        }
+    
+    ret = rda5890_start_join(priv);
+       if (ret) {
+               RDA5890_ERRP("rda5890_set_ssid fail, ret = %d\n", ret);
+               goto out;
+       }
+  
+    if(test_bit(ASSOC_FLAG_ASSOC_RETRY, &priv->assoc_flags))
+    {       
+        queue_delayed_work(priv->work_thread, &priv->assoc_work, 3*HZ);
+    }
+    
+out:
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<< \n", __func__);
+}
+
+static int rda5890_set_essid(struct net_device *dev, struct iw_request_info *info,
+               struct iw_point *dwrq, char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int ret = 0;
+       u8 ssid[IW_ESSID_MAX_SIZE + 1];
+       u8 ssid_len = 0;
+       int in_ssid_len = dwrq->length;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       /* Check the size of the string */
+       if (in_ssid_len > IW_ESSID_MAX_SIZE) {
+               ret = -E2BIG;
+               goto out;
+       }
+    
+       memset(&ssid, 0, sizeof(ssid));
+
+       if (!dwrq->flags || !in_ssid_len) {
+               /* "any" SSID requested; leave SSID blank */
+       } else {
+               /* Specific SSID requested */
+               memcpy(&ssid, extra, in_ssid_len);
+               ssid[in_ssid_len] = '\0';
+               ssid_len = in_ssid_len;
+       }
+
+       if (!ssid_len) {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "requested any SSID\n");
+       } else {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "requested SSID len = %d ssid:%s\n",
+                       ssid_len, ssid);
+       }
+
+    if(ssid_len)
+    {
+        memcpy(&priv->assoc_ssid[0], ssid, sizeof(ssid));
+        priv->assoc_ssid_len = ssid_len;
+    }
+
+    if(!test_bit(ASSOC_FLAG_SSID, &priv->assoc_flags))
+        goto out;
+
+    if(!test_bit(ASSOC_FLAG_ASSOC_RETRY, &priv->assoc_flags))
+    {
+        if(ssid_len)
+        {
+               cancel_delayed_work(&priv->assoc_work);
+               queue_delayed_work(priv->work_thread, &priv->assoc_work, HZ/2);
+        }
+    }
+
+out:
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+/**
+ *  @brief Connect to the AP or Ad-hoc Network with specific bssid
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param awrq         A pointer to iw_param structure
+ *  @param extra        A pointer to extra data buf
+ *  @return             0 --success, otherwise fail
+ */
+static int rda5890_set_wap(struct net_device *dev, struct iw_request_info *info,
+               struct sockaddr *awrq, char *extra)
+{
+    unsigned char * ap_addr = NULL;
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+    ap_addr = awrq->sa_data;
+    if(!test_bit(ASSOC_FLAG_ASSOC_RETRY, &priv->assoc_flags)
+        && !is_zero_eth_addr(ap_addr))
+    {
+       cancel_delayed_work(&priv->assoc_work);
+       queue_delayed_work(priv->work_thread, &priv->assoc_work, HZ/2);
+        set_bit(ASSOC_FLAG_SSID, &priv->assoc_flags);
+        memcpy(priv->assoc_bssid, ap_addr, 6);
+        printk("rda5890_set_wap addr is not null \n");
+    }
+
+    if(is_zero_eth_addr(ap_addr))
+    {
+        clear_bit(ASSOC_FLAG_SSID, &priv->assoc_flags);
+        disable_wep( priv);
+        disable_wpa(priv);
+    }
+        
+
+       RDA5890_ERRP("%s <<< \n connect mac: %2x:%2x:%2x:%2x:%2x:%2x \n", __func__, 
+         ap_addr[0],ap_addr[1],ap_addr[2],ap_addr[3],ap_addr[4],ap_addr[5]);
+    
+       return 0;
+}
+
+static inline char *translate_scan(struct rda5890_private *priv,
+                                           struct iw_request_info *info,
+                                           char *start, char *stop,
+                                           struct bss_descriptor *bss_desc)
+{
+#ifndef GET_SCAN_FROM_NETWORK_INFO
+       struct iw_event iwe;    /* Temporary buffer */
+       u8 snr;
+       struct rda5890_bss_descriptor *bss = &bss_desc->data;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+               "translate_scan, ssid = %s\n", bss->ssid);
+
+       /* First entry *MUST* be the BSSID */
+       iwe.cmd = SIOCGIWAP;
+       iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+       memcpy(iwe.u.ap_addr.sa_data, &bss->bssid, ETH_ALEN);
+       start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
+
+       /* SSID */
+       iwe.cmd = SIOCGIWESSID;
+       iwe.u.data.flags = 1;
+       iwe.u.data.length = strlen(bss->ssid);
+       start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid);
+
+       /* Mode */
+       iwe.cmd = SIOCGIWMODE;
+       //iwe.u.mode = bss->mode;
+       iwe.u.mode = IW_MODE_INFRA;
+       start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN);
+
+       /* Frequency */
+       iwe.cmd = SIOCGIWFREQ;
+       iwe.u.freq.m = (2412 + 5 * (bss->channel - 1)) * 100000;
+       iwe.u.freq.e = 1;
+       start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
+
+       /* Add quality statistics */
+       iwe.cmd = IWEVQUAL;
+       iwe.u.qual.updated = IW_QUAL_ALL_UPDATED;
+       iwe.u.qual.level = bss->rssi > 127? bss->rssi - 271: bss->rssi - 15;
+
+       snr = iwe.u.qual.level - RDA5890_NF_DEFAULT_SCAN_VALUE;
+       iwe.u.qual.qual =
+               (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - snr) *
+                (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - snr))) /
+               (RSSI_DIFF * RSSI_DIFF);
+       if (iwe.u.qual.qual > 100)
+               iwe.u.qual.qual = 100;
+       iwe.u.qual.noise = RDA5890_NF_DEFAULT_SCAN_VALUE;
+       start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
+
+       /* Add encryption capability */
+       iwe.cmd = SIOCGIWENCODE;
+       if (bss->dot11i_info & BIT0) {
+               iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+       } else {
+               iwe.u.data.flags = IW_ENCODE_DISABLED;
+       }
+       iwe.u.data.length = 0;
+       start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid);
+
+#if 0
+       current_val = start + iwe_stream_lcp_len(info);
+
+       iwe.cmd = SIOCGIWRATE;
+       iwe.u.bitrate.fixed = 0;
+       iwe.u.bitrate.disabled = 0;
+       iwe.u.bitrate.value = 0;
+
+       for (j = 0; bss->rates[j] && (j < sizeof(bss->rates)); j++) {
+               /* Bit rate given in 500 kb/s units */
+               iwe.u.bitrate.value = bss->rates[j] * 500000;
+               current_val = iwe_stream_add_value(info, start, current_val,
+                                                  stop, &iwe, IW_EV_PARAM_LEN);
+       }
+       if ((bss->mode == IW_MODE_ADHOC) && priv->adhoccreate
+           && !lbs_ssid_cmp(priv->curbssparams.ssid,
+                            priv->curbssparams.ssid_len,
+                            bss->ssid, bss->ssid_len)) {
+               iwe.u.bitrate.value = 22 * 500000;
+               current_val = iwe_stream_add_value(info, start, current_val,
+                                                  stop, &iwe, IW_EV_PARAM_LEN);
+       }
+       /* Check if we added any event */
+       if ((current_val - start) > iwe_stream_lcp_len(info))
+               start = current_val;
+#endif
+
+       memset(&iwe, 0, sizeof(iwe));
+       if (bss_desc->wpa_ie_len) {
+               char buf[MAX_WPA_IE_LEN];
+
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "translate_scan, wpa_ie, len %d\n", bss_desc->wpa_ie_len);
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "%02x %02x %02x %02x ... ... %02x %02x %02x %02x\n",
+                       bss_desc->wpa_ie[0], bss_desc->wpa_ie[1], 
+                       bss_desc->wpa_ie[2], bss_desc->wpa_ie[3], 
+                       bss_desc->wpa_ie[bss_desc->wpa_ie_len - 4], 
+                       bss_desc->wpa_ie[bss_desc->wpa_ie_len - 3],
+                       bss_desc->wpa_ie[bss_desc->wpa_ie_len - 2], 
+                       bss_desc->wpa_ie[bss_desc->wpa_ie_len - 1]);
+
+               memcpy(buf, bss_desc->wpa_ie, bss_desc->wpa_ie_len);
+               iwe.cmd = IWEVGENIE;
+               iwe.u.data.length = bss_desc->wpa_ie_len;
+               start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+       }
+
+       memset(&iwe, 0, sizeof(iwe));
+       if (bss_desc->rsn_ie_len) {
+               char buf[MAX_WPA_IE_LEN];
+
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "translate_scan, rsn_ie, len %d\n", bss_desc->rsn_ie_len);
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "%02x %02x %02x %02x ... ... %02x %02x %02x %02x\n",
+                       bss_desc->rsn_ie[0], bss_desc->rsn_ie[1], 
+                       bss_desc->rsn_ie[2], bss_desc->rsn_ie[3], 
+                       bss_desc->rsn_ie[bss_desc->rsn_ie_len - 4], 
+                       bss_desc->rsn_ie[bss_desc->rsn_ie_len - 3],
+                       bss_desc->rsn_ie[bss_desc->rsn_ie_len - 2], 
+                       bss_desc->rsn_ie[bss_desc->rsn_ie_len - 1]);
+
+               memcpy(buf, bss_desc->rsn_ie, bss_desc->rsn_ie_len);
+               iwe.cmd = IWEVGENIE;
+               iwe.u.data.length = bss_desc->rsn_ie_len;
+               start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+       }
+
+       return start;
+#else
+       struct iw_event iwe;    /* Temporary buffer */
+       u8 snr;
+       struct bss_descriptor *bss = bss_desc;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+               "translate_scan, ssid = %s ssi=%d ssid_len=%d \n", bss->ssid, bss->rssi, bss->ssid_len);
+
+       /* First entry *MUST* be the BSSID */
+       iwe.cmd = SIOCGIWAP;
+       iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+       memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+       start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
+
+       /* SSID */
+       iwe.cmd = SIOCGIWESSID;
+       iwe.u.data.flags = 1;
+       iwe.u.data.length = bss->ssid_len;
+       start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid);
+
+       /* Mode */
+       iwe.cmd = SIOCGIWMODE;
+       iwe.u.mode = bss->mode;
+       start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN);
+
+       /* Frequency */
+       iwe.cmd = SIOCGIWFREQ;
+       iwe.u.freq.m = (2412 + 5 * (bss->channel - 1)) * 100000;
+       iwe.u.freq.e = 1;
+       start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
+
+       /* Add quality statistics */
+       iwe.cmd = IWEVQUAL;
+       iwe.u.qual.updated = IW_QUAL_ALL_UPDATED;
+       iwe.u.qual.level = bss->rssi > 127? bss->rssi - 271: bss->rssi - 15;
+
+       snr = iwe.u.qual.level - RDA5890_NF_DEFAULT_SCAN_VALUE;
+       iwe.u.qual.qual =
+               (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - snr) *
+                (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - snr))) /
+               (RSSI_DIFF * RSSI_DIFF);
+       if (iwe.u.qual.qual > 100)
+               iwe.u.qual.qual = 100;
+       iwe.u.qual.noise = RDA5890_NF_DEFAULT_SCAN_VALUE;
+       start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
+
+       /* Add encryption capability */
+       iwe.cmd = SIOCGIWENCODE;
+       if (bss->capability & WLAN_CAPABILITY_PRIVACY) {
+               iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+       } else {
+               iwe.u.data.flags = IW_ENCODE_DISABLED;
+       }
+       iwe.u.data.length = 0;
+       start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid);
+
+       memset(&iwe, 0, sizeof(iwe));
+       if (bss_desc->wpa_ie_len && !bss_desc->wapi_ie_len) {
+               char buf[MAX_WPA_IE_LEN];
+
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "translate_scan, wpa_ie, len %d\n", bss_desc->wpa_ie_len);
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "%02x %02x %02x %02x ... ... %02x %02x %02x %02x\n",
+                       bss_desc->wpa_ie[0], bss_desc->wpa_ie[1], 
+                       bss_desc->wpa_ie[2], bss_desc->wpa_ie[3], 
+                       bss_desc->wpa_ie[bss_desc->wpa_ie_len - 4], 
+                       bss_desc->wpa_ie[bss_desc->wpa_ie_len - 3],
+                       bss_desc->wpa_ie[bss_desc->wpa_ie_len - 2], 
+                       bss_desc->wpa_ie[bss_desc->wpa_ie_len - 1]);
+
+               memcpy(buf, bss->wpa_ie, bss->wpa_ie_len);
+               iwe.cmd = IWEVGENIE;
+               iwe.u.data.length = bss_desc->wpa_ie_len;
+               start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+       }
+
+       memset(&iwe, 0, sizeof(iwe));
+       if (bss_desc->rsn_ie_len && !bss_desc->wapi_ie_len) {
+               char buf[MAX_WPA_IE_LEN];
+
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "translate_scan, rsn_ie, len %d\n", bss_desc->rsn_ie_len);
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "%02x %02x %02x %02x ... ... %02x %02x %02x %02x\n",
+                       bss_desc->rsn_ie[0], bss_desc->rsn_ie[1], 
+                       bss_desc->rsn_ie[2], bss_desc->rsn_ie[3], 
+                       bss_desc->rsn_ie[bss_desc->rsn_ie_len - 4], 
+                       bss_desc->rsn_ie[bss_desc->rsn_ie_len - 3],
+                       bss_desc->rsn_ie[bss_desc->rsn_ie_len - 2], 
+                       bss_desc->rsn_ie[bss_desc->rsn_ie_len - 1]);
+
+               memcpy(buf, bss->rsn_ie, bss->rsn_ie_len);
+               iwe.cmd = IWEVGENIE;
+               iwe.u.data.length = bss_desc->rsn_ie_len;
+               start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+       }
+
+       memset(&iwe, 0, sizeof(iwe));
+       if (bss_desc->wapi_ie_len) {
+               char buf[100];
+
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "translate_scan, wapi_len %d\n", bss_desc->wapi_ie_len);
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE,
+                       "%02x %02x %02x %02x ... ... %02x %02x %02x %02x\n",
+                       bss_desc->wapi_ie[0], bss_desc->wapi_ie[1], 
+                       bss_desc->wapi_ie[2], bss_desc->wapi_ie[3], 
+                       bss_desc->wapi_ie[bss_desc->wapi_ie_len - 4], 
+                       bss_desc->wapi_ie[bss_desc->wapi_ie_len - 3],
+                       bss_desc->wapi_ie[bss_desc->wapi_ie_len - 2], 
+                       bss_desc->wapi_ie[bss_desc->wapi_ie_len - 1]);
+
+               memcpy(buf, bss->wapi_ie, bss->wapi_ie_len);
+               iwe.cmd = IWEVGENIE;
+               iwe.u.data.length = bss_desc->wapi_ie_len;
+               start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+       }
+
+       return start;
+#endif
+}
+
+int is_same_network(struct bss_descriptor *src,
+                                 struct bss_descriptor *dst)
+{
+       /* A network is only a duplicate if the channel, BSSID, and ESSID
+        * all match.  We treat all <hidden> with the same BSSID and channel
+        * as one network */
+#ifndef GET_SCAN_FROM_NETWORK_INFO
+       return ((src->data.channel == dst->data.channel) &&
+               !compare_ether_addr(src->data.bssid, dst->data.bssid) &&
+               !memcmp(src->data.ssid, dst->data.ssid, IW_ESSID_MAX_SIZE));
+#else
+       return ((src->channel == dst->channel) &&
+               !compare_ether_addr(src->bssid, dst->bssid) &&
+               !memcmp(src->ssid, dst->ssid, IW_ESSID_MAX_SIZE));
+#endif
+}
+
+void clear_bss_descriptor(struct bss_descriptor *bss)
+{
+       /* Don't blow away ->list, just BSS data */
+       memset(bss, 0, offsetof(struct bss_descriptor, list));
+}
+
+static void dump_bss_desc(struct rda5890_bss_descriptor *bss_desc)
+{
+       RDA5890_DBGP("########## dump bss ##########\n");
+       RDA5890_DBGP("ssid = %s\n", bss_desc->ssid);
+       RDA5890_DBGP("bss_type = %d\n", bss_desc->bss_type);
+       RDA5890_DBGP("channel = %d\n", bss_desc->channel);
+       RDA5890_DBGP("dot11i_info = 0x%02x\n", bss_desc->dot11i_info);
+       RDA5890_DBGP("bssid = %02x:%02x:%02x:%02x:%02x:%02x\n", 
+               bss_desc->bssid[0], bss_desc->bssid[1], bss_desc->bssid[2], 
+               bss_desc->bssid[3], bss_desc->bssid[4], bss_desc->bssid[5]);
+       RDA5890_DBGP("rssi = %d\n", (char)bss_desc->rssi);
+       RDA5890_DBGP("auth_info = 0x%02x\n", bss_desc->auth_info);
+       RDA5890_DBGP("rsn_cap = 0x%04x\n",
+               (bss_desc->rsn_cap[1] << 8) | bss_desc->rsn_cap[0]);
+       RDA5890_DBGP("########## dump bss ##########\n");
+}
+
+/* Element Ids used in Management frames in 802.11i mode */
+typedef enum{ IRSNELEMENT = 48,  /* RSN Information Element  */
+              IWPAELEMENT = 221 /* WPA Information Element  */
+}ELEMENTID_11I_T;
+
+/* CIPHER set for RSN or WPA element  */
+typedef enum { CIPHER_TYPE_USE_GROUP_SET  = 0,
+               CIPHER_TYPE_WEP40          = 1,
+               CIPHER_TYPE_TKIP           = 2,
+               CIPHER_TYPE_CCMP           = 4,
+               CIPHER_TYPE_WEP104         = 5
+} CIPHER_TYPE_T;
+
+unsigned char oui_rsn[3] = {0x00, 0x0F, 0xAC};
+unsigned char oui_wpa[3] = {0x00, 0x50, 0xf2};
+
+static void fill_rsn_wpa_ie(unsigned char *data, unsigned char ie_type,
+               struct rda5890_bss_descriptor *bss, size_t *len)
+{
+       unsigned char index = 0;
+       unsigned char *oui;
+
+       if (ie_type == IRSNELEMENT) {
+               oui = &oui_rsn[0];
+
+               /* Set RSN Information Element element ID */
+               data[index] = IRSNELEMENT;
+               index += 2;
+       }
+       else {
+               oui = &oui_wpa[0];
+
+               /* Set WPA Information Element element ID */
+               data[index] = IWPAELEMENT;
+               index += 2;
+
+               /* Copy OUI */
+               memcpy(&data[index], oui, 3);
+               index += 3;
+               data[index++] = 0x01;
+       }
+
+       /* Set the version of RSN Element to 1 */
+       data[index++] = 1;
+       data[index++] = 0;
+
+       /* Set Group Cipher Suite */
+       memcpy(&data[index], oui, 3);
+       index += 3;
+       if ((bss->dot11i_info & BIT5) && !(bss->dot11i_info & BIT6)) {
+               /* only CCMP and !TKIP, use CCMP, otherwise, always TKIP */
+               data[index++] = CIPHER_TYPE_CCMP;
+       }
+       else {
+               data[index++] = CIPHER_TYPE_TKIP;
+       }
+
+       /* Set Pairwise cipher Suite */
+       if ((bss->dot11i_info & BIT5) && (bss->dot11i_info & BIT6)) {
+               /* both CCMP and TKIP */
+               data[index++] = 2;
+               data[index++] = 0;
+
+               /* Check BIT7 to determine who goes first */
+               if (bss->dot11i_info & BIT7) {
+                       /* BIT7 is 1 => CCMP goes first */
+                       memcpy(&data[index], oui, 3);
+                       index += 3;
+                       data[index++] = CIPHER_TYPE_CCMP;
+
+                       memcpy(&data[index], oui, 3);
+                       index += 3;
+                       data[index++] = CIPHER_TYPE_TKIP;
+               }
+               else {
+               /* BIT7 is 0 => TKIP goes first */
+                       memcpy(&data[index], oui, 3);
+                       index += 3;
+                       data[index++] = CIPHER_TYPE_TKIP;
+
+                       memcpy(&data[index], oui, 3);
+                       index += 3;
+                       data[index++] = CIPHER_TYPE_CCMP;
+               }
+       }
+       else if ((bss->dot11i_info & BIT5) && !(bss->dot11i_info & BIT6)) {
+               /* CCMP and !TKIP */
+               data[index++] = 1;
+               data[index++] = 0;
+
+               memcpy(&data[index], oui, 3);
+               index += 3;
+               data[index++] = CIPHER_TYPE_CCMP;
+       }
+       else if (!(bss->dot11i_info & BIT5) && (bss->dot11i_info & BIT6)) {
+               /* !CCMP and TKIP */
+               data[index++] = 1;
+               data[index++] = 0;
+
+               memcpy(&data[index], oui, 3);
+               index += 3;
+               data[index++] = CIPHER_TYPE_TKIP;
+       }
+       else {
+               /* neither CCMP nor TKIP, use TKIP for WPA, and CCMP for RSN */
+               data[index++] = 1;
+               data[index++] = 0;
+
+               memcpy(&data[index], oui, 3);
+               index += 3;
+               if (ie_type == IRSNELEMENT) {
+                       data[index++] = CIPHER_TYPE_CCMP;
+               }
+               else {
+                       data[index++] = CIPHER_TYPE_TKIP;
+               }
+       }
+
+       /* Set Authentication Suite */
+       if ((bss->auth_info & 0x01) && (bss->auth_info & 0x02)) {
+               /* both 802.1X and PSK */
+               data[index++] = 2;
+               data[index++] = 0;
+
+               memcpy(&data[index], oui, 3);
+               index += 3;
+               data[index++] = 0x01;
+
+               memcpy(&data[index], oui, 3);
+               index += 3;
+               data[index++] = 0x02;
+       }
+       else if ((bss->auth_info & 0x01) && !(bss->auth_info & 0x02)) {
+               /* 802.1X and !PSK */
+               data[index++] = 1;
+               data[index++] = 0;
+
+               memcpy(&data[index], oui, 3);
+               index += 3;
+               data[index++] = 0x01;
+       }
+       else if (!(bss->auth_info & 0x01) && (bss->auth_info & 0x02)) {
+               /* !802.1X and PSK */
+               data[index++] = 1;
+               data[index++] = 0;
+
+               memcpy(&data[index], oui, 3);
+               index += 3;
+               data[index++] = 0x02;
+       }
+       else {
+               /* neither 802.1X nor PSK, use 802.1X */
+               data[index++] = 1;
+               data[index++] = 0;
+
+               memcpy(&data[index], oui, 3);
+               index += 3;
+               data[index++] = 0x01;
+       }
+
+       /* The RSN Capabilities, for RSN IE only */
+       if (ie_type == IRSNELEMENT) {
+               data[index++] = bss->rsn_cap[0];
+               data[index++] = bss->rsn_cap[1];
+       }
+
+       /* Set the length of the RSN Information Element */
+       data[1] = (index - 2);
+
+       /* Return the Extended Supported Rates element length */
+       *len = (size_t)index;
+}
+
+/* reconstruct wpa/rsn ie from the dot11i_info and auth_info fields */
+/* TODO: 
+ * assuming RSN and WPA are using same cipher suite, no space to store each
+ * assuming grp and unicast are using same cipher suite
+ */
+static void reconstruct_rsn_wpa_ie(struct bss_descriptor *bss_desc)
+{
+       bss_desc->wpa_ie_len = 0;
+       bss_desc->rsn_ie_len = 0;
+       
+       if (bss_desc->data.dot11i_info & BIT0) {
+               if (bss_desc->data.dot11i_info & BIT3) {
+                       /* WPA IE present */
+                       fill_rsn_wpa_ie(&bss_desc->wpa_ie[0], IWPAELEMENT, 
+                               &bss_desc->data, &bss_desc->wpa_ie_len);
+               }
+
+               if (bss_desc->data.dot11i_info & BIT4) {
+                       /* RSN IE present */
+                       fill_rsn_wpa_ie(&bss_desc->rsn_ie[0], IRSNELEMENT, 
+                               &bss_desc->data, &bss_desc->rsn_ie_len);                        
+               }
+       }
+       else {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "form_rsn_ie, NO SEC\n");
+       }
+}
+
+void rda5890_scan_worker(struct work_struct *work)
+{
+       struct rda5890_private *priv = container_of(work, struct rda5890_private,
+               scan_work.work);
+       int ret = 0;
+       struct rda5890_bss_descriptor bss_desc[RDA5890_MAX_NETWORK_NUM];
+       int bss_index, bss_count;
+       struct bss_descriptor *iter_bss;
+       union iwreq_data wrqu;
+    unsigned char fist_send = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+    if(test_bit(ASSOC_FLAG_ASSOC_START, &priv->assoc_flags)
+        || test_bit(ASSOC_FLAG_WLAN_CONNECTING, &priv->assoc_flags))
+    {
+        cancel_delayed_work(&priv->scan_work);
+        queue_delayed_work(priv->work_thread, &priv->scan_work, HZ/2);
+        return;
+    }
+
+       priv->scan_running = 1;
+    
+#ifdef WIFI_UNLOCK_SYSTEM
+    rda5990_wakeLock();
+#endif
+
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+       ret = rda5890_start_scan_enable_network_info(priv);
+#else
+    ret = rda5890_start_scan(priv);
+#endif 
+       if (ret) {
+               RDA5890_ERRP("rda5890_start_scan fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "SCANNING ...\n");
+       /* TODO: delay 2 sec for now, need to be put into a worker */
+       rda5890_shedule_timeout(1500);
+
+#ifndef GET_SCAN_FROM_NETWORK_INFO
+retry:
+       bss_count = rda5890_get_scan_results(priv, bss_desc);
+
+    fist_send = (bss_count >> 8) & 0xff;
+    bss_count &= 0xff;
+       if (bss_count < 0 || bss_count >= RDA5890_MAX_NETWORK_NUM) {
+               RDA5890_ERRP("rda5890_get_scan_results fail, ret = %d\n", bss_count);
+               goto out;
+       }
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Get Scan Result, count = %d, fist_send= %d \n", bss_count, fist_send);
+
+       /* add scaned bss into list */
+       for(bss_index = 0; bss_index < bss_count; bss_index++) {
+               struct bss_descriptor new;
+               struct bss_descriptor *found = NULL;
+               struct bss_descriptor *oldest = NULL;
+
+       if (RDA5890_DBGLA(RDA5890_DA_WEXT, RDA5890_DL_TRACE))
+               dump_bss_desc(&bss_desc[bss_index]);
+
+               memcpy(&new.data, &bss_desc[bss_index], sizeof(struct rda5890_bss_descriptor));
+               reconstruct_rsn_wpa_ie(&new);
+               new.last_scanned = jiffies;
+
+               /* Try to find this bss in the scan table */
+               list_for_each_entry (iter_bss, &priv->network_list, list) {
+                       if (is_same_network(iter_bss, &new)) {
+                               found = iter_bss;
+                               break;
+                       }
+
+                       if ((oldest == NULL) ||
+                           (iter_bss->last_scanned < oldest->last_scanned))
+                               oldest = iter_bss;
+               }
+
+               if (found) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "FOUND SAME %s, update\n", found->data.ssid);
+                       /* found, clear it */
+                       clear_bss_descriptor(found);
+               } else if (!list_empty(&priv->network_free_list)) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "FOUND NEW %s, add\n", new.data.ssid);
+                       /* Pull one from the free list */
+                       found = list_entry(priv->network_free_list.next,
+                                          struct bss_descriptor, list);
+                       list_move_tail(&found->list, &priv->network_list);
+               } else if (oldest) {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "FOUND NEW %s, no space, replace oldest %s\n", 
+                               new.data.ssid, oldest->data.ssid);
+                       /* If there are no more slots, expire the oldest */
+                       found = oldest;
+                       clear_bss_descriptor(found);
+                       list_move_tail(&found->list, &priv->network_list);
+               } else {
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "FOUND NEW but no space to store\n");
+               }
+
+               /* Copy the locally created newbssentry to the scan table */
+               memcpy(found, &new, offsetof(struct bss_descriptor, list));
+       }
+
+    if(bss_count >= 5 && !fist_send)
+        goto retry;
+
+#else
+    //do noting in get network info modle
+
+#endif
+
+out:
+    priv->scan_running = 0;
+    memset(&wrqu, 0, sizeof(union iwreq_data));
+    wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+
+#ifdef WIFI_UNLOCK_SYSTEM
+    rda5990_wakeUnlock();
+#endif
+}
+
+/**
+ *  @brief Handle Scan Network ioctl
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param vwrq         A pointer to iw_param structure
+ *  @param extra        A pointer to extra data buf
+ *
+ *  @return             0 --success, otherwise fail
+ */
+int rda5890_set_scan(struct net_device *dev, struct iw_request_info *info,
+                union iwreq_data *wrqu, char *extra)
+{
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       if (priv->scan_running)
+               goto out;
+
+
+    cancel_delayed_work(&priv->scan_work);
+    queue_delayed_work(priv->work_thread, &priv->scan_work, HZ/50);
+
+out:
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return 0;
+}
+
+/**
+ *  @brief  Handle Retrieve scan table ioctl
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param dwrq         A pointer to iw_point structure
+ *  @param extra        A pointer to extra data buf
+ *
+ *  @return             0 --success, otherwise fail
+ */
+int rda5890_get_scan(struct net_device *dev, struct iw_request_info *info,
+                struct iw_point *dwrq, char *extra)
+{
+#define SCAN_ITEM_SIZE 128
+       struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       int ret = 0;
+       struct bss_descriptor *iter_bss;
+       struct bss_descriptor *safe;
+       char *ev = extra;
+       char *stop = ev + dwrq->length;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       /* iwlist should wait until the current scan is finished */
+       if (priv->scan_running) {
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "Scan is Running, return AGAIN\n");
+               return -EAGAIN;
+       }
+
+       /* report all bss to upper layer */
+       list_for_each_entry_safe (iter_bss, safe, &priv->network_list, list) {
+               char *next_ev;
+               unsigned long stale_time;
+
+               if (stop - ev < SCAN_ITEM_SIZE) {
+                       ret = -E2BIG;
+                       break;
+               }
+
+               /* Prune old an old scan result */
+               stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE;
+               if (time_after(jiffies, stale_time)) {
+                       list_move_tail(&iter_bss->list, &priv->network_free_list);
+#ifdef GET_SCAN_FROM_NETWORK_INFO              
+                       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "Prune Old Bss %s\n", iter_bss->ssid);
+#else
+            RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                               "Prune Old Bss %s\n", iter_bss->ssid);
+#endif
+                       clear_bss_descriptor(iter_bss);
+                       continue;
+               }
+
+               /* Translate to WE format this entry */
+               next_ev = translate_scan(priv, info, ev, stop, iter_bss);
+#ifdef GET_SCAN_FROM_NETWORK_INFO        
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "Report BSS %s\n", iter_bss->ssid);
+#else
+        RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+            "Report BSS %s\n", iter_bss->data.ssid);
+#endif
+               if (next_ev == NULL)
+                       continue;
+               ev = next_ev;
+       }
+       dwrq->length = (ev - extra);
+       dwrq->flags = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return ret;
+}
+
+int rda5890_set_mlme(struct net_device *dev,
+                                 struct iw_request_info *info,
+                                 union iwreq_data *wrqu, char *extra)
+{
+       //struct rda5890_private *priv = (struct rda5890_private *)netdev_priv(dev);
+       struct iw_mlme *mlme = (struct iw_mlme *)extra;
+       int ret = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s >>>\n", __func__);
+
+       switch (mlme->cmd) {
+       case IW_MLME_DEAUTH:
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "DEAUTH\n");
+               /* silently ignore */
+               break;
+
+       case IW_MLME_DISASSOC:
+       {
+               unsigned char ssid[6];
+               memset(ssid, 0, 6);
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "DISASSOC\n");
+               /* silently ignore */
+               rda5890_set_ssid((struct rda5890_private *)netdev_priv(dev) , ssid, 6);
+       }
+               break;
+       default:
+               RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, 
+                       "Not supported cmd %d\n", mlme->cmd);
+               ret = -EOPNOTSUPP;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WEXT, RDA5890_DL_TRACE, "%s <<<\n", __func__);
+       return ret;
+}
+
+/*
+ * iwconfig settable callbacks
+ */
+static const iw_handler rda5890_wext_handler[] = {
+       (iw_handler) NULL,      /* SIOCSIWCOMMIT */
+       (iw_handler) rda5890_get_name,  /* SIOCGIWNAME */
+       (iw_handler) NULL,      /* SIOCSIWNWID */
+       (iw_handler) NULL,      /* SIOCGIWNWID */
+       (iw_handler) rda5890_set_freq,  /* SIOCSIWFREQ */
+       (iw_handler) rda5890_get_freq,  /* SIOCGIWFREQ */
+       (iw_handler) rda5890_set_mode,  /* SIOCSIWMODE */
+       (iw_handler) rda5890_get_mode,  /* SIOCGIWMODE */
+       (iw_handler) NULL,      /* SIOCSIWSENS */
+       (iw_handler) NULL,      /* SIOCGIWSENS */
+       (iw_handler) NULL,      /* SIOCSIWRANGE */
+       (iw_handler) rda5890_get_range, /* SIOCGIWRANGE */
+       (iw_handler) NULL,      /* SIOCSIWPRIV */
+       (iw_handler) NULL,      /* SIOCGIWPRIV */
+       (iw_handler) NULL,      /* SIOCSIWSTATS */
+       (iw_handler) NULL,      /* SIOCGIWSTATS */
+       (iw_handler) NULL,      /* SIOCSIWSPY */
+       (iw_handler) NULL,      /* SIOCGIWSPY */
+       (iw_handler) NULL,      /* SIOCSIWTHRSPY */
+       (iw_handler) NULL,      /* SIOCGIWTHRSPY */
+       (iw_handler) rda5890_set_wap,   /* SIOCSIWAP */
+       (iw_handler) rda5890_get_wap,   /* SIOCGIWAP */
+       (iw_handler) rda5890_set_mlme,  /* SIOCSIWMLME */
+       (iw_handler) NULL,      /* SIOCGIWAPLIST - deprecated */
+       (iw_handler) rda5890_set_scan,  /* SIOCSIWSCAN */
+       (iw_handler) rda5890_get_scan,  /* SIOCGIWSCAN */
+       (iw_handler) rda5890_set_essid, /* SIOCSIWESSID */
+       (iw_handler) rda5890_get_essid, /* SIOCGIWESSID */
+       (iw_handler) rda5890_set_nick,  /* SIOCSIWNICKN */
+       (iw_handler) rda5890_get_nick,  /* SIOCGIWNICKN */
+       (iw_handler) NULL,      /* -- hole -- */
+       (iw_handler) NULL,      /* -- hole -- */
+       (iw_handler) rda5890_set_rate,  /* SIOCSIWRATE */
+       (iw_handler) rda5890_get_rate,  /* SIOCGIWRATE */
+       (iw_handler) rda5890_set_rts,   /* SIOCSIWRTS */
+       (iw_handler) rda5890_get_rts,   /* SIOCGIWRTS */
+       (iw_handler) rda5890_set_frag,  /* SIOCSIWFRAG */
+       (iw_handler) rda5890_get_frag,  /* SIOCGIWFRAG */
+       (iw_handler) rda5890_set_txpow, /* SIOCSIWTXPOW */
+       (iw_handler) rda5890_get_txpow, /* SIOCGIWTXPOW */
+       (iw_handler) rda5890_set_retry, /* SIOCSIWRETRY */
+       (iw_handler) rda5890_get_retry, /* SIOCGIWRETRY */
+       (iw_handler) rda5890_set_encode,        /* SIOCSIWENCODE */
+       (iw_handler) rda5890_get_encode,        /* SIOCGIWENCODE */
+       (iw_handler) rda5890_set_power, /* SIOCSIWPOWER */
+       (iw_handler) rda5890_get_power, /* SIOCGIWPOWER */
+       (iw_handler) NULL,      /* -- hole -- */
+       (iw_handler) NULL,      /* -- hole -- */
+       (iw_handler) rda5890_set_genie, /* SIOCSIWGENIE */
+       (iw_handler) rda5890_get_genie, /* SIOCGIWGENIE */
+       (iw_handler) rda5890_set_auth,  /* SIOCSIWAUTH */
+       (iw_handler) rda5890_get_auth,  /* SIOCGIWAUTH */
+       (iw_handler) rda5890_set_encodeext,/* SIOCSIWENCODEEXT */
+       (iw_handler) rda5890_get_encodeext,/* SIOCGIWENCODEEXT */
+       (iw_handler) rda5890_set_pmksa,         /* SIOCSIWPMKSA */
+};
+
+struct iw_handler_def rda5890_wext_handler_def = {
+       .num_standard   = ARRAY_SIZE(rda5890_wext_handler),
+       .standard       = (iw_handler *) rda5890_wext_handler,
+       .get_wireless_stats = rda5890_get_wireless_stats,
+};
+
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.h b/drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.h
new file mode 100755 (executable)
index 0000000..35ff2f7
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+  * This file contains definition for IOCTL call.
+  */
+#include "rda5890_defs.h"  
+#ifndef        _RDA5890_WEXT_H_
+#define        _RDA5890_WEXT_H_
+
+#ifndef DEFAULT_MAX_SCAN_AGE
+#define DEFAULT_MAX_SCAN_AGE (15*HZ)
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+#else
+#define DEFAULT_MAX_SCAN_AGE (15*HZ)
+#endif
+#else
+#undef DEFAULT_MAX_SCAN_AGE
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+#define DEFAULT_MAX_SCAN_AGE (15*HZ)
+#else
+#define DEFAULT_MAX_SCAN_AGE (15*HZ)
+#endif
+#endif
+
+extern struct iw_handler_def rda5890_wext_handler_def;
+
+void rda5890_indicate_disconnected(struct rda5890_private *priv);
+void rda5890_indicate_connected(struct rda5890_private *priv);
+void rda5890_assoc_done_worker(struct work_struct *work);
+void rda5890_assoc_worker(struct work_struct *work);
+void rda5890_scan_worker(struct work_struct *work);
+void rda5890_wlan_connect_worker(struct work_struct *work);
+
+static inline unsigned char  is_zero_eth_addr(unsigned char * addr)
+{
+    return !(addr[0] | addr[1] |addr[2] |addr[3] |addr[4] |addr[5]);
+}
+
+#endif
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.c b/drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.c
new file mode 100755 (executable)
index 0000000..1a1fc1d
--- /dev/null
@@ -0,0 +1,1957 @@
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/etherdevice.h>
+#include <net/iw_handler.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "rda5890_defs.h"
+#include "rda5890_dev.h"
+#include "rda5890_wid.h"
+#include "rda5890_wext.h"
+#include "rda5890_txrx.h"
+#include "rda5890_if_sdio.h"
+
+static unsigned char is_need_set_notch = 0;
+
+/* for both Query and Write */
+int rda5890_wid_request(struct rda5890_private *priv, 
+               char *wid_req, unsigned short wid_req_len,
+               char *wid_rsp, unsigned short *wid_rsp_len)
+{
+       int ret = 0;
+       int timeleft = 0;
+       char data_buf[RDA5890_MAX_WID_LEN + 2];
+       unsigned short data_len;
+    struct if_sdio_card * card = (struct if_sdio_card *)priv->card;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+               
+#ifdef WIFI_TEST_MODE
+  if(rda_5990_wifi_in_test_mode())
+      return 0;
+#endif  //end WIFI_TEST_MODE
+
+       mutex_lock(&priv->wid_lock);
+       priv->wid_rsp = wid_rsp;
+       priv->wid_rsp_len = *wid_rsp_len;
+       priv->wid_pending = 1;
+
+       data_len = wid_req_len + 2;
+       data_buf[0] = (char)(data_len&0xFF);
+       data_buf[1] = (char)((data_len>>8)&0x0F);
+       data_buf[1] |= 0x40;  // for Request(Q/W) 0x4
+       memcpy(data_buf + 2, wid_req, wid_req_len);
+
+       init_completion(&priv->wid_done);
+#ifdef WIFI_UNLOCK_SYSTEM
+    rda5990_wakeLock();
+#endif
+       ret = rda5890_host_to_card(priv, data_buf, data_len, WID_REQUEST_PACKET);
+       if (ret) {
+               RDA5890_ERRP("host_to_card send failed, ret = %d\n", ret);
+               priv->wid_pending = 0;
+               goto out;
+       }
+
+    atomic_inc(&card->wid_complete_flag);
+       timeleft = wait_for_completion_timeout(&priv->wid_done, msecs_to_jiffies(450)); 
+       if (timeleft == 0) {
+               RDA5890_ERRP("respose timeout wid :%x %x \n", wid_req[4], wid_req[5]);
+               priv->wid_pending = 0;
+               ret = -EFAULT; 
+               goto out;
+       }
+
+       *wid_rsp_len = priv->wid_rsp_len;
+
+out:
+       mutex_unlock(&priv->wid_lock);
+    atomic_set(&card->wid_complete_flag, 0);
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s >>> wid: %x %x \n", __func__, wid_req[4],wid_req[5]);
+#ifdef WIFI_UNLOCK_SYSTEM
+    rda5990_wakeUnlock();
+#endif
+       return ret;
+}
+
+int rda5890_wid_request_polling(struct rda5890_private *priv, 
+               char *wid_req, unsigned short wid_req_len,
+               char *wid_rsp, unsigned short *wid_rsp_len)
+{
+       int ret = -1;
+       int timeleft = 0;
+       char data_buf[RDA5890_MAX_WID_LEN + 2];
+       unsigned short data_len;
+       unsigned char status;
+       unsigned int retry = 0, count = 0;
+    struct if_sdio_card * card = (struct if_sdio_card*)priv->card;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       priv->wid_rsp = wid_rsp;
+       priv->wid_rsp_len = *wid_rsp_len;
+
+       data_len = wid_req_len + 2;
+       data_buf[0] = (char)(data_len&0xFF);
+       data_buf[1] = (char)((data_len>>8)&0x0F);
+       data_buf[1] |= 0x40;  // for Request(Q/W) 0x4
+       memcpy(data_buf + 2, wid_req, wid_req_len);
+
+re_send:
+
+    count += 1;
+       ret = rda5890_host_to_card(priv, data_buf, data_len, WID_REQUEST_POLLING_PACKET);
+       if (ret) {
+               RDA5890_ERRP("host_to_card send failed, ret = %d\n", ret);
+               goto out;
+       }
+
+    rda5890_shedule_timeout(3);   //3ms delay
+       while(retry < 20)
+       {
+             sdio_claim_host(card->func);
+               status = sdio_readb(card->func, IF_SDIO_FUN1_INT_STAT, &ret);
+            sdio_release_host(card->func);
+               if (ret)
+                   goto out;
+
+               if (status & IF_SDIO_INT_AHB2SDIO)
+               {
+                       int ret = 0;
+                       u8 size_l = 0, size_h = 0;
+                       u16 size, chunk;
+
+                    sdio_claim_host(card->func);
+                       size_l = sdio_readb(card->func, IF_SDIO_AHB2SDIO_PKTLEN_L, &ret);
+                   sdio_release_host(card->func);
+                       if (ret) {
+                               RDA5890_ERRP("read PKTLEN_L reg fail\n");
+                       goto out;
+                       }
+                       else
+                               RDA5890_ERRP("read PKTLEN_L reg size_l:%d \n", size_l);
+
+                   sdio_claim_host(card->func);
+                       size_h = sdio_readb(card->func, IF_SDIO_AHB2SDIO_PKTLEN_H, &ret);
+                    sdio_release_host(card->func);
+                       if (ret) {
+                               RDA5890_ERRP("read PKTLEN_H reg fail\n");
+                               goto out;
+                       }
+                       else
+                               RDA5890_ERRP("read PKTLEN_H reg size_h:%d\n",size_h);   
+
+                       size = (size_l | ((size_h & 0x7f) << 8)) * 4;
+                       if (size < 4) {
+                               RDA5890_ERRP("invalid packet size (%d bytes) from firmware\n", size);
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
+                       /* alignment is handled on firmside */
+                       //chunk = sdio_align_size(card->func, size);
+                       chunk = size;
+
+                       RDA5890_DBGLAP(RDA5890_DA_SDIO, RDA5890_DL_NORM,
+                       "if_sdio_card_to_host, size = %d, aligned size = %d\n", size, chunk);
+
+                       /* TODO: handle multiple packets here */
+            sdio_claim_host(card->func);
+                       ret = sdio_readsb(card->func, card->buffer, IF_SDIO_FUN1_FIFO_RD, chunk);
+            sdio_release_host(card->func);
+                       if (ret) {
+                               RDA5890_ERRP("sdio_readsb fail, ret = %d\n", ret);
+                           goto out;
+                       }
+#if 1
+            if(priv->version == 7)
+                               sdio_writeb(card->func, 0x20 ,IF_SDIO_FUN1_INT_PEND, &ret);
+#endif            
+
+                       /* TODO: this chunk size need to be handled here */
+                       {
+                               unsigned char rx_type;
+                               unsigned short rx_length;
+                               unsigned char msg_type;
+                               unsigned char *packet = (unsigned char *)card->buffer;
+
+                               RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+                                       "%s >>>\n", __func__);
+
+                               rx_type = (unsigned char)packet[1]&0xF0;
+                               rx_length = (unsigned short)(packet[0] + ((packet[1]&0x0f) << 8));
+
+                               if (rx_length > chunk) {
+                                       RDA5890_ERRP("packet_len %d less than header specified length %d\n", 
+                                               chunk, rx_length);
+                                       goto out;
+                               }
+
+                               if( rx_type == 0x30 )
+                               {
+                                       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_NORM,
+                                               "Message Packet type =%x \n", msg_type);
+                                       msg_type = packet[2];
+
+                                       if (msg_type == 'R')
+                                       {               
+                        packet += 2;
+                        if(priv->wid_msg_id - 1 == packet[1])
+                        {
+                            ret = 0;
+                            if(priv->wid_rsp_len > rx_length - 2)
+                            {
+                                priv->wid_rsp_len = rx_length - 2;
+                                memcpy(priv->wid_rsp, packet, rx_length -2);
+                            }
+                            break;
+                        }
+                        else
+                            RDA5890_ERRP("rda5890_wid_request_polling wid_msg_id is wrong %d %d wid=%x \n", priv->wid_msg_id -1, packet[1], (packet[4] | (packet[5] << 8)));                                       
+                                       }
+                               }
+                       }
+               }
+        
+       rda5890_shedule_timeout(3); //3ms delay
+       ret = -1;
+       retry ++;
+       }
+
+    if(ret < 0 && count <= 3)
+        goto re_send;
+out:
+    
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s <<< wid: %x %x retry: %d count = %d \n", __func__, wid_req[4],wid_req[5], retry, count);
+
+       return ret;
+}
+void rda5890_wid_response(struct rda5890_private *priv, 
+               char *wid_rsp, unsigned short wid_rsp_len)
+{
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       if (!priv->wid_pending) {
+               RDA5890_ERRP("no wid pending\n");
+               return;
+       }
+
+       if (wid_rsp_len > priv->wid_rsp_len) {
+               RDA5890_ERRP("not enough space for wid response, size = %d, buf = %d\n",
+                       wid_rsp_len, priv->wid_rsp_len);
+               complete(&priv->wid_done); 
+               return;
+       }
+
+       memcpy(priv->wid_rsp, wid_rsp, wid_rsp_len);
+       priv->wid_rsp_len = wid_rsp_len;
+       priv->wid_pending = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+
+       complete(&priv->wid_done); 
+}
+
+void rda5890_wid_status(struct rda5890_private *priv, 
+               char *wid_status, unsigned short wid_status_len)
+{
+       char mac_status;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+#ifdef WIFI_UNLOCK_SYSTEM
+    rda5990_wakeLock();
+#endif
+
+       mac_status = wid_status[7];
+       if (mac_status == MAC_CONNECTED) {
+               RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_CRIT,
+                       "MAC CONNECTED\n");
+        
+               priv->connect_status = MAC_CONNECTED;
+               netif_carrier_on(priv->dev);
+               netif_wake_queue(priv->dev);
+        rda5890_indicate_connected(priv);
+                
+           priv->first_init = 0;
+        cancel_delayed_work(&priv->assoc_done_work);
+               queue_delayed_work(priv->work_thread, &priv->assoc_done_work, 0);
+        
+        if(test_and_clear_bit(ASSOC_FLAG_ASSOC_RETRY, &priv->assoc_flags))
+        {
+            cancel_delayed_work_sync(&priv->assoc_work);
+        }
+
+        if(test_bit(ASSOC_FLAG_WLAN_CONNECTING, &priv->assoc_flags))        
+            cancel_delayed_work(&priv->wlan_connect_work);
+        
+        set_bit(ASSOC_FLAG_WLAN_CONNECTING, &priv->assoc_flags);
+               queue_delayed_work(priv->work_thread, &priv->wlan_connect_work, HZ*20);
+        
+       }
+       else if (mac_status == MAC_DISCONNECTED) {
+        
+               RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_CRIT,
+                       "MAC DISCONNECTED\n");
+               
+        if(priv->connect_status == MAC_CONNECTED)      
+                   is_need_set_notch = 1;
+               else
+                   is_need_set_notch = 0;
+        
+        if(!test_bit(ASSOC_FLAG_ASSOC_RETRY, &priv->assoc_flags))
+            {
+                       priv->connect_status = MAC_DISCONNECTED;
+                netif_stop_queue(priv->dev);
+                       netif_carrier_off(priv->dev);
+                if(!priv->first_init) // the first disconnect should not send to upper
+                           rda5890_indicate_disconnected(priv);
+                else
+                    priv->first_init = 0;
+                
+                if(test_bit(ASSOC_FLAG_ASSOC_START, &priv->assoc_flags))
+                {
+                    cancel_delayed_work(&priv->wlan_connect_work);
+                           queue_delayed_work(priv->work_thread, &priv->wlan_connect_work, HZ*4);
+                }
+                
+                clear_bit(ASSOC_FLAG_ASSOC_START, &priv->assoc_flags);
+                clear_bit(ASSOC_FLAG_WLAN_CONNECTING, &priv->assoc_flags);
+            }
+        else
+            {
+                RDA5890_ERRP("********wep assoc will be retry ---------- 0x%02x\n", mac_status);
+            }
+       }
+       else {
+               RDA5890_ERRP("Invalid MAC Status 0x%02x\n", mac_status);
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+    
+#ifdef WIFI_UNLOCK_SYSTEM
+    rda5990_wakeUnlock();
+#endif    
+}
+
+void rda5890_card_to_host(struct rda5890_private *priv, 
+               char *packet, unsigned short packet_len)
+{
+       unsigned char rx_type;
+       unsigned short rx_length;
+       unsigned char msg_type;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s >>>\n", __func__);
+
+       rx_type = (unsigned char)packet[1]&0xF0;
+       rx_length = (unsigned short)(packet[0] + ((packet[1]&0x0f) << 8));
+
+       if (rx_length > packet_len) {
+               RDA5890_ERRP("packet_len %d less than header specified length %d\n", 
+                       packet_len, rx_length);
+               goto out;
+       }
+
+       if( rx_type == 0x30 )
+       {
+               RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_NORM,
+                       "Message Packet\n");
+               msg_type = packet[2];
+               if(msg_type == 'I')
+               {
+                       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_NORM,
+                               "Indication Message\n");
+                       rda5890_wid_status(priv, packet + 2, rx_length - 2);
+               }
+               else if (msg_type == 'R')
+               {
+                       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_NORM,
+                               "WID Message\n");
+                       rda5890_wid_response(priv, packet + 2, rx_length - 2);
+               }
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+        else if(msg_type == 'N')
+        {
+            extern void rda5890_network_information(struct rda5890_private *priv, 
+            char *info, unsigned short info_len);
+            rda5890_network_information(priv, packet + 2, rx_length - 2);
+        }
+#endif             
+               else {
+                       //RDA5890_ERRP("Invalid Message Type '%c'\n", msg_type);
+               }
+       }
+       else if(rx_type == 0x20)
+       {
+               RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_NORM,
+                       "Data Packet\n");
+               rda5890_data_rx(priv, packet + 2, rx_length - 2);
+       }
+       else {
+               RDA5890_ERRP("Invalid Packet Type 0x%02x\n", rx_type);
+       }
+
+out:
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s <<<\n", __func__);
+}
+
+/*
+ * check wid response header
+ * if no error, return the pointer to the payload
+ * support only 1 wid per packet
+ */
+int rda5890_check_wid_response(char *wid_rsp, unsigned short wid_rsp_len,
+               unsigned short wid, char wid_msg_id, 
+               char payload_len, char **ptr_payload)
+{
+       unsigned short rsp_len;
+       unsigned short rsp_wid;
+       unsigned char msg_len;
+
+       if (wid_rsp[0] != 'R') {
+               RDA5890_ERRP("wid_rsp[0] != 'R'\n");
+               goto err;
+       }
+
+       if (wid_rsp[1] != wid_msg_id) {
+               RDA5890_ERRP("wid_msg_id not match msg_id: %d \n", wid_rsp[1]);
+               goto err;
+       }
+
+       if (wid_rsp_len < 4) {
+               RDA5890_ERRP("wid_rsp_len < 4\n");
+               goto err;
+       }
+
+       rsp_len = wid_rsp[2] | (wid_rsp[3] << 8);
+       if (wid_rsp_len != rsp_len) {
+               RDA5890_ERRP("wid_rsp_len not match, %d != %d\n", wid_rsp_len, rsp_len);
+               goto err;
+       }
+
+       if (wid_rsp_len < 7) {
+               RDA5890_ERRP("wid_rsp_len < 7\n");
+               goto err;
+       }
+#if 0
+       rsp_wid = wid_rsp[4] | (wid_rsp[5] << 8);
+       if (wid != rsp_wid) {
+               RDA5890_ERRP("wid not match, 0x%04x != 0x%04x\n", wid, rsp_wid);
+               goto err;
+       }
+#endif
+       msg_len = wid_rsp[6];
+       if (wid_rsp_len != msg_len + 7) {
+               RDA5890_ERRP("msg_len not match, %d + 7 != %d\n", msg_len, wid_rsp_len);
+               goto err;
+       }
+
+       if (payload_len != msg_len) {
+               RDA5890_ERRP("payload_len not match, %d  != %d\n", msg_len, payload_len);
+               goto err;
+       }
+
+       *ptr_payload = wid_rsp + 7;
+
+    return 0;
+    
+err:
+    RDA5890_ERRP("rda5890_check_wid_response failed wid=0x%04x wid_msg_id:%d \n" ,wid_rsp[4] | (wid_rsp[5] << 8), wid_msg_id);
+       return -EINVAL;
+}
+
+/*
+ * check wid status response
+ */
+int rda5890_check_wid_status(char *wid_rsp, unsigned short wid_rsp_len, 
+               char wid_msg_id)
+{
+       int ret;
+       unsigned short wid = WID_STATUS;
+       char *ptr_payload;
+       char status_val;
+
+       ret = rda5890_check_wid_response(wid_rsp, wid_rsp_len, wid, wid_msg_id, 
+           1, &ptr_payload);
+       if (ret) {
+               RDA5890_ERRP("rda5890_check_wid_status, check_wid_response fail\n");
+               return ret;
+       }
+
+       status_val = ptr_payload[0];
+       if (status_val != WID_STATUS_SUCCESS) {
+               RDA5890_ERRP("check_wid_status NOT success, status = %d\n", status_val);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int rda5890_generic_get_uchar(struct rda5890_private *priv, 
+               unsigned short wid, unsigned char *val)
+{
+       int ret;
+       char wid_req[6];
+       unsigned short wid_req_len = 6;
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       char wid_msg_id = priv->wid_msg_id++;
+       char *ptr_payload;
+
+       wid_req[0] = 'Q';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_response(wid_rsp, wid_rsp_len, wid, wid_msg_id, 
+               sizeof(unsigned char), &ptr_payload);
+       if (ret) {
+               RDA5890_ERRP("check_wid_response fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       *val = *ptr_payload;
+out:
+       return ret;
+}
+
+int rda5890_generic_set_uchar(struct rda5890_private *priv, 
+               unsigned short wid, unsigned char val)
+{
+       int ret;
+       char wid_req[7 + sizeof(unsigned char)];
+       unsigned short wid_req_len = 7 + 1;
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       char wid_msg_id = priv->wid_msg_id++;
+
+       wid_req[0] = 'W';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       wid_req[6] = 1;
+       wid_req[7] = val;
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_status(wid_rsp, wid_rsp_len, wid_msg_id);
+       if (ret) {
+               RDA5890_ERRP("check_wid_status fail, ret = %d\n", ret);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+#if 1
+int rda5890_generic_get_ushort(struct rda5890_private *priv, 
+               unsigned short wid, unsigned short *val)
+{
+       int ret;
+       char wid_req[6];
+       unsigned short wid_req_len = 6;
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       char wid_msg_id = priv->wid_msg_id++;
+       char *ptr_payload;
+
+       wid_req[0] = 'Q';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_response(wid_rsp, wid_rsp_len, wid, wid_msg_id, 
+               sizeof(unsigned short), &ptr_payload);
+       if (ret) {
+               RDA5890_ERRP("check_wid_response fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       memcpy(val, ptr_payload, sizeof(unsigned short)); 
+
+out:
+       return ret;
+}
+
+int rda5890_generic_set_ushort(struct rda5890_private *priv, 
+               unsigned short wid, unsigned short val)
+{
+       int ret;
+       char wid_req[7 + sizeof(unsigned short)];
+       unsigned short wid_req_len = 7 + sizeof(unsigned short);
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       char wid_msg_id = priv->wid_msg_id++;
+
+       wid_req[0] = 'W';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       wid_req[6] = (char)(sizeof(unsigned short));
+       memcpy(wid_req + 7, &val, sizeof(unsigned short)); 
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_status(wid_rsp, wid_rsp_len, wid_msg_id);
+       if (ret) {
+               RDA5890_ERRP("check_wid_status fail, ret = %d\n", ret);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+#endif
+
+int rda5890_generic_get_ulong(struct rda5890_private *priv, 
+               unsigned short wid, unsigned long *val)
+{
+       int ret;
+       char wid_req[6];
+       unsigned short wid_req_len = 6;
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       char wid_msg_id = priv->wid_msg_id++;
+       char *ptr_payload;
+
+       wid_req[0] = 'Q';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_response(wid_rsp, wid_rsp_len, wid, wid_msg_id, 
+               sizeof(unsigned long), &ptr_payload);
+       if (ret) {
+               RDA5890_ERRP("check_wid_response fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       memcpy(val, ptr_payload, sizeof(unsigned long)); 
+
+out:
+       return ret;
+}
+
+int rda5890_generic_set_ulong(struct rda5890_private *priv, 
+               unsigned short wid, unsigned long val)
+{
+       int ret;
+       char wid_req[7 + sizeof(unsigned long)];
+       unsigned short wid_req_len = 7 + sizeof(unsigned long);
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       char wid_msg_id = priv->wid_msg_id++;
+
+       wid_req[0] = 'W';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       wid_req[6] = (char)(sizeof(unsigned long));
+       memcpy(wid_req + 7, &val, sizeof(unsigned long)); 
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_status(wid_rsp, wid_rsp_len, wid_msg_id);
+       if (ret) {
+               RDA5890_ERRP("check_wid_status fail, ret = %d\n", ret);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+int rda5890_generic_get_str(struct rda5890_private *priv, 
+               unsigned short wid, unsigned char *val, unsigned char len)
+{
+       int ret;
+       char wid_req[6];
+       unsigned short wid_req_len = 6;
+       char wid_rsp[RDA5890_MAX_WID_LEN];
+       unsigned short wid_rsp_len = RDA5890_MAX_WID_LEN;
+       char wid_msg_id = priv->wid_msg_id++;
+       char *ptr_payload;
+
+       wid_req[0] = 'Q';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_response(wid_rsp, wid_rsp_len, wid, wid_msg_id, 
+               (char)len, &ptr_payload);
+       if (ret) {
+               RDA5890_ERRP("check_wid_response fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       memcpy(val, ptr_payload, len); 
+
+out:
+       return ret;
+}
+
+int rda5890_generic_set_str(struct rda5890_private *priv, 
+               unsigned short wid, unsigned char *val, unsigned char len)
+{
+       int ret;
+       char wid_req[RDA5890_MAX_WID_LEN];
+       unsigned short wid_req_len = 7 + len;
+       char wid_rsp[RDA5890_MAX_WID_LEN];
+       unsigned short wid_rsp_len = RDA5890_MAX_WID_LEN;
+       char wid_msg_id = priv->wid_msg_id++;
+
+       wid_req[0] = 'W';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       wid_req[6] = (char)(len);
+       memcpy(wid_req + 7, val, len); 
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_status(wid_rsp, wid_rsp_len, wid_msg_id);
+       if (ret) {
+               RDA5890_ERRP("check_wid_status fail, ret = %d\n", ret);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+int rda5890_check_wid_response_unknown_len(
+        char *wid_rsp, unsigned short wid_rsp_len,
+               unsigned short wid, char wid_msg_id, 
+               char *payload_len, char **ptr_payload)
+{
+       unsigned short rsp_len;
+       unsigned short rsp_wid;
+       unsigned char msg_len;
+
+       if (wid_rsp[0] != 'R') {
+               RDA5890_ERRP("wid_rsp[0] != 'R'\n");
+               return -EINVAL;
+       }
+
+       if (wid_rsp[1] != wid_msg_id) {
+               RDA5890_ERRP("wid_msg_id not match\n");
+               return -EINVAL;
+       }
+
+       if (wid_rsp_len < 4) {
+               RDA5890_ERRP("wid_rsp_len < 4\n");
+               return -EINVAL;
+       }
+
+       rsp_len = wid_rsp[2] | (wid_rsp[3] << 8);
+       if (wid_rsp_len != rsp_len) {
+               RDA5890_ERRP("wid_rsp_len not match, %d != %d\n", wid_rsp_len, rsp_len);
+               return -EINVAL;
+       }
+
+       if (wid_rsp_len < 7) {
+               RDA5890_ERRP("wid_rsp_len < 7\n");
+               return -EINVAL;
+       }
+#if 0
+       rsp_wid = wid_rsp[4] | (wid_rsp[5] << 8);
+       if (wid != rsp_wid) {
+               RDA5890_ERRP("wid not match, 0x%04x != 0x%04x\n", wid, rsp_wid);
+               return -EINVAL;
+       }
+#endif
+       msg_len = wid_rsp[6];
+       if (wid_rsp_len != msg_len + 7) {
+               RDA5890_ERRP("msg_len not match, %d + 7 != %d\n", msg_len, wid_rsp_len);
+               return -EINVAL;
+       }
+
+       *payload_len = msg_len;
+
+       *ptr_payload = wid_rsp + 7;
+
+       return 0;
+err:
+
+    RDA5890_ERRP("wid is %x wid_msg %d \n", wid_rsp[4] | (wid_rsp[5] << 8), wid_rsp[1]);
+    return -EINVAL;
+}
+
+int rda5890_get_str_unknown_len(struct rda5890_private *priv, 
+               unsigned short wid, unsigned char *val, unsigned char *len)
+{
+       int ret;
+       char wid_req[6];
+       unsigned short wid_req_len = 6;
+       char wid_rsp[RDA5890_MAX_WID_LEN];
+       unsigned short wid_rsp_len = RDA5890_MAX_WID_LEN;
+       char wid_msg_id = priv->wid_msg_id++;
+       char *ptr_payload;
+
+       wid_req[0] = 'Q';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_response_unknown_len(
+           wid_rsp, wid_rsp_len, wid, wid_msg_id, (char*)len, &ptr_payload);
+       if (ret) {
+               RDA5890_ERRP("check_wid_response fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       memcpy(val, ptr_payload, *len); 
+
+out:
+       return ret;
+}
+
+extern int rda5890_sdio_set_default_notch(struct rda5890_private *priv);
+int rda5890_start_scan(struct rda5890_private *priv)
+{
+       int ret;
+       char wid_req[12];
+       unsigned short wid_req_len = 12;
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       unsigned short wid;
+       char wid_msg_id = 0;
+
+       if(priv->connect_status != MAC_CONNECTED)
+    {
+        rda5890_sdio_set_default_notch(priv);
+    }
+
+    wid_msg_id = priv->wid_msg_id++;
+    
+       wid_req[0] = 'W';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid = WID_SITE_SURVEY;
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       wid_req[6] = (char)(0x01);
+       wid_req[7] = (char)(0x01);
+
+       wid = WID_START_SCAN_REQ;
+       wid_req[8] = (char)(wid&0x00FF);
+       wid_req[9] = (char)((wid&0xFF00) >> 8);
+
+       wid_req[10] = (char)(0x01);
+       wid_req[11] = (char)(0x01);
+    
+
+    wid_req_len = 12;
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+    
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_status(wid_rsp, wid_rsp_len, wid_msg_id);
+       if (ret) {
+               RDA5890_ERRP("check_wid_status fail, ret = %d\n", ret);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+
+int rda5890_start_scan_enable_network_info(struct rda5890_private *priv)
+{
+       int ret;
+       char wid_req[12];
+       unsigned short wid_req_len = 12;
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       unsigned short wid;
+       char wid_msg_id = 0;
+
+       if(priv->connect_status != MAC_CONNECTED)
+    {
+        rda5890_sdio_set_default_notch(priv);
+    }
+
+    wid_msg_id = priv->wid_msg_id++;
+    
+       wid_req[0] = 'W';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid = WID_SITE_SURVEY;
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+       wid_req[6] = (char)(0x01);
+       wid_req[7] = (char)(0x01);
+
+       wid = WID_START_SCAN_REQ;
+       wid_req[8] = (char)(wid&0x00FF);
+       wid_req[9] = (char)((wid&0xFF00) >> 8);
+
+       wid_req[10] = (char)(0x01);
+       wid_req[11] = (char)(0x01);
+    
+    wid = WID_NETWORK_INFO_EN;
+       wid_req[12] = (char)(wid&0x00FF);
+       wid_req[13] = (char)((wid&0xFF00) >> 8);
+
+       wid_req[14] = (char)(0x01);
+       wid_req[15] = (char)(0x01); // 0x01 scan network info
+       
+       wid_req_len = 16;
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_wid_request fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_status(wid_rsp, wid_rsp_len, wid_msg_id);
+       if (ret) {
+               RDA5890_ERRP("check_wid_status fail, ret = %d\n", ret);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+int rda5890_start_join(struct rda5890_private *priv)
+{
+    int ret;
+       char wid_req[255];
+       unsigned short wid_req_len = 0;
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       unsigned short wid;
+       char wid_msg_id = priv->wid_msg_id++;
+    unsigned short i = 0;
+    unsigned char * wep_key = 0, key_str_len = 0;
+    unsigned char key_str[26 + 1] , * key, *pWid_req;
+    
+    RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+        "%s <<< mode:%d authtype:%d ssid:%s\n", __func__, priv->imode, priv->authtype,
+        priv->assoc_ssid);
+    
+       wid_req[0] = 'W';
+       wid_req[1] = wid_msg_id;
+
+
+    wid = WID_802_11I_MODE;
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+    
+       wid_req[6] = 1;
+    wid_req[7] = priv->imode;
+
+    wid = WID_AUTH_TYPE;
+    wid_req[8] = (char)(wid&0x00FF);
+       wid_req[9] = (char)((wid&0xFF00) >> 8);
+    
+       wid_req[10] = 1;
+    wid_req[11] = priv->authtype;
+    
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+    wid = WID_NETWORK_INFO_EN;
+    wid_req[12] = (char)(wid&0x00FF);
+       wid_req[13] = (char)((wid&0xFF00) >> 8);
+    
+       wid_req[14] = 1;
+    wid_req[15] = 0;
+
+    wid = WID_CURRENT_TX_RATE;
+    wid_req[16] = (char)(wid&0x00FF);
+       wid_req[17] = (char)((wid&0xFF00) >> 8);
+    
+       wid_req[18] = 1;
+    wid_req[19] = 1;
+    
+    wid_req_len = 20;
+    pWid_req = wid_req + 20;    
+#else
+    wid_req_len = 12;
+    pWid_req = wid_req + 12;
+#endif
+    wid = WID_WEP_KEY_VALUE0;
+    if(priv->imode == 3 || priv->imode == 7) //write wep key
+    {
+        for(i = 0 ; i < 4; i ++)
+        {
+            key = priv->wep_keys[i].key;
+
+            if(priv->wep_keys[i].len == 0)
+                continue;
+            
+            if (priv->wep_keys[i].len == KEY_LEN_WEP_40) {
+                sprintf(key_str, "%02x%02x%02x%02x%02x\n", 
+                key[0], key[1], key[2], key[3], key[4]);
+                key_str_len = 10;
+                key_str[key_str_len] = '\0';
+            }
+            else if (priv->wep_keys[i].len == KEY_LEN_WEP_104) {
+                sprintf(key_str, "%02x%02x%02x%02x%02x"
+                "%02x%02x%02x%02x%02x"
+                "%02x%02x%02x\n",
+                key[0], key[1], key[2], key[3], key[4],
+                key[5], key[6], key[7], key[8],        key[9],
+                key[10], key[11], key[12]);
+                key_str_len = 26;
+                key_str[key_str_len] = '\0';
+            }
+            else
+                continue;
+
+            pWid_req[0] = (char)((wid + i)&0x00FF);
+               pWid_req[1] = (char)(((wid + i)&0xFF00) >> 8);
+    
+               pWid_req[2] = key_str_len;
+            memcpy(pWid_req + 3, key_str, key_str_len);
+            
+            pWid_req += 3 + key_str_len;
+            wid_req_len += 3 + key_str_len;            
+        }
+    }
+
+//  ssid
+    wid = WID_SSID;
+    pWid_req[0] = (char)(wid&0x00FF);
+       pWid_req[1] = (char)((wid&0xFF00) >> 8);
+    
+       pWid_req[2] = priv->assoc_ssid_len;
+    memcpy(pWid_req + 3, priv->assoc_ssid, priv->assoc_ssid_len);
+    
+    wid_req_len += 3 + priv->assoc_ssid_len;
+
+    
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_start_join fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       ret = rda5890_check_wid_status(wid_rsp, wid_rsp_len, wid_msg_id);
+       if (ret) {
+               RDA5890_ERRP("rda5890_start_join check status fail, ret = %d\n", ret);
+               goto out;
+       }
+
+out:
+    
+    RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+        "%s >>> ret = %d req len %d mod:0x%x auth_type:0x%x \n", __func__, ret, wid_req_len, priv->imode
+        , priv->authtype);
+       return ret;    
+}
+
+int rda5890_set_txrate(struct rda5890_private *priv, unsigned char mbps)
+{
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "rda5890_set_txrate <<< \n");
+
+       ret = rda5890_generic_set_uchar(priv, WID_CURRENT_TX_RATE, mbps); //O FOR AUTO 1FOR 1MBPS
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "rda5890_set_txrate success >>> \n");
+
+out:
+       return ret;
+}
+
+int rda5890_set_core_init_polling(struct rda5890_private *priv, const unsigned int (*data)[2], unsigned char num)
+{
+        int ret = -1;
+        char wid_req[255];
+        unsigned short wid_req_len = 4 + 14*num;
+        char wid_rsp[32];
+        unsigned short wid_rsp_len = 32;
+        unsigned short wid;
+        char wid_msg_id = priv->wid_msg_id++;
+           char count = 0, *p_wid_req = NULL;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s <<< \n", __func__);
+
+        wid_req[0] = 'W';
+        wid_req[1] = wid_msg_id;
+
+        wid_req[2] = (char)(wid_req_len&0x00FF);
+        wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+        p_wid_req = wid_req + 4;
+        for(count = 0; count < num; count ++)
+        {
+            wid = WID_MEMORY_ADDRESS;
+            p_wid_req[0] = (char)(wid&0x00FF);
+            p_wid_req[1] = (char)((wid&0xFF00) >> 8);
+
+            p_wid_req[2] = (char)4;
+            memcpy(p_wid_req + 3, (char*)&data[count][0], 4);
+
+            wid = WID_MEMORY_ACCESS_32BIT;
+            p_wid_req[7] = (char)(wid&0x00FF);
+            p_wid_req[8] = (char)((wid&0xFF00) >> 8);
+
+            p_wid_req[9] = (char)4;
+            memcpy(p_wid_req + 10, (char*)&data[count][1], 4);
+            p_wid_req += 14;
+        }
+        ret = rda5890_wid_request_polling(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+                RDA5890_ERRP("rda5890_set_sdio_core_init fail, ret = %d\n", ret);
+                goto out;
+        }
+
+out:
+        RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s >>> \n", __func__);
+        return ret;
+}
+
+int rda5890_set_core_init(struct rda5890_private *priv, const unsigned int (*data)[2], unsigned char num)
+{
+        int ret = -1;
+        char wid_req[255];
+        unsigned short wid_req_len = 4 + 14*num;
+        char wid_rsp[32];
+        unsigned short wid_rsp_len = 32;
+        unsigned short wid;
+        char wid_msg_id = priv->wid_msg_id++;
+        char count = 0, *p_wid_req = NULL;
+
+        wid_req[0] = 'W';
+        wid_req[1] = wid_msg_id;
+
+        wid_req[2] = (char)(wid_req_len&0x00FF);
+        wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+        p_wid_req = wid_req + 4;
+        for(count = 0; count < num; count ++)
+        {
+            wid = WID_MEMORY_ADDRESS;
+            p_wid_req[0] = (char)(wid&0x00FF);
+            p_wid_req[1] = (char)((wid&0xFF00) >> 8);
+
+            p_wid_req[2] = (char)4;
+            memcpy(p_wid_req + 3, (char*)&data[count][0], 4);
+
+            wid = WID_MEMORY_ACCESS_32BIT;
+            p_wid_req[7] = (char)(wid&0x00FF);
+            p_wid_req[8] = (char)((wid&0xFF00) >> 8);
+
+            p_wid_req[9] = (char)4;
+            memcpy(p_wid_req + 10, (char*)&data[count][1], 4);
+            p_wid_req += 14;
+        }
+        ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+                RDA5890_ERRP("rda5890_set_sdio_core_init fail, ret = %d\n", ret);
+                goto out;
+        }
+
+out:
+        return ret;
+}
+
+int rda5890_set_core_patch_polling(struct rda5890_private *priv, const unsigned char (*patch)[2], unsigned char num)
+{
+        int ret;
+        char wid_req[255];
+        unsigned short wid_req_len = 4 + 8*num;
+        char wid_rsp[32];
+        unsigned short wid_rsp_len = 32;
+        unsigned short wid;
+        char wid_msg_id = priv->wid_msg_id++;
+        char count = 0 , *p_wid_req = NULL;    
+        
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s <<< \n", __func__); 
+        
+        wid_req[0] = 'W';
+        wid_req[1] = wid_msg_id;
+
+        wid_req[2] = (char)(wid_req_len&0x00FF);
+        wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       p_wid_req = wid_req + 4;
+       for(count = 0; count < num; count ++)
+        {
+            wid = WID_PHY_ACTIVE_REG;
+            p_wid_req[0] = (char)(wid&0x00FF);
+            p_wid_req[1] = (char)((wid&0xFF00) >> 8);
+
+            p_wid_req[2] = (char)(0x01);
+            p_wid_req[3] = patch[count][0];
+
+            wid = WID_PHY_ACTIVE_REG_VAL;
+            p_wid_req[4] = (char)(wid&0x00FF);
+            p_wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+            p_wid_req[6] = (char)(0x01);
+            p_wid_req[7] = patch[count][1];
+            p_wid_req += 8;
+        }
+        ret = rda5890_wid_request_polling(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+        if (ret) {
+                RDA5890_ERRP("rda5890_set_core_patch fail, ret = %d \n", ret);
+                goto out;
+        }
+
+out:
+        RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_DEBUG,
+               "%s >>> \n", __func__); 
+        return ret;
+}
+
+int rda5890_set_core_patch(struct rda5890_private *priv, const unsigned char (*patch)[2], unsigned char num)
+{
+        int ret;
+        char wid_req[255];
+        unsigned short wid_req_len = 4 + 8*num;
+        char wid_rsp[32];
+        unsigned short wid_rsp_len = 32;
+        unsigned short wid;
+        char wid_msg_id = priv->wid_msg_id++;
+        char count = 0 , *p_wid_req = NULL;    
+       
+        wid_req[0] = 'W';
+        wid_req[1] = wid_msg_id;
+
+        wid_req[2] = (char)(wid_req_len&0x00FF);
+        wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+           p_wid_req = wid_req + 4;
+        for(count = 0; count < num; count ++)
+        {
+            wid = WID_PHY_ACTIVE_REG;
+            p_wid_req[0] = (char)(wid&0x00FF);
+            p_wid_req[1] = (char)((wid&0xFF00) >> 8);
+
+            p_wid_req[2] = (char)(0x01);
+            p_wid_req[3] = patch[count][0];
+
+            wid = WID_PHY_ACTIVE_REG_VAL;
+            p_wid_req[4] = (char)(wid&0x00FF);
+            p_wid_req[5] = (char)((wid&0xFF00) >> 8);
+
+            p_wid_req[6] = (char)(0x01);
+            p_wid_req[7] = patch[count][1];
+            p_wid_req += 8;
+        }
+        ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+        if (ret) {
+                RDA5890_ERRP("rda5890_set_core_patch fail, ret = %d \n", ret);
+                goto out;
+        }
+
+out:
+        return ret;
+}
+
+
+int rda5890_get_fw_ver(struct rda5890_private *priv, unsigned long *fw_ver)
+{
+       int ret;
+
+       ret = rda5890_generic_get_ulong(priv, WID_SYS_FW_VER, fw_ver);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Get FW_VER 0x%08lx\n", *fw_ver);
+
+out:
+       return ret;
+}
+
+int rda5890_get_mac_addr(struct rda5890_private *priv, unsigned char *mac_addr)
+{
+       int ret;
+
+       ret = rda5890_generic_get_str(priv, WID_MAC_ADDR, mac_addr, ETH_ALEN);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "STA MAC Address [%02x:%02x:%02x:%02x:%02x:%02x]\n",
+               mac_addr[0], mac_addr[1], mac_addr[2],
+               mac_addr[3], mac_addr[4], mac_addr[5]);
+out:
+       return ret;
+}
+
+/* support only one bss per packet for now */
+int rda5890_get_scan_results(struct rda5890_private *priv, 
+               struct rda5890_bss_descriptor *bss_desc)
+{
+       int ret;
+       int count = 0;
+       unsigned char len;
+       unsigned char buf[sizeof(struct rda5890_bss_descriptor)*RDA5890_MAX_NETWORK_NUM + 2];
+    unsigned char first_send = 0;
+
+       ret = rda5890_get_str_unknown_len(priv, WID_SITE_SURVEY_RESULTS,
+               buf, &len);
+       if (ret) {
+               return ret;
+       }
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Get Scan Result, len = %d\n", len);
+
+       if ((len - 2) % sizeof(struct rda5890_bss_descriptor)) {
+               RDA5890_ERRP("Scan Result len not correct, %d\n", len);
+               return -EINVAL;
+       }
+
+       count = (len - 2) / sizeof(struct rda5890_bss_descriptor);
+
+    first_send = *(buf + 1);
+       memcpy(bss_desc, buf + 2, (len - 2)); 
+
+       return (count | first_send << 8);
+}
+
+int rda5890_get_bssid(struct rda5890_private *priv, unsigned char *bssid)
+{
+       int ret;
+
+       ret = rda5890_generic_get_str(priv, WID_BSSID, bssid, ETH_ALEN);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Get BSSID [%02x:%02x:%02x:%02x:%02x:%02x]\n",
+               bssid[0], bssid[1], bssid[2],
+               bssid[3], bssid[4], bssid[5]);
+out:
+       return ret;
+}
+
+int rda5890_get_channel(struct rda5890_private *priv, unsigned char *channel)
+{
+       int ret;
+
+       ret = rda5890_generic_get_uchar(priv, WID_CURRENT_CHANNEL, channel);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Get Channel %d\n", *channel);
+
+out:
+       return ret;
+}
+
+int rda5890_get_rssi(struct rda5890_private *priv, unsigned char *rssi)
+{
+       int ret;
+
+    *rssi = 0;
+       ret = rda5890_generic_get_uchar(priv, WID_RSSI, rssi);
+       if (ret) {
+               goto out;
+       }
+
+
+       if(priv->connect_status == MAC_CONNECTED)
+       {
+               if(*rssi > 215)
+               {
+                       rda5890_rssi_up_to_200(priv);                   
+               }
+               else
+               {
+                       rda5890_sdio_set_notch_by_channel(priv, priv->curbssparams.channel);                    
+               }
+       }
+    else
+        *rssi = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Get RSSI <<< %d\n", *(char*)rssi);
+
+out:
+       return ret;
+}
+
+int rda5890_set_mac_addr(struct rda5890_private *priv, unsigned char *mac_addr)
+{
+       int ret;
+
+       ret = rda5890_generic_set_str(priv, WID_MAC_ADDR, mac_addr, ETH_ALEN);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set STA MAC Address [%02x:%02x:%02x:%02x:%02x:%02x]\n",
+               mac_addr[0], mac_addr[1], mac_addr[2],
+               mac_addr[3], mac_addr[4], mac_addr[5]);
+out:
+       return ret;
+}
+
+int rda5890_set_preamble(struct rda5890_private *priv, unsigned char  preamble)
+{
+       int ret;
+
+       ret = rda5890_generic_set_uchar(priv, WID_PREAMBLE, preamble);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE, "rda5890_set_preamble \n");
+out:
+       return ret;
+}
+
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+
+int rda5890_set_scan_complete(struct rda5890_private *priv)
+{
+       int ret;
+
+       ret = rda5890_generic_set_uchar(priv, WID_NETWORK_INFO_EN, 0);
+       if (ret) {
+               goto out;
+       }
+     
+out:
+      RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE, "rda5890_set_scan_complete  ret=%d \n", ret);
+       return ret;
+}
+#endif
+
+int rda5890_set_ssid(struct rda5890_private *priv, 
+               unsigned char *ssid, unsigned char ssid_len)
+{
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set SSID: %s, len = %d\n", ssid, ssid_len);
+
+       ret = rda5890_generic_set_str(priv, WID_SSID, ssid, ssid_len);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set SSID Done\n");
+
+out:
+       return ret;
+}
+
+int rda5890_get_ssid(struct rda5890_private *priv, 
+               unsigned char *ssid, unsigned char *ssid_len)
+{
+    int ret;
+    
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Get SSID \n");
+    
+    ret = rda5890_get_str_unknown_len(priv, WID_SSID, ssid, ssid_len);
+    if(*ssid_len > 0)
+        ssid[*ssid_len] = '\0';
+    
+    RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+       "Get SSID Done len:%d %s\n", *ssid_len, *ssid_len > 0? ssid:"NULL");
+out:
+       return ret;
+}
+int rda5890_set_bssid(struct rda5890_private *priv, unsigned char *bssid)
+{
+       int ret;
+
+       ret = rda5890_generic_set_str(priv, WID_BSSID, bssid, ETH_ALEN);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set BSSID [%02x:%02x:%02x:%02x:%02x:%02x]\n",
+               bssid[0], bssid[1], bssid[2],
+               bssid[3], bssid[4], bssid[5]);
+out:
+       return ret;
+}
+
+
+int rda5890_set_imode(struct rda5890_private *priv, unsigned char imode)
+{
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set IMode 0x%02x\n", imode);
+
+       ret = rda5890_generic_set_uchar(priv, WID_802_11I_MODE, imode);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set IMode Done\n");
+
+out:
+       return ret;
+}
+
+int rda5890_set_authtype(struct rda5890_private *priv, unsigned char authtype)
+{
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set AuthType 0x%02x\n", authtype);
+
+       ret = rda5890_generic_set_uchar(priv, WID_AUTH_TYPE, authtype);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set AuthType Done\n");
+
+out:
+       return ret;
+}
+
+int rda5890_set_listen_interval(struct rda5890_private *priv, unsigned char interval)
+{
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set rda5890_set_listen_interval 0x%02x\n", interval);
+
+       ret = rda5890_generic_set_uchar(priv, WID_LISTEN_INTERVAL, interval);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set rda5890_set_listen_interval Done\n");
+
+out:
+       return ret;
+}
+
+int rda5890_set_link_loss_threshold(struct rda5890_private *priv, unsigned char threshold)
+{
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set rda5890_set_link_loss_threshold 0x%02x\n", threshold);
+
+       ret = rda5890_generic_set_uchar(priv, WID_LINK_LOSS_THRESHOLD, threshold);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set rda5890_set_link_loss_threshold Done\n");
+
+out:
+       return ret;
+}
+
+
+int rda5890_set_wepkey(struct rda5890_private *priv, 
+               unsigned short index, unsigned char *key, unsigned char key_len)
+{
+       int ret;
+       unsigned char key_str[26 + 1]; // plus 1 for debug print
+       unsigned char key_str_len;
+
+       if (key_len == KEY_LEN_WEP_40) {
+               sprintf(key_str, "%02x%02x%02x%02x%02x\n", 
+                       key[0], key[1], key[2], key[3], key[4]);
+               key_str_len = 10;
+               key_str[key_str_len] = '\0';
+       }
+       else if (key_len == KEY_LEN_WEP_104) {
+               sprintf(key_str, "%02x%02x%02x%02x%02x"
+                       "%02x%02x%02x%02x%02x"
+                       "%02x%02x%02x\n",
+                       key[0], key[1], key[2], key[3], key[4],
+                       key[5], key[6], key[7], key[8], key[9],
+                       key[10], key[11], key[12]);
+               key_str_len = 26;
+               key_str[key_str_len] = '\0';
+       }
+       else {
+               RDA5890_ERRP("Error in WEP Key length %d\n", key_len);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set WEP KEY[%d]: %s\n", index, key_str);
+
+       ret = rda5890_generic_set_str(priv,
+               (WID_WEP_KEY_VALUE0 + index), key_str, key_str_len);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set WEP KEY[%d] Done\n", index);
+
+out:
+       return ret;
+}
+
+static void dump_key(unsigned char *key, unsigned char key_len)
+{
+       RDA5890_DBGP("%02x %02x %02x %02x  %02x %02x %02x %02x\n",
+               key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]);
+       RDA5890_DBGP("%02x %02x %02x %02x  %02x %02x %02x %02x\n",
+               key[8], key[9], key[10], key[11], key[12], key[13], key[14], key[15]);
+       if (key_len > 16)
+               RDA5890_DBGP("%02x %02x %02x %02x  %02x %02x %02x %02x\n",
+                       key[16], key[17], key[18], key[19], key[20], key[21], key[22], key[23]);
+       if (key_len > 24)
+               RDA5890_DBGP("%02x %02x %02x %02x  %02x %02x %02x %02x\n",
+                       key[24], key[25], key[26], key[27], key[28], key[29], key[30], key[31]);
+}
+
+int rda5890_set_ptk(struct rda5890_private *priv, 
+               unsigned char *key, unsigned char key_len)
+{
+       int ret;
+       unsigned char key_str[32 + ETH_ALEN + 1]; 
+       unsigned char key_str_len = key_len + ETH_ALEN + 1;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set PTK: len = %d\n", key_len);
+       if (RDA5890_DBGLA(RDA5890_DA_WID, RDA5890_DL_VERB))
+               dump_key(key, key_len);
+
+       if (priv->connect_status != MAC_CONNECTED) {
+               RDA5890_ERRP("Adding PTK while not connected\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /*----------------------------------------*/
+       /*    STA Addr  | KeyLength |   Key       */
+       /*----------------------------------------*/
+       /*       6      |     1     |  KeyLength  */
+       /*----------------------------------------*/
+
+       /*---------------------------------------------------------*/
+       /*                      key                                */
+       /*---------------------------------------------------------*/
+       /* Temporal Key    | Rx Micheal Key    |   Tx Micheal Key  */
+       /*---------------------------------------------------------*/
+       /*    16 bytes     |      8 bytes      |       8 bytes     */
+       /*---------------------------------------------------------*/
+
+       memcpy(key_str, priv->curbssparams.bssid, ETH_ALEN);
+       key_str[6] = key_len;
+       memcpy(key_str + 7, key, 16);
+
+       /* swap TX MIC and RX MIC, rda5890 need RX MIC to be ahead */
+       if(key_len > 16) {
+               memcpy(key_str + 7 + 16, key + 24, 8);
+               memcpy(key_str + 7 + 24, key + 16, 8);
+       }
+
+       if(priv->is_wapi)
+               ret = rda5890_generic_set_str(priv,
+                       WID_ADD_WAPI_PTK, key_str, key_str_len);
+       else
+               ret = rda5890_generic_set_str(priv,
+                       WID_ADD_PTK, key_str, key_str_len);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set PTK Done\n");
+
+out:
+       return ret;
+}
+
+int rda5890_set_gtk(struct rda5890_private *priv, unsigned char key_id,
+               unsigned char *key_rsc, unsigned char key_rsc_len,
+               unsigned char *key, unsigned char key_len)
+{
+       int ret;
+       unsigned char key_str[32 + ETH_ALEN + 8 + 2]; 
+       unsigned char key_str_len = key_len + ETH_ALEN + 8 + 2;
+
+       /*---------------------------------------------------------*/
+       /*    STA Addr  | KeyRSC | KeyID | KeyLength |   Key       */
+       /*---------------------------------------------------------*/
+       /*       6      |   8    |   1   |     1     |  KeyLength  */
+       /*---------------------------------------------------------*/
+
+       /*-------------------------------------*/
+       /*                      key            */
+       /*-------------------------------------*/
+       /* Temporal Key    | Rx Micheal Key    */
+       /*-------------------------------------*/
+       /*    16 bytes     |      8 bytes      */
+       /*-------------------------------------*/
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set GTK: len = %d\n", key_len);
+       if (RDA5890_DBGLA(RDA5890_DA_WID, RDA5890_DL_VERB))
+               dump_key(key, key_len);
+
+       if (priv->connect_status != MAC_CONNECTED) {
+               RDA5890_ERRP("Adding PTK while not connected\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       memcpy(key_str, priv->curbssparams.bssid, ETH_ALEN);
+       memcpy(key_str + 6, key_rsc, key_rsc_len);
+       key_str[14] = key_id;
+       key_str[15] = key_len;
+       memcpy(key_str + 16, key, 16);
+
+       /* swap TX MIC and RX MIC, rda5890 need RX MIC to be ahead */
+       if(key_len > 16) {
+               //memcpy(key_str + 16 + 16, key + 16, key_len - 16);
+               memcpy(key_str + 16 + 16, key + 24, 8);
+               memcpy(key_str + 16 + 24, key + 16, 8);
+       }
+
+       if(priv->is_wapi)
+               ret = rda5890_generic_set_str(priv,
+                       WID_ADD_WAPI_RX_GTK, key_str, key_str_len);
+       else
+               ret = rda5890_generic_set_str(priv,
+                       WID_ADD_RX_GTK, key_str, key_str_len);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set GTK Done\n");
+
+out:
+       return ret;
+}
+
+int rda5890_set_scan_timeout(struct rda5890_private *priv)
+{
+       int ret;
+       char wid_req[14];
+       unsigned short wid_req_len = 19;
+       char wid_rsp[32];
+       unsigned short wid_rsp_len = 32;
+       char wid_msg_id = priv->wid_msg_id++;
+       char *ptr_payload;
+    unsigned short wid = 0;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+       "rda5890_set_scan_timeout <<< \n");
+
+    wid = WID_SITE_SURVEY_SCAN_TIME;
+    
+       wid_req[0] = 'W';
+       wid_req[1] = wid_msg_id;
+
+       wid_req[2] = (char)(wid_req_len&0x00FF);
+       wid_req[3] = (char)((wid_req_len&0xFF00) >> 8);
+
+       wid_req[4] = (char)(wid&0x00FF);
+       wid_req[5] = (char)((wid&0xFF00) >> 8);
+    wid_req[6] = 2;
+    wid_req[7] = 50;    //50 ms one channel
+    wid_req[8] = 0;
+
+    wid = WID_ACTIVE_SCAN_TIME;
+    wid_req[9] = (char)(wid&0x00FF);
+       wid_req[10] = (char)((wid&0xFF00) >> 8);
+    wid_req[11] = 2;
+    wid_req[12] = 50;   //50 ms one channel
+    wid_req[13] = 0;
+
+    wid = WID_PASSIVE_SCAN_TIME;
+    wid_req[14] = (char)(wid&0x00FF);
+       wid_req[15] = (char)((wid&0xFF00) >> 8);
+    wid_req[16] = 2;
+    wid_req[17] = 50;   //50 ms one channel
+    wid_req[18] = 0;
+
+       ret = rda5890_wid_request(priv, wid_req, wid_req_len, wid_rsp, &wid_rsp_len);
+       if (ret) {
+               RDA5890_ERRP("rda5890_set_scan_timeout fail, ret = %d\n", ret);
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "rda5890_set_scan_timeout >>> \n");
+
+out:
+       return ret;
+}
+
+
+int rda5890_set_pm_mode(struct rda5890_private *priv, unsigned char pm_mode)
+{
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set PM Mode 0x%02x\n", pm_mode);
+
+       ret = rda5890_generic_set_uchar(priv, WID_POWER_MANAGEMENT, pm_mode);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set PM Mode Done\n");
+
+out:
+       return ret;
+}
+
+int rda5890_set_preasso_sleep(struct rda5890_private *priv, unsigned int preasso_sleep)
+{
+       int ret;
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set Preasso Sleep 0x%08x\n", preasso_sleep);
+
+       ret = rda5890_generic_set_ulong(priv, WID_PREASSO_SLEEP, preasso_sleep);
+       if (ret) {
+               goto out;
+       }
+
+       RDA5890_DBGLAP(RDA5890_DA_WID, RDA5890_DL_TRACE,
+               "Set Preasso Sleep Done\n");
+
+out:
+       return ret;
+}
+
diff --git a/drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.h b/drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.h
new file mode 100755 (executable)
index 0000000..7cf712f
--- /dev/null
@@ -0,0 +1,288 @@
+#ifndef _RDA5890_WID_H_
+#define _RDA5890_WID_H_
+
+#define MAX_STRING_LEN      256
+#define MAX_CMD_LEN         MAX_STRING_LEN
+#define RDA5890_MAX_WID_LEN    MAX_CMD_LEN - 2
+
+#define MAC_CONNECTED          1
+#define MAC_DISCONNECTED       0
+
+#define WID_STATUS_SUCCESS       1
+
+typedef enum
+{
+    G_SHORT_PREAMBLE = 0,
+    G_LONG_PREAMBLE = 1,
+    G_AUTO_PREAMBLE = 2
+}G_PREAMBLE_T;
+
+/* WLAN Identifiers */
+typedef enum {
+                       WID_NIL                            = -1,
+                       WID_BSS_TYPE                       = 0x0000,
+                       WID_CURRENT_TX_RATE                = 0x0001,
+                       WID_CURRENT_CHANNEL                = 0x0002,
+                       WID_PREAMBLE                       = 0x0003,
+                       WID_11G_OPERATING_MODE             = 0x0004,
+                       WID_STATUS                         = 0x0005,
+                       WID_11G_PROT_MECH                  = 0x0006,
+
+#ifdef MAC_HW_UNIT_TEST_MODE
+                       WID_GOTO_SLEEP                     = 0x0007,
+#else /* MAC_HW_UNIT_TEST_MODE */
+                       WID_SCAN_TYPE                      = 0x0007,
+#endif /* MAC_HW_UNIT_TEST_MODE */
+                       WID_PRIVACY_INVOKED                = 0x0008,
+                       WID_KEY_ID                         = 0x0009,
+                       WID_QOS_ENABLE                     = 0x000A,
+                       WID_POWER_MANAGEMENT               = 0x000B,
+                       WID_802_11I_MODE                   = 0x000C,
+                       WID_AUTH_TYPE                      = 0x000D,
+                       WID_SITE_SURVEY                    = 0x000E,
+                       WID_LISTEN_INTERVAL                = 0x000F,
+                       WID_DTIM_PERIOD                    = 0x0010,
+                       WID_ACK_POLICY                     = 0x0011,
+                       WID_RESET                          = 0x0012,
+                       WID_PCF_MODE                       = 0x0013,
+                       WID_CFP_PERIOD                     = 0x0014,
+                       WID_BCAST_SSID                     = 0x0015,
+
+#ifdef MAC_HW_UNIT_TEST_MODE
+                       WID_PHY_TEST_PATTERN               = 0x0016,
+#else /* MAC_HW_UNIT_TEST_MODE */
+                       WID_DISCONNECT                     = 0x0016,
+#endif /* MAC_HW_UNIT_TEST_MODE */
+
+                       WID_READ_ADDR_SDRAM                = 0x0017,
+                       WID_TX_POWER_LEVEL_11A             = 0x0018,
+                       WID_REKEY_POLICY                   = 0x0019,
+                       WID_SHORT_SLOT_ALLOWED             = 0x001A,
+                       WID_PHY_ACTIVE_REG                 = 0x001B,
+                       WID_PHY_ACTIVE_REG_VAL             = 0x001C,
+                       WID_TX_POWER_LEVEL_11B             = 0x001D,
+                       WID_START_SCAN_REQ                 = 0x001E,
+                       WID_RSSI                           = 0x001F,
+                       WID_JOIN_REQ                       = 0x0020,
+                       WID_ANTENNA_SELECTION              = 0x0021,
+                       WID_USER_CONTROL_ON_TX_POWER       = 0x0027,
+                       WID_MEMORY_ACCESS_8BIT             = 0x0029,
+                       WID_UAPSD_SUPPORT_AP               = 0x002A,
+
+                       WID_CURRENT_MAC_STATUS             = 0x0031,
+                       WID_AUTO_RX_SENSITIVITY            = 0x0032,
+                       WID_DATAFLOW_CONTROL               = 0x0033,
+                       WID_SCAN_FILTER                    = 0x0036,
+                       WID_LINK_LOSS_THRESHOLD            = 0x0037,
+                       WID_AUTORATE_TYPE                  = 0x0038,
+                       WID_CCA_THRESHOLD                  = 0x0039,
+
+                       WID_802_11H_DFS_MODE               = 0x003B,
+                       WID_802_11H_TPC_MODE               = 0x003C,
+
+                       WID_PHY_REG_ADDR                   = 0x0040,
+                       WID_PHY_REG_VAL                    = 0x0041,
+                       WID_PTA_MODE                       = 0x0042,
+                       WID_TRAP_TEST                      = 0x0043,
+                       WID_PTA_BLOCK_BT                   = 0x0044,
+                       WID_NETWORK_INFO_EN          = 0x0045,
+
+                       WID_RTS_THRESHOLD                  = 0x1000,
+                       WID_FRAG_THRESHOLD                 = 0x1001,
+                       WID_SHORT_RETRY_LIMIT              = 0x1002,
+                       WID_LONG_RETRY_LIMIT               = 0x1003,
+                       WID_CFP_MAX_DUR                    = 0x1004,
+                       WID_PHY_TEST_FRAME_LEN             = 0x1005,
+                       WID_BEACON_INTERVAL                = 0x1006,
+                       WID_MEMORY_ACCESS_16BIT            = 0x1008,
+
+                       WID_RX_SENSE                       = 0x100B,
+                       WID_ACTIVE_SCAN_TIME               = 0x100C,
+                       WID_PASSIVE_SCAN_TIME              = 0x100D,
+                       WID_SITE_SURVEY_SCAN_TIME          = 0x100E,
+                       WID_JOIN_TIMEOUT                   = 0x100F,
+                       WID_AUTH_TIMEOUT                   = 0x1010,
+                       WID_ASOC_TIMEOUT                   = 0x1011,
+                       WID_11I_PROTOCOL_TIMEOUT           = 0x1012,
+                       WID_EAPOL_RESPONSE_TIMEOUT         = 0x1013,
+                       WID_CCA_BUSY_STATUS                = 0x1014,
+
+                       WID_FAILED_COUNT                   = 0x2000,
+                       WID_RETRY_COUNT                    = 0x2001,
+                       WID_MULTIPLE_RETRY_COUNT           = 0x2002,
+                       WID_FRAME_DUPLICATE_COUNT          = 0x2003,
+                       WID_ACK_FAILURE_COUNT              = 0x2004,
+                       WID_RECEIVED_FRAGMENT_COUNT        = 0x2005,
+                       WID_MULTICAST_RECEIVED_FRAME_COUNT = 0x2006,
+                       WID_FCS_ERROR_COUNT                = 0x2007,
+                       WID_SUCCESS_FRAME_COUNT            = 0x2008,
+                       WID_PHY_TEST_PKT_CNT               = 0x2009,
+                       WID_PHY_TEST_TXD_PKT_CNT           = 0x200A,
+                       WID_TX_FRAGMENT_COUNT              = 0x200B,
+                       WID_TX_MULTICAST_FRAME_COUNT       = 0x200C,
+                       WID_RTS_SUCCESS_COUNT              = 0x200D,
+                       WID_RTS_FAILURE_COUNT              = 0x200E,
+                       WID_WEP_UNDECRYPTABLE_COUNT        = 0x200F,
+                       WID_REKEY_PERIOD                   = 0x2010,
+                       WID_REKEY_PACKET_COUNT             = 0x2011,
+#ifdef MAC_HW_UNIT_TEST_MODE
+                       WID_Q_ENABLE_INFO                  = 0x2012,
+#else /* MAC_HW_UNIT_TEST_MODE */
+                       WID_802_1X_SERV_ADDR               = 0x2012,
+#endif /* MAC_HW_UNIT_TEST_MODE */
+                       WID_STACK_IP_ADDR                  = 0x2013,
+                       WID_STACK_NETMASK_ADDR             = 0x2014,
+                       WID_HW_RX_COUNT                    = 0x2015,
+                       WID_MEMORY_ADDRESS                 = 0x201E,
+                       WID_MEMORY_ACCESS_32BIT            = 0x201F,
+                       WID_RF_REG_VAL                     = 0x2021,
+                       WID_FIRMWARE_INFO                  = 0x2023,
+
+                       WID_SYS_FW_VER                     = 0x2801,
+                       WID_SYS_DBG_LVL                    = 0x2802,
+                       WID_SYS_DBG_AREA                   = 0x2803,
+                       WID_UT_MODE                        = 0x2804,
+                       WID_UT_TX_LEN                      = 0x2805,
+                       WID_PTA_CTS_FRAME_LEN              = 0x2806,
+                       WID_PREASSO_SLEEP                  = 0x2807,
+
+                       WID_SSID                           = 0x3000,
+                       WID_FIRMWARE_VERSION               = 0x3001,
+                       WID_OPERATIONAL_RATE_SET           = 0x3002,
+                       WID_BSSID                          = 0x3003,
+                       WID_WEP_KEY_VALUE0                 = 0x3004,
+                       WID_WEP_KEY_VALUE1                 = 0x3005,
+                       WID_WEP_KEY_VALUE2                 = 0x3006,
+                       WID_WEP_KEY_VALUE3                 = 0x3007,
+                       WID_802_11I_PSK                    = 0x3008,
+                       WID_HCCA_ACTION_REQ                = 0x3009,
+                       WID_802_1X_KEY                     = 0x300A,
+                       WID_HARDWARE_VERSION               = 0x300B,
+                       WID_MAC_ADDR                       = 0x300C,
+                       WID_PHY_TEST_DEST_ADDR             = 0x300D,
+                       WID_PHY_TEST_STATS                 = 0x300E,
+                       WID_PHY_VERSION                    = 0x300F,
+                       WID_SUPP_USERNAME                  = 0x3010,
+                       WID_SUPP_PASSWORD                  = 0x3011,
+                       WID_SITE_SURVEY_RESULTS            = 0x3012,
+                       WID_RX_POWER_LEVEL                 = 0x3013,
+
+                       WID_ADD_WEP_KEY                    = 0x3019,
+                       WID_REMOVE_WEP_KEY                 = 0x301A,
+                       WID_ADD_PTK                        = 0x301B,
+                       WID_ADD_RX_GTK                     = 0x301C,
+                       WID_ADD_TX_GTK                     = 0x301D,
+                       WID_REMOVE_KEY                     = 0x301E,
+                       WID_ASSOC_REQ_INFO                 = 0x301F,
+                       WID_ASSOC_RES_INFO                 = 0x3020,
+                       WID_UPDATE_RF_SUPPORTED_INFO       = 0x3021,
+                       WID_COUNTRY_IE                     = 0x3022,
+
+                       WID_WAPI_ASSOC_IE                  = 0x3023,
+                       WID_ADD_WAPI_PTK                   = 0x3024,
+                       WID_ADD_WAPI_RX_GTK                = 0x3025,
+                       WID_ADD_WAPI_TX_GTK                = 0x3026,
+
+                       WID_CONFIG_HCCA_ACTION_REQ         = 0x4000,
+                       WID_UAPSD_CONFIG                   = 0x4001,
+                       WID_UAPSD_STATUS                   = 0x4002,
+                       WID_WMM_AP_AC_PARAMS               = 0x4003,
+                       WID_WMM_STA_AC_PARAMS              = 0x4004,
+                       WID_NEWORK_INFO                    = 0x4005,
+                       WID_STA_JOIN_INFO                  = 0x4006,
+                       WID_CONNECTED_STA_LIST             = 0x4007,
+                       WID_HUT_STATS                      = 0x4082,
+                       WID_STATISTICS                     = 0x4008,
+                       WID_MEMORY_DUMP                    = 0x4009,
+                       WID_LOAD_TRAP_MAP                  = 0x400a,
+                       WID_AGC_DGC_TBL                    = 0x400b,
+                       WID_ALL                            = 0x7FFE,
+                       WID_MAX                            = 0xFFFF
+} WID_T;
+
+int rda5890_wid_request(struct rda5890_private *priv, 
+               char *wid_req, unsigned short wid_req_len,
+               char *wid_rsp, unsigned short *wid_rsp_len);
+
+void rda5890_wid_response(struct rda5890_private *priv, 
+               char *wid_rsp, unsigned short wid_rsp_len);
+
+void rda5890_wid_status(struct rda5890_private *priv, 
+               char *wid_status, unsigned short wid_status_len);
+
+
+void rda5890_card_to_host(struct rda5890_private *priv, 
+               char *packet, unsigned short packet_len);
+
+int rda5890_start_scan(struct rda5890_private *priv);
+int rda5890_start_scan_enable_network_info(struct rda5890_private *priv);
+int rda5890_start_join(struct rda5890_private *priv);
+
+int rda5890_get_fw_ver(struct rda5890_private *priv, unsigned long *fw_ver);
+int rda5890_get_mac_addr(struct rda5890_private *priv, unsigned char *mac_addr);
+int rda5890_get_scan_results(struct rda5890_private *priv, 
+               struct rda5890_bss_descriptor *bss_desc);
+int rda5890_get_bssid(struct rda5890_private *priv, unsigned char *bssid);
+int rda5890_set_bssid(struct rda5890_private *priv, unsigned char *bssid);
+
+int rda5890_get_channel(struct rda5890_private *priv, unsigned char *channel);
+int rda5890_get_rssi(struct rda5890_private *priv, unsigned char *rssi);
+
+int rda5890_set_mac_addr(struct rda5890_private *priv, unsigned char *mac_addr);
+int rda5890_set_preamble(struct rda5890_private *priv, unsigned char  preamble);
+
+int rda5890_set_ssid(struct rda5890_private *priv, 
+               unsigned char *ssid, unsigned char ssid_len);
+int rda5890_get_ssid(struct rda5890_private *priv, 
+               unsigned char *ssid, unsigned char *ssid_len);
+int rda5890_set_imode(struct rda5890_private *priv, unsigned char imode);
+int rda5890_set_authtype(struct rda5890_private *priv, unsigned char authtype);
+int rda5890_set_listen_interval(struct rda5890_private *priv, unsigned char interval);
+int rda5890_set_link_loss_threshold(struct rda5890_private *priv, unsigned char threshold);
+int rda5890_set_wepkey(struct rda5890_private *priv, 
+               unsigned short index, unsigned char *key, unsigned char key_len);
+int rda5890_set_ptk(struct rda5890_private *priv, 
+               unsigned char *key, unsigned char key_len);
+int rda5890_set_gtk(struct rda5890_private *priv, unsigned char key_id,
+               unsigned char *key_rsc, unsigned char key_rsc_len,
+               unsigned char *key, unsigned char key_len);
+
+int rda5890_set_pm_mode(struct rda5890_private *priv, unsigned char pm_mode);
+int rda5890_set_preasso_sleep(struct rda5890_private *priv, unsigned int preasso_sleep);
+
+int rda5890_generic_get_str(struct rda5890_private *priv, 
+               unsigned short wid, unsigned char *val, unsigned char len);
+int rda5890_generic_set_str(struct rda5890_private *priv, 
+               unsigned short wid, unsigned char *val, unsigned char len);
+
+int rda5890_generic_set_uchar(struct rda5890_private *priv,
+                unsigned short wid, unsigned char val);
+int rda5890_generic_get_ushort(struct rda5890_private *priv, 
+               unsigned short wid, unsigned short *val);
+int rda5890_generic_set_ushort(struct rda5890_private *priv, 
+               unsigned short wid, unsigned short val);
+
+int rda5890_generic_set_ulong(struct rda5890_private *priv,
+                unsigned short wid, unsigned long val);
+int rda5890_generic_get_ulong(struct rda5890_private *priv,
+                unsigned short wid, unsigned long *val);
+
+int rda5890_set_core_init(struct rda5890_private *priv, const unsigned int (*data)[2], unsigned char num);
+int rda5890_set_core_patch(struct rda5890_private *priv, const unsigned char (*patch)[2], unsigned char num);
+int rda5890_set_core_init_polling(struct rda5890_private *priv, const unsigned int (*data)[2], unsigned char num);
+int rda5890_set_core_patch_polling(struct rda5890_private *priv, const unsigned char (*patch)[2], unsigned char num);
+int rda5890_set_scan_timeout(struct rda5890_private *priv);
+
+int rda5890_set_txrate(struct rda5890_private *priv, unsigned char mbps);
+#ifdef GET_SCAN_FROM_NETWORK_INFO
+
+int rda5890_set_scan_complete(struct rda5890_private *priv);
+#endif
+int rda5890_wid_request_polling(struct rda5890_private *priv, 
+               char *wid_req, unsigned short wid_req_len,
+               char *wid_rsp, unsigned short *wid_rsp_len);
+int rda5890_check_wid_response(char *wid_rsp, unsigned short wid_rsp_len,
+               unsigned short wid, char wid_msg_id, 
+               char payload_len, char **ptr_payload);
+#endif
+