From 97e508f4ea7a1c0d1610cd8e2b8c385bc09a7071 Mon Sep 17 00:00:00 2001 From: linjh Date: Thu, 27 Sep 2012 17:39:53 +0800 Subject: [PATCH] phonepad: commit phonepad(i30) rda code [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 --- drivers/net/wireless/Kconfig | 10 +- drivers/net/wireless/Makefile | 1 + drivers/net/wireless/rda5990/Makefile | 5 + .../net/wireless/rda5990/drv_fm_rda/Makefile | 49 + .../rda5990/drv_fm_rda/RDA5990_FM_drv.c | 2142 ++++++++++++++ .../rda5990/drv_fm_rda/RDA5990_FM_drv_gpio.c | 1784 ++++++++++++ .../rda5990/rda_5990_power_ctrl/Makefile | 3 + .../rda_5990_power_ctrl/rda_5990_power_ctrl.c | 1895 ++++++++++++ .../rda_5990_power_ctrl_by_gpio.c | 1462 ++++++++++ .../wireless/rda5990/rda_gpio_i2c/Makefile | 39 + .../rda5990/rda_gpio_i2c/rda_gpio_i2c.c | 460 +++ .../net/wireless/rda5990/rda_wlan/Makefile | 20 + .../wireless/rda5990/rda_wlan/Makefile_gwl | 23 + .../rda5990/rda_wlan/rda5890_debugfs.c | 593 ++++ .../rda5990/rda_wlan/rda5890_debugfs.h | 13 + .../wireless/rda5990/rda_wlan/rda5890_defs.h | 71 + .../wireless/rda5990/rda_wlan/rda5890_dev.c | 633 ++++ .../wireless/rda5990/rda_wlan/rda5890_dev.h | 272 ++ .../rda5990/rda_wlan/rda5890_if_sdio.c | 795 +++++ .../rda5990/rda_wlan/rda5890_if_sdio.h | 49 + .../wireless/rda5990/rda_wlan/rda5890_ioctl.h | 17 + .../wireless/rda5990/rda_wlan/rda5890_nvram.c | 108 + .../wireless/rda5990/rda_wlan/rda5890_scan.c | 369 +++ .../rda5990/rda_wlan/rda5890_sdio_patch.c | 1406 +++++++++ .../wireless/rda5990/rda_wlan/rda5890_txrx.c | 139 + .../wireless/rda5990/rda_wlan/rda5890_txrx.h | 13 + .../wireless/rda5990/rda_wlan/rda5890_wext.c | 2562 +++++++++++++++++ .../wireless/rda5990/rda_wlan/rda5890_wext.h | 37 + .../wireless/rda5990/rda_wlan/rda5890_wid.c | 1957 +++++++++++++ .../wireless/rda5990/rda_wlan/rda5890_wid.h | 288 ++ 30 files changed, 17214 insertions(+), 1 deletion(-) create mode 100755 drivers/net/wireless/rda5990/Makefile create mode 100755 drivers/net/wireless/rda5990/drv_fm_rda/Makefile create mode 100755 drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv.c create mode 100755 drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv_gpio.c create mode 100755 drivers/net/wireless/rda5990/rda_5990_power_ctrl/Makefile create mode 100755 drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl.c create mode 100755 drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl_by_gpio.c create mode 100755 drivers/net/wireless/rda5990/rda_gpio_i2c/Makefile create mode 100755 drivers/net/wireless/rda5990/rda_gpio_i2c/rda_gpio_i2c.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/Makefile create mode 100755 drivers/net/wireless/rda5990/rda_wlan/Makefile_gwl create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.h create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_defs.h create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.h create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.h create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_ioctl.h create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_nvram.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_scan.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_sdio_patch.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.h create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.h create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.c create mode 100755 drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.h diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 792268c67c2e..2acbb3f3e75b 100755 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -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 diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 669b28b90009..08143cc16a8b 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -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 index 000000000000..0b0494d04ae2 --- /dev/null +++ b/drivers/net/wireless/rda5990/Makefile @@ -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 index 000000000000..4eaffa464f77 --- /dev/null +++ b/drivers/net/wireless/rda5990/drv_fm_rda/Makefile @@ -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 index 000000000000..cfd9f1b7846b --- /dev/null +++ b/drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv.c @@ -0,0 +1,2142 @@ +#include +#include +#include +#include // udelay() +#include // device_create() +#include +#include +#include +#include +#include /* constant of kernel version */ +#include // get_user() + +#include +#include +#include +#include +#include +#include + +// 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 "); + + 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 index 000000000000..b154fbcaa5cd --- /dev/null +++ b/drivers/net/wireless/rda5990/drv_fm_rda/RDA5990_FM_drv_gpio.c @@ -0,0 +1,1784 @@ +#include +#include +#include +#include // udelay() +#include // device_create() +#include +#include +#include +#include +#include /* constant of kernel version */ +#include // get_user() + +#include +#include +#include +#include +#include +#include + +// 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 "); 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 index 000000000000..ab2ac900edd6 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_5990_power_ctrl/Makefile @@ -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 index 000000000000..250d032b94ae --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl.c @@ -0,0 +1,1895 @@ +/* ----------------------------------------------------------------------- * + * + This file created by albert RDA Inc + */ + +#include +#include +#include +#include +#include +#include /* get the user-level API */ +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define RDA5890_USE_CRYSTAL // if use share crystal should close this +#define RDA5990_USE_DCDC + +#define u32 unsigned int +#define u8 unsigned char +#define u16 unsigned short + +#define RDA_I2C_CHANNEL (0) +#define RDA_WIFI_CORE_ADDR (0x13 << 1) +#define RDA_WIFI_RF_ADDR (0x14 << 1) //correct add is 0x14 +#define RDA_BT_CORE_ADDR (0x15 << 1) +#define RDA_BT_RF_ADDR (0x16 << 1) + +#define I2C_MASTER_ACK (1<<0) +#define I2C_MASTER_RD (1<<4) +#define I2C_MASTER_STO (1<<8) +#define I2C_MASTER_WR (1<<12) +#define I2C_MASTER_STA (1<<16) + +#define RDA_WIFI_RF_I2C_DEVNAME "rda_wifi_rf_i2c" +#define RDA_WIFI_CORE_I2C_DEVNAME "rda_wifi_core_i2c" +#define RDA_BT_RF_I2C_DEVNAME "rda_bt_rf_i2c" +#define RDA_BT_CORE_I2C_DEVNAME "rda_bt_core_i2c" + +static struct mutex i2c_rw_lock; +//extern int +//i2c_register_board_info(int busnum, struct i2c_board_info const *info, +// unsigned n); + + +static unsigned short wlan_version = 0; +static struct wake_lock rda_5990_wake_lock; +static struct delayed_work rda_5990_sleep_worker; +static struct i2c_client * rda_wifi_core_client = NULL; +static struct i2c_client * rda_wifi_rf_client = NULL; +static struct i2c_client * rda_bt_core_client = NULL; +static struct i2c_client * rda_bt_rf_client = NULL; + +static const struct i2c_device_id wifi_rf_i2c_id[] = {{RDA_WIFI_RF_I2C_DEVNAME, 0}, {}}; +MODULE_DEVICE_TABLE(i2c, wifi_rf_i2c_id); + +static unsigned short wifi_rf_force[] = { RDA_I2C_CHANNEL, RDA_WIFI_RF_ADDR, I2C_CLIENT_END, I2C_CLIENT_END }; + +static struct i2c_board_info __initdata i2c_rda_wifi_rf={ I2C_BOARD_INFO(RDA_WIFI_RF_I2C_DEVNAME, (RDA_WIFI_RF_ADDR>>1))}; +static struct i2c_driver rda_wifi_rf_driver; + + + +static const struct i2c_device_id wifi_core_i2c_id[] = {{RDA_WIFI_CORE_I2C_DEVNAME, 0}, {}}; +MODULE_DEVICE_TABLE(i2c, wifi_core_i2c_id); + +static unsigned short wifi_core_force[] = { RDA_I2C_CHANNEL, RDA_WIFI_CORE_ADDR, I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_board_info __initdata i2c_rda_wifi_core={ I2C_BOARD_INFO(RDA_WIFI_CORE_I2C_DEVNAME, (RDA_WIFI_CORE_ADDR>>1))}; +static struct i2c_driver rda_wifi_core_driver; + + + +static const struct i2c_device_id bt_rf_i2c_id[] = {{RDA_BT_RF_I2C_DEVNAME, 0}, {}}; + +MODULE_DEVICE_TABLE(i2c, bt_rf_i2c_id); + +static unsigned short bt_rf_force[] = { RDA_I2C_CHANNEL, RDA_BT_RF_ADDR, I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_board_info __initdata i2c_rda_bt_rf={ I2C_BOARD_INFO(RDA_BT_RF_I2C_DEVNAME, (RDA_BT_RF_ADDR>>1))}; +static struct i2c_driver rda_bt_rf_driver; + +static const struct i2c_device_id bt_core_i2c_id[] = {{RDA_BT_CORE_I2C_DEVNAME, 0}, {}}; +MODULE_DEVICE_TABLE(i2c, bt_core_i2c_id); + +static unsigned short bt_core_force[] = { RDA_I2C_CHANNEL, RDA_BT_CORE_ADDR, I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_board_info __initdata i2c_rda_bt_core={ I2C_BOARD_INFO(RDA_BT_CORE_I2C_DEVNAME, (RDA_BT_CORE_ADDR>>1))}; +static struct i2c_driver rda_bt_core_driver; + + +static u8 isBigEnded = 0; +static u8 wifi_in_test_mode = 0; + +static int i2c_write_data_4_addr_4_data(struct i2c_client* client, const u32 addr, const u32 data) +{ + unsigned char ADDR[4], DATA[4]; + int ret = 0; + + if(!isBigEnded) + { + ADDR[0] = addr >> 24; + ADDR[1] = addr >> 16; + ADDR[2] = addr >> 8; + ADDR[3] = addr >> 0; + + DATA[0] = data >> 24; + DATA[1] = data >> 16; + DATA[2] = data >> 8; + DATA[3] = data >> 0; + } + else + { + memcpy(ADDR, &addr, sizeof(u32)); + memcpy(DATA, &data, sizeof(u32)); + } + + ret = i2c_master_send(client, (char*)ADDR, 4); + if (ret < 0) + { + printk(KERN_INFO "***i2c_write_data_4_addr_4_data send:0x%X err:%d\n", addr,ret); + return -1; + } + + ret = i2c_master_send(client, (char*)DATA, 4); + if (ret < 0) + { + printk(KERN_INFO "***i2c_write_data_4_addr_4_data send:0x%X err:%d\n", data,ret); + return -1; + } + + return 0; +} + + +static int i2c_read_4_addr_4_data(struct i2c_client* client, const u32 addr, u32* data) +{ + unsigned char ADDR[4], DATA[4]; + int ret = 0; + + if(!isBigEnded) + { + ADDR[0] = addr >> 24; + ADDR[1] = addr >> 16; + ADDR[2] = addr >> 8; + ADDR[3] = addr >> 0; + } + else + { + memcpy(ADDR, &addr, sizeof(u32)); + } + + ret = i2c_master_send(client, (char*)ADDR, 4); + if (ret < 0) + { + printk(KERN_INFO "***i2c_read_4_addr_4_data send:0x%X err:%d\n", addr,ret); + return -1; + } + + ret = i2c_master_recv(client, (char*)DATA, 4); + if (ret < 0) + { + printk(KERN_INFO "***i2c_read_4_addr_4_data send:0x%X err:%d\n", addr,ret); + return -1; + } + + if(!isBigEnded) + { + data[3] = DATA[0]; + data[2] = DATA[1]; + data[1] = DATA[2]; + data[0] = DATA[3]; + } + else + memcpy(data, DATA, sizeof(u32)); + + return 0; +} + + +static int i2c_write_1_addr_2_data(struct i2c_client* client, const u8 addr, const u16 data) +{ + unsigned char DATA[3]; + int ret = 0; + + if(!isBigEnded) + { + DATA[0] = addr; + DATA[1] = data >> 8; + DATA[2] = data >> 0; + } + else + { + DATA[0] = addr; + DATA[1] = data >> 0; + DATA[2] = data >> 8; + } + + ret = i2c_master_send(client, (char*)DATA, 3); + if (ret < 0) + { + printk(KERN_INFO "***i2c_write_1_addr_2_data send:0x%X err:%d bigendia: %d \n", addr,ret, isBigEnded); + return -1; + } + + return 0; +} + + +static int i2c_read_1_addr_2_data(struct i2c_client* client, const u8 addr, u16* data) +{ + unsigned char DATA[2]; + int ret = 0; + + ret = i2c_master_send(client, (char*)&addr, 1); + if (ret < 0) + { + printk(KERN_INFO "***i2c_read_1_addr_2_data send:0x%X err:%d\n", addr,ret); + return -1; + } + + ret = i2c_master_recv(client, DATA, 2); + if (ret < 0) + { + printk(KERN_INFO "***i2c_read_1_addr_2_data send:0x%X err:%d\n", addr,ret); + return -1; + } + + if(!isBigEnded) + { + *data = (DATA[0] << 8) | DATA[1]; + } + else + { + *data = (DATA[1] << 8) | DATA[0]; + } + return 0; +} + + +static int rda_wifi_rf_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int result = 0; + + rda_wifi_rf_client = client; + return result; +} + +static int rda_wifi_rf_remove(struct i2c_client *client) +{ + return 0; +} + +static int rda_wifi_rf_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) +{ + strcpy(info->type, RDA_WIFI_RF_I2C_DEVNAME); + return 0; +} + +static struct i2c_driver rda_wifi_rf_driver = { + .probe = rda_wifi_rf_probe, + .remove = rda_wifi_rf_remove, + .detect = rda_wifi_rf_detect, + .driver.name = RDA_WIFI_RF_I2C_DEVNAME, + .id_table = wifi_rf_i2c_id, +// .address_data = &rda_wifi_rf_addr_data, + //.address_list = (const unsigned short*) wifi_rf_force, +}; + +static int rda_wifi_core_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) +{ + strcpy(info->type, RDA_WIFI_CORE_I2C_DEVNAME); + return 0; +} + +static int rda_wifi_core_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int result = 0; + + rda_wifi_core_client = client; + return result; +} + +static int rda_wifi_core_remove(struct i2c_client *client) +{ + return 0; +} + +int rda_wifi_power_off(void); +static void rda_wifi_shutdown(struct i2c_client * client) +{ + printk("rda_wifi_shutdown \n"); + rda_wifi_power_off(); +} + +static struct i2c_driver rda_wifi_core_driver = +{ + .probe = rda_wifi_core_probe, + .remove = rda_wifi_core_remove, + .detect = rda_wifi_core_detect, + .shutdown = rda_wifi_shutdown, + .driver.name = RDA_WIFI_CORE_I2C_DEVNAME, + .id_table = wifi_core_i2c_id, + //.address_data = &rda_wifi_core_addr_data, + //.address_list = (const unsigned short*)wifi_core_force, +}; + +const u32 wifi_core_init_data[][2] = +{ + +}; + +u16 wifi_off_data[][2] = +{ + { 0x3F, 0x0001 }, //page up + { 0x31, 0x0B40 }, //power off wifi + { 0x3F, 0x0000 }, //page down +}; + +u16 wifi_en_data[][2] = +{ + //item:VerD_wf_on_2012_02_08 + {0x3f, 0x0001}, +#ifdef RDA5990_USE_DCDC /*houzhen update Mar 15 2012 */ + {0x23, 0x8FA1},//20111001 higher AVDD voltage to improve EVM to 0x8f21 download current -1db 0x8fA1>>0x8bA1 +#else + {0x23, 0x0FA1}, +#endif + {0x31, 0x0B40 }, //power off wifi +// {0x22, 0xD3C7},//for ver.c 20111109, txswitch + {0x24, 0x80C8},//freq_osc_in[1:0]00 0x80C8 >> 0x80CB + {0x27, 0x4925},//for ver.c20111109, txswitch + // {0x28, 0x80A1}, //BT_enable + {0x31, 0x8140},//enable wifi + {0x32, 0x0113},//set_ rdenout_ldooff_wf=0; rden4in_ldoon_wf=1 + // {0x39, 0x0004}, //uart switch to wf + {0x3F, 0x0000}, //page down +}; + + +u16 wifi_dc_cal_data[][2]= +{ + {0x3f, 0x0000}, + {0x30, 0x0248}, + {0x30, 0x0249}, + //{wait 200ms; } here +}; + +u16 wifi_dig_reset_data[][2]= +{ + {0x3F, 0x0001}, + {0x31, 0x8D40}, + {0x31, 0x8F40}, + {0x31, 0x8b40}, + {0x3F, 0x0000}, +}; + +u16 wifi_rf_init_data_verE[][2] = +{ + {0x3f, 0x0000}, + //{;;set_rf_swi},ch + {0x06, 0x0101}, + {0x07, 0x0101}, + {0x08, 0x0101}, + {0x09, 0x0101}, + {0x0A, 0x002C},//aain_0 + {0x0D, 0x0507}, + {0x0E, 0x2300}, + {0x0F, 0x5689},// + //{;;//set_RF }, + {0x10, 0x0f78},//20110824 + {0x11, 0x0602}, + {0x13, 0x0652},//adc_tuning_bit[011] + {0x14, 0x8886}, + {0x15, 0x0990}, + {0x16, 0x049f}, + {0x17, 0x0990}, + {0x18, 0x049F}, + {0x19, 0x3C01}, + {0x1C, 0x0934}, + {0x1D, 0xFF00},//for ver.D20120119for temperature 70 degree + //{0x1F, 0x01F8},//for ver.c20111109 + //{0x1F, 0x0300},//for burst tx 不锁 + {0x20, 0x06E4}, + {0x21, 0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset + {0x22, 0x24DC}, + {0x23, 0x23FF}, + {0x24, 0x00FC}, + {0x26, 0x004F},//004F >> 005f premote pa + {0x27, 0x171D},///mdll*7 + {0x28, 0x031D},///mdll*7 + {0x2A, 0x2860},//et0x2849-8.5p :yd 0x2861-7pf C1,C2=6.8p + {0x2B, 0x0800},//bbpll,or ver.c20111116 + {0x32, 0x8a08}, + {0x33, 0x1D02},//liuyanan + //{;;//agc_gain}, + {0x36, 0x02f4}, //00F8;//gain_7 + {0x37, 0x01f4}, //0074;//aain_6 + {0x38, 0x21d4}, //0014;//gain_5 + {0x39, 0x25d4}, //0414;//aain_4 + {0x3A, 0x2584}, //1804;//gain_3 + {0x3B, 0x2dc4}, //1C04;//aain_2 + {0x3C, 0x2d04}, //1C02;//gain_1 + {0x3D, 0x2c02}, //3C01;//gain_0 + {0x33, 0x1502},//liuyanan + //{;;SET_channe},_to_11 + {0x1B, 0x0001},//set_channel + {0x30, 0x024D}, + {0x29, 0xD468}, + {0x29, 0x1468}, + {0x30, 0x0249}, + {0x3f, 0x0000}, +}; + +u16 wifi_rf_init_data[][2] = +{ + {0x3f, 0x0000}, + //{;;set_rf_swi},ch + {0x06, 0x0101}, + {0x07, 0x0101}, + {0x08, 0x0101}, + {0x09, 0x0101}, + {0x0A, 0x002C},//aain_0 + {0x0D, 0x0507}, + {0x0E, 0x2300},//2012_02_20 + {0x0F, 0x5689},// + //{;;//set_RF }, + {0x10, 0x0f78},//20110824 + {0x11, 0x0602}, + {0x13, 0x0652},//adc_tuning_bit[011] + {0x14, 0x8886}, + {0x15, 0x0990}, + {0x16, 0x049f}, + {0x17, 0x0990}, + {0x18, 0x049F}, + {0x19, 0x3C01},//sdm_vbit[3:0]=1111 + {0x1C, 0x0934}, + {0x1D, 0xFF00},//for ver.D20120119for temperature 70 degree 0xCE00 >> 0xFF00 + {0x1F, 0x0300},//div2_band_48g_dr=1;div2_band_48g_reg[8:0] + {0x20, 0x06E4}, + {0x21, 0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset + {0x22, 0x24DC}, + {0x23, 0x23FF}, + {0x24, 0x00FC}, + {0x26, 0x004F},//004F >> 005f premote pa + {0x27, 0x171D},///mdll*7 + {0x28, 0x031D},///mdll*7 + {0x2A, 0x2860},//et0x2849-8.5p :yd 0x2861-7pf + {0x2B, 0x0800},//bbpll,or ver.c20111116 + {0x32, 0x8a08}, + {0x33, 0x1D02},//liuyanan + //{;;//agc_gain}, + {0x36, 0x02f4}, //00F8;//gain_7 + {0x37, 0x01f4}, //0074;//aain_6 + {0x38, 0x21d4}, //0014;//gain_5 + {0x39, 0x25d4}, //0414;//aain_4 + {0x3A, 0x2584}, //1804;//gain_3 + {0x3B, 0x2dc4}, //1C04;//aain_2 + {0x3C, 0x2d04}, //1C02;//gain_1 + {0x3D, 0x2c02}, //3C01;//gain_0 + {0x33, 0x1502},//liuyanan + //{;;SET_channe},_to_11 + {0x1B, 0x0001},//set_channel + {0x30, 0x024D}, + {0x29, 0xD468}, + {0x29, 0x1468}, + {0x30, 0x0249}, + {0x3f, 0x0000}, +}; +u16 wifi_uart_debug_data[][2] = +{ + {0x3F,0x0001}, + {0x28,0x80A1}, //BT_enable + {0x39,0x0004}, //uart switch to wf + {0x3f,0x0000}, +}; + +u16 wifi_tm_en_data[][2] = +{ + {0x3F,0x0001}, +#ifdef RDA5990_USE_DCDC /*houzhen update Mar 15 2012 */ + {0x23, 0x8FA1},//20111001 higher AVDD voltage to improve EVM to 0x8f21 download current -1db 0x8fA1>>0x8bA1 +#else + {0x23, 0x0FA1}, +#endif + {0x22,0xD3C7},//for ver.c 20111109, tx + {0x24, 0x80C8},//freq_osc_in[1:0]00 0x80C8 >> 0x80CB + {0x27,0x4925},//for ver.c20111109, txs + {0x28,0x80A1}, //BT_enable + {0x29,0x111F}, + {0x31,0x8140}, + {0x32,0x0113},//set_ rdenout_ldooff_wf + {0x39,0x0004},//uart switch to wf + {0x3f,0x0000}, +}; + +u16 wifi_tm_rf_init_data[][2] = +{ + {0x3f, 0x0000}, + //set_rf_switch + {0x06,0x0101}, + {0x07,0x0101}, + {0x08,0x0101}, + {0x09,0x0101}, + {0x0A,0x002C},//aain_0 + {0x0D, 0x0507}, + {0x0E,0x2300},//2012_02_20 + {0x0F,0x5689},// + //set_RF + {0x10,0x0f78},//20110824 + {0x11,0x0602}, + {0x13,0x0652},//adc_tuning_bit[011] + {0x14,0x8886}, + {0x15,0x0990}, + {0x16,0x049f}, + {0x17,0x0990}, + {0x18,0x049F}, + {0x19,0x3C01},//sdm_vbit[3:0]=1111 + {0x1C,0x0934}, + {0x1D,0xFF00},//for ver.D20120119for temperature 70 degree + {0x1F,0x0300},//div2_band_48g_dr=1;div2_band_48g_reg[8:0]1000000000 + {0x20,0x06E4}, + {0x21,0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset + {0x22,0x24DC}, + {0x23,0x23FF}, + {0x24,0x00FC}, + {0x26,0x004F}, + {0x27,0x171D},///mdll*7 + {0x28,0x031D},///mdll*7 + {0x2A,0x2860}, + {0x2B,0x0800},//bbpll,or ver.c20111116 + {0x32,0x8a08}, + {0x33,0x1D02},//liuyanan + //agc_gain + {0x36,0x02f4}, //00F8;//gain_7 + {0x37,0x01f4}, //0074;//aain_6 + {0x38,0x21d4}, //0014;//gain_5 + {0x39,0x25d4}, //0414;//aain_4 + {0x3A,0x2584}, //1804;//gain_3 + {0x3B,0x2dc4}, //1C04;//aain_2 + {0x3C,0x2d04}, //1C02;//gain_1 + {0x3D,0x2c02}, //3C01;//gain_0 + //DC_CAL + {0x30,0x0248}, + {0x30,0x0249}, + //wait 200ms; + {0x33,0x1502},//liuyanan + //SET_channel_to_11 + {0x1B,0x0001},//set_channel + {0x3f,0x0000}, +}; + +/*houzhen update Mar 15 2012 + should be called when power up/down bt + */ +static int rda5990_wf_setup_A2_power(int enable) +{ + int ret; + u16 temp_data=0; + printk(KERN_INFO "***rda5990_wf_setup_A2_power start! \n"); + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001); + if(ret) + goto err; + + if(enable) + { + ret=i2c_read_1_addr_2_data(rda_wifi_rf_client,0x22,&temp_data); + if(ret) + goto err; + printk(KERN_INFO "***0xA2 readback value:0x%X \n", temp_data); + + temp_data |=0x0200; /*en reg4_pa bit*/ +#ifdef RDA5890_USE_CRYSTAL + temp_data &= ~(1 << 14); //disable xen_out +#endif + ret=i2c_write_1_addr_2_data(rda_wifi_rf_client,0x22,temp_data); + if(ret) + goto err; + //read wlan version + ret = i2c_read_1_addr_2_data(rda_wifi_rf_client,0x21,&temp_data); + if(ret) + goto err; + else + wlan_version = temp_data; + } + else + { + ret=i2c_read_1_addr_2_data(rda_wifi_rf_client,0x28,&temp_data); + if(ret) + goto err; + if(temp_data&0x8000) // bt is on + { + goto out; + } + else + { + ret=i2c_read_1_addr_2_data(rda_wifi_rf_client,0x22,&temp_data); + if(ret) + goto err; + temp_data&=0xfdff; + + ret=i2c_write_1_addr_2_data(rda_wifi_rf_client,0x22,temp_data); + if(ret) + goto err; + } + wlan_version = 0; + } + +out: + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000); + if(ret) + goto err; + printk(KERN_INFO "***rda5990_wf_setup_A2_power succeed! \n"); + return 0; + +err: + printk(KERN_INFO "***rda5990_wf_setup_A2_power failed! \n"); + return -1; +} + + +int rda_wifi_rf_init(void) +{ + unsigned int count = 0; + int ret = 0; + + mutex_lock(&i2c_rw_lock); + if( (wlan_version&0x1f) == 7) + { + for(count = 0; count < sizeof(wifi_rf_init_data)/sizeof(wifi_rf_init_data[0]); count ++) + { + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_rf_init_data[count][0], wifi_rf_init_data[count][1]); + if(ret) + goto err; + } + } + else if((wlan_version&0x1f) == 4 || (wlan_version&0x1f)==5) + { + for(count = 0; count < sizeof(wifi_rf_init_data_verE)/sizeof(wifi_rf_init_data_verE[0]); count ++) + { + 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]); + if(ret) + goto err; + } + } + else + { + printk("unknown version of this 5990 chip\n"); + goto err; + } + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_rf_init_succceed \n"); + msleep(5); //5ms delay + return 0; +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_rf_init failed! \n"); + return -1; +} + +int rda_wifi_dc_cal(void) +{ + unsigned int count = 0; + int ret = 0; + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(wifi_dc_cal_data)/sizeof(wifi_dc_cal_data[0]); count ++) + { + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client ,wifi_dc_cal_data[count][0], wifi_dc_cal_data[count][1]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_rf_dc_calsuccceed \n"); + msleep(50); //50ms delay + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_rf_dc_calf_failed! \n"); + return -1; +} + +int rda_wifi_en(void) +{ + unsigned int count = 0; + int ret = 0; + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(wifi_en_data)/sizeof(wifi_en_data[0]); count ++) + { + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_en_data[count][0], wifi_en_data[count][1]); + if(ret) + goto err; + if(wifi_en_data[count][0] == 0x31) + msleep(10); //10ms delay + } + + ret=rda5990_wf_setup_A2_power(1); //en pa_reg for wf + if(ret) + goto err; + + mutex_unlock(&i2c_rw_lock); + msleep(8); //8ms delay + + printk(KERN_INFO "***rda_wifi_en_succceed \n"); + return 0; +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_power_on failed! \n"); + return -1; +} + +int rda_wifi_debug_en(void) +{ + unsigned int count = 0; + int ret = 0; + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(wifi_uart_debug_data)/sizeof(wifi_uart_debug_data[0]); count ++) + { + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_uart_debug_data[count][0], wifi_uart_debug_data[count][1]); + if(ret) + goto err; + } + +err: + mutex_unlock(&i2c_rw_lock); + return ret; +} + +int rda_tm_wifi_en(void) +{ + unsigned int count = 0; + int ret = 0; + unsigned short temp_data; + + for(count = 0; count < sizeof(wifi_tm_en_data)/sizeof(wifi_tm_en_data[0]); count ++) + { + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_tm_en_data[count][0], wifi_tm_en_data[count][1]); + if(ret) + goto err; + } + + msleep(8); //8ms delay + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001); //PAGE UP + if(ret) + goto err; + + ret = i2c_read_1_addr_2_data(rda_wifi_rf_client,0x21,&temp_data); + if(ret) + goto err; + else + wlan_version = temp_data; + + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000); //PAGE DOWN + if(ret) + goto err; + + + printk(KERN_INFO "***rda_wifi_en_succceed \n"); + return 0; +err: + printk(KERN_INFO "***rda_wifi_power_on failed! \n"); + return -1; +} + +int rda_tm_wifi_rf_init(void) +{ + unsigned int count = 0; + int ret = 0; + + for(count = 0; count < sizeof(wifi_tm_rf_init_data)/sizeof(wifi_tm_rf_init_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + printk(KERN_INFO "***rda_wifi_rf_init_succceed \n"); + msleep(5); //5ms delay + return 0; + +err: + printk(KERN_INFO "***rda_wifi_rf_init failed! \n"); + return -1; +} +/*houzhen add 2012 04 09 + add to ensure wf dig powerup + */ + +int rda_wifi_dig_reset(void) +{ + unsigned int count = 0; + int ret = 0; + msleep(8); //8ms delay + mutex_lock(&i2c_rw_lock); + + for(count = 0; count < sizeof(wifi_dig_reset_data)/sizeof(wifi_dig_reset_data[0]); count ++) + { + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_dig_reset_data[count][0], wifi_dig_reset_data[count][1]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + msleep(8); //8ms delay + printk(KERN_INFO "***rda_wifi_dig_reset \n"); + return 0; +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_dig_reset failed! \n"); + return -1; +} + +int rda_wlan_version(void) +{ + printk("******version %x \n", wlan_version); + return wlan_version; +} + +int rda_wifi_power_off(void) +{ + unsigned int count = 0; + int ret = 0; + u16 temp=0x0000; + printk(KERN_INFO "rda_wifi_power_off \n"); + + if(!rda_wifi_rf_client) + { + printk(KERN_INFO "rda_wifi_power_off failed on:i2c client \n"); + return -1; + } + + mutex_lock(&i2c_rw_lock); + ret=rda5990_wf_setup_A2_power(0); //disable pa_reg for wf + if(ret) + goto err; + + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001); //page up + if(ret) + goto err; + + ret=i2c_read_1_addr_2_data(rda_wifi_rf_client,0x28,&temp); //poll bt status + if(ret) + goto err; + + if(temp&0x8000) + { + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000); //page down + if(ret) + goto err; + + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x0f, 0x2223); // set antenna for bt + if(ret) + goto err; + + } + + for(count = 0; count < sizeof(wifi_off_data)/sizeof(wifi_off_data[0]); count ++) + { + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, wifi_off_data[count][0], wifi_off_data[count][1]); + if(ret) + goto err; + } + printk(KERN_INFO "***rda_wifi_power_off success!!! \n"); + + + + mutex_unlock(&i2c_rw_lock); + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_power_off failed! \n"); + return -1; + +} + +int rda_wifi_power_on(void) +{ + int ret; + char retry = 3; + + printk("------------------rda_wifi_power_on\n"); + if(!rda_wifi_rf_client) + { + printk(KERN_INFO "rda_wifi_power_on failed on:i2c client \n"); + return -1; + } + +_retry: + + + if(!wifi_in_test_mode) + { + ret = rda_wifi_en(); + if(ret < 0) + goto err; + + ret = rda_wifi_rf_init(); + if(ret < 0) + goto err; + + ret = rda_wifi_dc_cal(); + if(ret < 0) + goto err; + + msleep(20); //20ms delay + ret=rda_wifi_dig_reset(); //houzhen add to ensure wf power up safely + + if(ret < 0) + goto err; + msleep(20); //20ms delay + } + else + { + ret = rda_tm_wifi_en(); + if(ret < 0) + goto err; + + ret = rda_tm_wifi_rf_init(); + if(ret < 0) + goto err; + } + printk(KERN_INFO "rda_wifi_power_on_succeed!! \n"); + + return 0; + +err: + printk(KERN_INFO "rda_wifi_power_on_failed retry:%d \n", retry); + if(retry -- > 0) + { + rda_wifi_power_off(); + goto _retry; + } + + return -1; +} + +extern int rda_wifi_power_on(void); + +int rda_fm_power_on(void) +{ + int ret = 0; + u16 temp = 0; + + if(!rda_wifi_rf_client) + { + printk(KERN_INFO "rda_wifi_rf_client is NULL, rda_fm_power_on failed!\n"); + return -1; + } + + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001); // page down + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + ret = i2c_read_1_addr_2_data(rda_wifi_rf_client, 0x22, &temp); //read 0xA2 + if(ret < 0){ + printk(KERN_INFO "%s() read from address(0x%02x) failed! \n", __func__, 0x22); + return -1; + } + temp = temp & (~(1 << 15)); //clear bit[15] + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x22, temp); //write back + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000); // page up + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + + return 0; +} + +int rda_fm_power_off(void) +{ + int ret = 0; + u16 temp = 0; + + if(!rda_wifi_rf_client) + { + printk(KERN_INFO "rda_wifi_rf_client is NULL, rda_fm_power_off failed!\n"); + return -1; + } + + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0001); // page down + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + ret = i2c_read_1_addr_2_data(rda_wifi_rf_client, 0x22, &temp); //read 0xA2 + if(ret < 0){ + printk(KERN_INFO "%s() read from address(0x%02x) failed! \n", __func__, 0x22); + return -1; + } + temp = temp | (1 << 15); //set bit[15] + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x22, temp); //write back + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + ret = i2c_write_1_addr_2_data(rda_wifi_rf_client, 0x3f, 0x0000); // page up + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + + return 0; +} + +static int rda_bt_rf_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int result = 0; + + rda_bt_rf_client = client; + return result; +} + +static int rda_bt_rf_remove(struct i2c_client *client) +{ + rda_bt_rf_client = NULL; + return 0; +} + +static int rda_bt_rf_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) +{ + strcpy(info->type, RDA_BT_RF_I2C_DEVNAME); + return 0; +} + +static struct i2c_driver rda_bt_rf_driver = { + .probe = rda_bt_rf_probe, + .remove = rda_bt_rf_remove, + .detect = rda_bt_rf_detect, + .driver.name = RDA_BT_RF_I2C_DEVNAME, + .id_table = bt_rf_i2c_id, + //.address_data = &rda_bt_rf_addr_data, + //.address_list = (const unsigned short*)bt_rf_force, +}; + + +static int rda_bt_core_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) +{ + strcpy(info->type, RDA_BT_CORE_I2C_DEVNAME); + return 0; +} + +static int rda_bt_core_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int result = 0; + + rda_bt_core_client = client; + return result; +} + +static int rda_bt_core_remove(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_driver rda_bt_core_driver = { + .probe = rda_bt_core_probe, + .remove = rda_bt_core_remove, + .detect = rda_bt_core_detect, + .driver.name = RDA_BT_CORE_I2C_DEVNAME, + .id_table = bt_core_i2c_id, + //.address_data = &rda_bt_core_addr_data, + //.address_list = (const unsigned short*)bt_core_force, +}; + +u16 rda_5990_bt_off_data[][2] = +{ + {0x3f, 0x0001 }, //pageup + {0x28, 0x00A1 }, //power off bt + {0x3f, 0x0000 }, //pagedown +}; + +/*houzhen update 2012 03 06*/ +u16 rda_5990_bt_en_data[][2] = +{ + {0x3f, 0x0001 }, //pageup +#ifdef RDA5990_USE_DCDC + {0x23, 0x8FA1}, // //20111001 higher AVDD voltage to improve EVM +#else + {0x23, 0x0FA1}, +#endif + {0x24, 0x80C8}, // ;//freq_osc_in[1:0]00 + {0x26, 0x47A5}, // reg_vbit_normal_bt[2:0] =111 + {0x27, 0x4925}, // //for ver.c20111109, txswitch + {0x29, 0x111F}, // // rden4in_ldoon_bt=1 + {0x32, 0x0111}, // set_ rdenout_ldooff_wf=0; + {0x39, 0x0000}, // //uart switch to bt + + {0x28, 0x80A1}, // bt en + {0x3f, 0x0000}, //pagedown +}; + + +u16 rda_5990_bt_dc_cal[][2] = +{ + {0x3f, 0x0000 }, + {0x30, 0x0129 }, + {0x30, 0x012B }, + {0x3f, 0x0000 }, +}; + + +u16 rda_5990_bt_set_rf_switch_data[][2] = +{ + {0x3f, 0x0000 }, + {0x0F, 0x2223 }, + {0x3f, 0x0000 }, +}; + + +u16 RDA5990_bt_enable_clk_data[][2] = +{ + {0x3f, 0x0000 }, + {0x30, 0x0040 }, + {0x2a, 0x285d }, + {0x3f, 0x0000 }, +}; + +u16 RDA5990_bt_dig_reset_data[][2] = +{ + {0x3f, 0x0001 }, //pageup + {0x28, 0x86A1 }, + {0x28, 0x87A1 }, + {0x28, 0x85A1 }, + {0x3f, 0x0000 }, //pagedown +}; + +/*houzhen update 2012 03 06*/ +u16 rda_5990_bt_rf_data[][2] = +{ + {0x3f, 0x0000}, //pagedown + {0x01, 0x1FFF}, + {0x06, 0x07F7}, + {0x08, 0x29E7}, + {0x09, 0x0520}, + {0x0B, 0x03DF}, + {0x0C, 0x85E8}, + {0x0F, 0x0DBC}, + {0x12, 0x07F7}, + {0x13, 0x0327}, + {0x14, 0x0CCC}, + {0x15, 0x0526}, + {0x16, 0x8918}, + {0x18, 0x8800}, + {0x19, 0x10C8}, + {0x1A, 0x9078}, + {0x1B, 0x80E2}, + {0x1C, 0x361F}, + {0x1D, 0x4363}, + {0x1E, 0x303F}, + {0x23, 0x2222}, + {0x24, 0x359D}, + {0x27, 0x0011}, + {0x28, 0x124F}, + {0x39, 0xA5FC}, + {0x3f, 0x0001}, //page 1 + {0x00, 0x043F}, + {0x01, 0x467F}, + {0x02, 0x28FF}, + {0x03, 0x67FF}, + {0x04, 0x57FF}, + {0x05, 0x7BFF}, + {0x06, 0x3FFF}, + {0x07, 0x7FFF}, + {0x18, 0xF3F5}, + {0x19, 0xF3F5}, + {0x1A, 0xE7F3}, + {0x1B, 0xF1FF}, + {0x1C, 0xFFFF}, + {0x1D, 0xFFFF}, + {0x1E, 0xFFFF}, + {0x1F, 0xFFFF}, + // {0x22, 0xD3C7}, + // {0x23, 0x8fa1}, + // {0x24, 0x80c8}, + // {0x26, 0x47A5}, + // {0x27, 0x4925}, + // {0x28, 0x85a1}, + // {0x29, 0x111f}, + // {0x32, 0x0111}, + // {0x39, 0x0000}, + {0x3f, 0x0000}, //pagedown +}; + +/*houzhen update Mar 15 2012 + should be called when power up/down bt + */ +static int rda5990_bt_setup_A2_power(int enable) +{ + int ret; + u16 temp_data=0; + + ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0001); + if(ret) + goto err; + + if(enable) + { + ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x22,&temp_data); + if(ret) + goto err; + printk(KERN_INFO "***0xA2 readback value:0x%X \n", temp_data); + + temp_data |=0x0200; /*en reg4_pa bit*/ + + ret=i2c_write_1_addr_2_data(rda_bt_rf_client,0x22,temp_data); + if(ret) + goto err; + } + else + { + ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x31,&temp_data); + if(ret) + goto err; + + if(temp_data&0x8000) // wf is on + { + goto out; + } + else + { + ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x22,&temp_data); + if(ret) + goto err; + temp_data&=0xfdff; + + ret=i2c_write_1_addr_2_data(rda_bt_rf_client,0x22,temp_data); + if(ret) + goto err; + } + + } + +out: + ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0000); + if(ret) + goto err; + return 0; + +err: + printk(KERN_INFO "***rda5990_bt_setup_A2_power failed! \n"); + return -1; +} + +int rda_bt_power_off(void); + +int rda_bt_power_on(void) +{ + unsigned int count = 0; + int ret = 0; + + printk(KERN_INFO "rda_bt_power_on \n"); + + if(!rda_bt_rf_client || !rda_bt_rf_client) + { + printk(KERN_INFO "rda_bt_power_on failed on:i2c client \n"); + return -1; + } + + mutex_lock(&i2c_rw_lock); + + for(count = 0; count < sizeof(rda_5990_bt_en_data)/sizeof(rda_5990_bt_en_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + ret=rda5990_bt_setup_A2_power(1); + if(ret) + { + printk(KERN_INFO "***rda5990_bt_setup_A2_power fail!!! \n"); + goto err; + } + + printk(KERN_INFO "***rda_bt_power_on success!!! \n"); + + mutex_unlock(&i2c_rw_lock); + /*houzhen update 2012 03 06*/ + msleep(10); //delay 10 ms after power on + + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_bt_power_on failed! \n"); + return -1; + +} + +int rda_bt_power_off(void) +{ + unsigned int count = 0; + int ret = 0; + printk(KERN_INFO "rda_bt_power_off \n"); + + if(!rda_bt_rf_client) + { + printk(KERN_INFO "rda_bt_power_off failed on:i2c client \n"); + return -1; + } + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(rda_5990_bt_off_data)/sizeof(rda_5990_bt_off_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + msleep(10); //10ms + printk(KERN_INFO "***rda_bt_power_off success!!! \n"); + + ret=rda5990_bt_setup_A2_power(0);//disable ldo_pa reg + if(ret) + goto err; + + + + mutex_unlock(&i2c_rw_lock); + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_bt_power_off failed! \n"); + return -1; + +} + + +int RDA5990_bt_rf_init(void) +{ + unsigned int count = 0; + int ret = 0; + printk(KERN_INFO "RDA5990_bt_rf_init \n"); + + if(!rda_bt_rf_client) + { + printk(KERN_INFO "RDA5990_bt_rf_init on:i2c client \n"); + return -1; + } + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(rda_5990_bt_rf_data)/sizeof(rda_5990_bt_rf_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_rf_init success!!! \n"); + msleep(5); //5ms + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_rf_init failed! \n"); + return -1; + +} + +/*houzhen add 2012 04 09 + add to ensure bt dig powerup + */ + +int RDA5990_bt_dig_reset(void) +{ + unsigned int count = 0; + int ret = 0; + + printk(KERN_INFO "RDA5990_bt_dig_reset \n"); + if(!rda_bt_rf_client) + { + printk(KERN_INFO "RDA5990_bt_dig_reset on:i2c client \n"); + return -1; + } + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(RDA5990_bt_dig_reset_data)/sizeof(RDA5990_bt_dig_reset_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_rf_init success!!! \n"); + msleep(5); //5ms + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_rf_init failed! \n"); + return -1; + +} + + +int RDA5990_bt_dc_cal(void) +{ + unsigned int count = 0; + int ret = 0; + printk(KERN_INFO "rda_bt_dc_cal \n"); + + if(!rda_bt_rf_client) + { + printk(KERN_INFO "rda_bt_rf_client \n"); + return -1; + } + mutex_lock(&i2c_rw_lock); + + for(count = 0; count < sizeof(rda_5990_bt_dc_cal)/sizeof(rda_5990_bt_dc_cal[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_bt_dc_cal success!!! \n"); + msleep(200); //200ms + + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_bt_dc_cal failed! \n"); + return -1; + +} + + + +/*houzhen update Mar 15 2012 + bypass RDA5990_bt_set_rf_switch when wf is already on + */ + +int RDA5990_bt_set_rf_switch(void) +{ + unsigned int count = 0; + int ret = 0; + u16 temp_data=0; + printk(KERN_INFO "RDA5990_bt_set_rf_switch \n"); + + if(!rda_bt_rf_client || !rda_wifi_rf_client) + { + printk(KERN_INFO "RDA5990_bt_set_rf_switch failed on:i2c client \n"); + return -1; + } + + mutex_lock(&i2c_rw_lock); + + ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0001); + if(ret) + goto err; + + ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x31,&temp_data); + + if(ret) + goto err; + + if(temp_data&0x8000) // if wf is already on + { + printk(KERN_INFO "wf already en, bypass RDA5990_bt_set_rf_switch function \n"); + ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0000); + if(ret) + goto err; + mutex_unlock(&i2c_rw_lock); + return 0; + } + + for(count = 0; count < sizeof(rda_5990_bt_set_rf_switch_data)/sizeof(rda_5990_bt_set_rf_switch_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_set_rf_switch success!!! \n"); + msleep(50); //50ms + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_set_rf_switch failed! \n"); + return -1; + +} + + +/*houzhen update Mar 15 2012 + bypass RDA5990_bt_enable_clk when wf is already on + */ + +int RDA5990_bt_enable_clk(void) +{ + unsigned int count = 0; + int ret = 0; + u16 temp_data=0; + printk(KERN_INFO "RDA5990_bt_enable_clk \n"); + + if(!rda_bt_rf_client) + { + printk(KERN_INFO "RDA5990_bt_enable_clk failed on:i2c client \n"); + return -1; + } + + mutex_lock(&i2c_rw_lock); + ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0001); + if(ret) + goto err; + + ret=i2c_read_1_addr_2_data(rda_bt_rf_client,0x31,&temp_data); + + if(ret) + goto err; + + if(temp_data&0x8000) // if wf is already on + { + printk(KERN_INFO "wf already en, bypass RDA5990_bt_enable_clk function \n"); + ret = i2c_write_1_addr_2_data(rda_bt_rf_client, 0x3f, 0x0000); + if(ret) + goto err; + mutex_unlock(&i2c_rw_lock); + return 0; + } + + + for(count = 0; count < sizeof(RDA5990_bt_enable_clk_data)/sizeof(RDA5990_bt_enable_clk_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_enable_clk success!!! \n"); + msleep(50); //50ms + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_enable_clk failed! \n"); + return -1; + +} + +//extern void mt_combo_bgf_enable_irq(void); +//extern void mt_combo_bgf_disable_irq(void); + +#define RDA_BT_IOCTL_MAGIC 'u' +#define RDA_BT_POWER_ON_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x01) +#define RD_BT_RF_INIT_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x02) +#define RD_BT_DC_CAL_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x03) +#define RD_BT_SET_RF_SWITCH_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x04) +#define RDA_BT_POWER_OFF_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x05) +#define RDA_BT_EN_CLK _IO(RDA_BT_IOCTL_MAGIC ,0x06) +#define RD_BT_DC_DIG_RESET_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x07) + +#define RDA_WIFI_POWER_ON_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x10) +#define RDA_WIFI_POWER_OFF_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x11) +#define RDA_WIFI_POWER_SET_TEST_MODE_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x12) +#define RDA_WIFI_POWER_CANCEL_TEST_MODE_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x13) +#define RDA_WIFI_DEBUG_MODE_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x14) + +extern int rk29sdk_wifi_set_carddetect(int val); +static int rda_5990_pw_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch(cmd) + { + case RDA_WIFI_POWER_ON_IOCTL: + rda_wifi_power_on(); + rk29sdk_wifi_set_carddetect(1); + break; + + case RDA_WIFI_POWER_OFF_IOCTL: + rk29sdk_wifi_set_carddetect(0); + rda_wifi_power_off(); + break; + + case RDA_WIFI_POWER_SET_TEST_MODE_IOCTL: + wifi_in_test_mode = 1; + printk("****set rda wifi in test mode"); + break; + + case RDA_WIFI_POWER_CANCEL_TEST_MODE_IOCTL: + wifi_in_test_mode = 0; + printk("****set rda wifi in normal mode"); + break; + + case RDA_WIFI_DEBUG_MODE_IOCTL: + ret = rda_wifi_debug_en(); + break; + + case RDA_BT_POWER_ON_IOCTL: + { + ret = rda_bt_power_on(); + //mt_combo_bgf_enable_irq(); + } + break; + + /* should call thif function after bt_power_on*/ + case RDA_BT_EN_CLK: + ret = RDA5990_bt_enable_clk(); + break; + + case RD_BT_RF_INIT_IOCTL: + ret = RDA5990_bt_rf_init(); + break; + + case RD_BT_DC_CAL_IOCTL: + ret = RDA5990_bt_dc_cal(); + break; + + case RD_BT_DC_DIG_RESET_IOCTL: + ret = RDA5990_bt_dig_reset(); + break; + + case RD_BT_SET_RF_SWITCH_IOCTL: + ret = RDA5990_bt_set_rf_switch(); + break; + + case RDA_BT_POWER_OFF_IOCTL: + { + //mt_combo_bgf_disable_irq(); + ret = rda_bt_power_off(); + } + break; + + default: + break; + } + + printk(KERN_INFO "rda_bt_pw_ioctl cmd=0x%02x \n", cmd); + + return ret; +} + +void mmc_rescan_slot(int id) +{ + rda_wifi_power_on(); + rk29sdk_wifi_set_carddetect(1); +} +EXPORT_SYMBOL(mmc_rescan_slot); + +void mmc_remove(int id) +{ + rk29sdk_wifi_set_carddetect(0); + rda_wifi_power_off(); +} +EXPORT_SYMBOL(mmc_remove); + +static int rda_5990_major; +static struct class *rda_5990_class = NULL; +static const struct file_operations rda_5990_operations = { + .owner = THIS_MODULE, + .unlocked_ioctl = rda_5990_pw_ioctl, + .release = NULL +}; + +void rda_5990_sleep_worker_task(struct work_struct *work) +{ + printk("---rda_5990_sleep_worker_task end"); + wake_unlock(&rda_5990_wake_lock); +} + +void rda_5990_set_wake_lock(void) +{ + wake_lock(&rda_5990_wake_lock); + cancel_delayed_work(&rda_5990_sleep_worker); + schedule_delayed_work(&rda_5990_sleep_worker, 6*HZ); +} + +int rda_5990_power_ctrl_init(void) +{ + int ret = 0; + printk("rda_5990_power_ctrl_init begin\n"); + + /*i2c_register_board_info(0, &i2c_rda_wifi_core, 1); + if (i2c_add_driver(&rda_wifi_core_driver)) + { + printk("rda_wifi_core_driver failed!\n"); + ret = -ENODEV; + return ret; + } + */ + + //i2c_register_board_info(0, &i2c_rda_wifi_rf, 1); + if (i2c_add_driver(&rda_wifi_rf_driver)) + { + printk("rda_wifi_rf_driver failed!\n"); + ret = -ENODEV; + return ret; + } + + /* + i2c_register_board_info(0, &i2c_rda_bt_core, 1); + if (i2c_add_driver(&rda_bt_core_driver)) + { + printk("rda_bt_core_driver failed!\n"); + ret = -ENODEV; + return ret; + } + */ + + //i2c_register_board_info(0, &i2c_rda_bt_rf, 1); + if (i2c_add_driver(&rda_bt_rf_driver)) + { + printk("rda_bt_rf_driver failed!\n"); + ret = -ENODEV; + return ret; + } + + rda_5990_major = register_chrdev(0, "rda5990_power_ctrl", &rda_5990_operations); + if(rda_5990_major < 0) + { + printk(KERN_INFO "register rdabt_power_ctrl failed!!! \n"); + return rda_5990_major; + } + + rda_5990_class = class_create(THIS_MODULE, "rda_combo"); + if(IS_ERR(rda_5990_class)) + { + unregister_chrdev(rda_5990_major, "rdabt_power_ctrl"); + return PTR_ERR(rda_5990_class); + } + + device_create(rda_5990_class, NULL, MKDEV(rda_5990_major, 0), NULL, "rdacombo"); + + { + unsigned char* temp = NULL; + unsigned short testData = 0xffee; + temp = (unsigned char *)&testData; + if(*temp == 0xee) + isBigEnded = 0; + else + isBigEnded = 1; + } + + INIT_DELAYED_WORK(&rda_5990_sleep_worker, rda_5990_sleep_worker_task); + wake_lock_init(&rda_5990_wake_lock, WAKE_LOCK_SUSPEND, "RDA_sleep_worker_wake_lock"); + + mutex_init(&i2c_rw_lock); + printk("rda_5990_power_ctrl_init end\n"); + return 0; +} + +void rda_5990_power_ctrl_exit(void) +{ + i2c_del_driver(&rda_wifi_core_driver); + i2c_del_driver(&rda_wifi_rf_driver); + i2c_del_driver(&rda_bt_core_driver); + i2c_del_driver(&rda_bt_rf_driver); + + unregister_chrdev(rda_5990_major, "rdabt_power_ctrl"); + if(rda_5990_class) + class_destroy(rda_5990_class); + + cancel_delayed_work_sync(&rda_5990_sleep_worker); + wake_lock_destroy(&rda_5990_wake_lock); +} + +unsigned char rda_5990_wifi_in_test_mode(void) +{ + return wifi_in_test_mode; +} + + +static __inline__ void cfmakeraw(struct termios *s) +{ + s->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + s->c_oflag &= ~OPOST; + s->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + s->c_cflag &= ~(CSIZE|PARENB); + s->c_cflag |= CS8; +} + +static __inline__ int cfsetospeed(struct termios *s, speed_t speed) +{ + s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD); + return 0; +} + +static __inline__ int cfsetispeed(struct termios *s, speed_t speed) +{ + s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD); + return 0; +} + +static int rda_wifi_init_uart(char *dev) +{ + int errno; + struct termios ti; + struct serial_struct ss; + int fd; + + fd = sys_open(dev, O_RDWR | O_NOCTTY, 0); + if (fd < 0) { + printk("Can't open serial port"); + return -1; + } + + sys_ioctl(fd, TCFLSH, TCIOFLUSH); + + /* Clear the cust flag */ + if((errno = sys_ioctl(fd, TIOCGSERIAL, &ss))<0){ + printk("BAUD: error to get the serial_struct info:%s\n", errno); + goto err; + } + + if (ss.flags & ASYNC_SPD_CUST) { + printk("clear ASYNC_SPD_CUST\r\n"); + ss.flags &= ~ASYNC_SPD_CUST; + } + if((errno = sys_ioctl(fd, TIOCSSERIAL, &ss))<0){ + printk("BAUD: error to set serial_struct:%s\n", errno); + goto err; + } + + if ((errno = sys_ioctl(fd, TCGETS, (long)&ti)) < 0) { + printk("unable to get UART port setting"); + printk("Can't get port settings"); + goto err; + } + + cfmakeraw(&ti); + + ti.c_cflag |= CLOCAL; + ti.c_cflag &= ~CRTSCTS; + ti.c_lflag = 0; + ti.c_cc[VTIME] = 5; /* 0.5 sec */ + ti.c_cc[VMIN] = 0; + + /* Set initial baudrate */ + cfsetospeed(&ti, B115200); + cfsetispeed(&ti, B115200); + + if ((errno = sys_ioctl(fd, TCSETS, (long)&ti)) < 0) { + printk("unable to set UART port setting"); + printk("Can't set port settings"); + goto err; + } + + errno = sys_ioctl(fd, TCFLSH, TCIOFLUSH); + if(errno < 0) + goto err; + + return fd; + +err: + if(fd > 0) + sys_close(fd); + + return -1; +} + +EXPORT_SYMBOL(rda_wlan_version); + +EXPORT_SYMBOL(rda_wifi_init_uart); +EXPORT_SYMBOL(rda_5990_wifi_in_test_mode); + +EXPORT_SYMBOL(rda_5990_set_wake_lock); +EXPORT_SYMBOL(rda_wifi_power_off); +EXPORT_SYMBOL(rda_wifi_power_on); +EXPORT_SYMBOL(rda_fm_power_on); +EXPORT_SYMBOL(rda_fm_power_off); + +module_init(rda_5990_power_ctrl_init); +module_exit(rda_5990_power_ctrl_exit); + +MODULE_LICENSE("GPL"); + 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 index 000000000000..8f508c12de07 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_5990_power_ctrl/rda_5990_power_ctrl_by_gpio.c @@ -0,0 +1,1462 @@ +/* ----------------------------------------------------------------------- * + * + This file created by albert RDA Inc + */ + +#include +#include +#include +#include +#include +#include /* get the user-level API */ +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#define RDA5890_USE_CRYSTAL //if use share crystal should close this +#define RDA5990_USE_DCDC + +#define u32 unsigned int +#define u8 unsigned char +#define u16 unsigned short + +extern int rda_gpio_i2c_read_1_addr_2_data(uint8_t chipAddr, uint8_t regAddr, uint16_t *buffer); +extern int rda_gpio_i2c_write_1_addr_2_data(uint8_t chipAddr, uint8_t regAddr, uint16_t data); +extern int rda_gpio_i2c_read_4_addr_4_data(uint8_t chipAddr, uint32_t regAddr, uint32_t *buffer); +extern int rda_gpio_i2c_write_4_addr_4_data(uint8_t chipAddr, uint32_t regAddr, uint32_t data); + + +//#define RDA_I2C_CHANNEL (0) +#define RDA_WIFI_CORE_ADDR (0x13) +#define RDA_WIFI_RF_ADDR (0x14) //correct add is 0x14 +#define RDA_BT_CORE_ADDR (0x15) +#define RDA_BT_RF_ADDR (0x16) + +static struct mutex i2c_rw_lock; +static unsigned short wlan_version = 0; +static struct wake_lock rda_5990_wake_lock; +static struct delayed_work rda_5990_sleep_worker; + +static u8 isBigEnded = 0; + +static u8 wifi_in_test_mode = 0; + +const u32 wifi_core_init_data[][2] = +{ + +}; + +u16 wifi_off_data[][2] = +{ + { 0x3F, 0x0001 }, //page up + { 0x31, 0x0B40 }, //power off wifi + { 0x3F, 0x0000 }, //page down +}; + +u16 wifi_en_data[][2] = +{ + //item:VerD_wf_on_2012_02_08 + {0x3f, 0x0001}, +#ifdef RDA5990_USE_DCDC /*houzhen update Mar 15 2012 */ + {0x23, 0x8FA1},//20111001 higher AVDD voltage to improve EVM to 0x8f21 download current -1db 0x8fA1>>0x8bA1 +#else + {0x23, 0x0FA1}, +#endif + {0x31, 0x0B40 }, //power off wifi +// {0x22, 0xD3C7},//for ver.c 20111109, txswitch + {0x24, 0x80C8},//freq_osc_in[1:0]00 0x80C8 >> 0x80CB + {0x27, 0x4925},//for ver.c20111109, txswitch + // {0x28, 0x80A1}, //BT_enable + {0x31, 0x8140},//enable wifi + {0x32, 0x0113},//set_ rdenout_ldooff_wf=0; rden4in_ldoon_wf=1 + // {0x39, 0x0004}, //uart switch to wf + {0x3F, 0x0000}, //page down +}; + + +u16 wifi_dc_cal_data[][2]= +{ + {0x3f, 0x0000}, + {0x30, 0x0248}, + {0x30, 0x0249}, + //{wait 200ms; } here +}; + +u16 wifi_dig_reset_data[][2]= +{ + {0x3F, 0x0001}, + {0x31, 0x8D40}, + {0x31, 0x8F40}, + {0x31, 0x8b40}, + {0x3F, 0x0000}, +}; + +u16 wifi_rf_init_data_verE[][2] = +{ + {0x3f, 0x0000}, + //{;;set_rf_swi},ch + {0x06, 0x0101}, + {0x07, 0x0101}, + {0x08, 0x0101}, + {0x09, 0x0101}, + {0x0A, 0x002C},//aain_0 + {0x0D, 0x0507}, + {0x0E, 0x2300}, + {0x0F, 0x5689},// + //{;;//set_RF }, + {0x10, 0x0f78},//20110824 + {0x11, 0x0602}, + {0x13, 0x0652},//adc_tuning_bit[011] + {0x14, 0x8886}, + {0x15, 0x0990}, + {0x16, 0x049f}, + {0x17, 0x0990}, + {0x18, 0x049F}, + {0x19, 0x3C01}, + {0x1C, 0x0934}, + {0x1D, 0xFF00},//for ver.D20120119for temperature 70 degree + //{0x1F, 0x01F8},//for ver.E should not set + //{0x1F, 0x0300},//for ver.E should not set + {0x20, 0x06E4}, + {0x21, 0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset + {0x22, 0x24DC}, + {0x23, 0x23FF}, + {0x24, 0x00FC}, + {0x26, 0x004F},//004F >> 005f premote pa + {0x27, 0x171D},///mdll*7 + {0x28, 0x031D},///mdll*7 + {0x2A, 0x2860},//et0x2849-8.5p :yd 0x2861-7pf C1,C2=6.8p + {0x2B, 0x0800},//bbpll,or ver.c20111116 + {0x32, 0x8a08}, + {0x33, 0x1D02},//liuyanan + //{;;//agc_gain}, + {0x36, 0x02f4}, //00F8;//gain_7 + {0x37, 0x01f4}, //0074;//aain_6 + {0x38, 0x21d4}, //0014;//gain_5 + {0x39, 0x25d4}, //0414;//aain_4 + {0x3A, 0x2584}, //1804;//gain_3 + {0x3B, 0x2dc4}, //1C04;//aain_2 + {0x3C, 0x2d04}, //1C02;//gain_1 + {0x3D, 0x2c02}, //3C01;//gain_0 + {0x33, 0x1502},//liuyanan + //{;;SET_channe},_to_11 + {0x1B, 0x0001},//set_channel + {0x30, 0x024D}, + {0x29, 0xD468}, + {0x29, 0x1468}, + {0x30, 0x0249}, + {0x3f, 0x0000}, +}; + +u16 wifi_rf_init_data[][2] = +{ + {0x3f, 0x0000}, + //{;;set_rf_swi},ch + {0x06, 0x0101}, + {0x07, 0x0101}, + {0x08, 0x0101}, + {0x09, 0x0101}, + {0x0A, 0x002C},//aain_0 + {0x0D, 0x0507}, + {0x0E, 0x2300}, + {0x0F, 0x5689},// + //{;;//set_RF }, + {0x10, 0x0f78},//20110824 + {0x11, 0x0602}, + {0x13, 0x0652},//adc_tuning_bit[011] + {0x14, 0x8886}, + {0x15, 0x0990}, + {0x16, 0x049f}, + {0x17, 0x0990}, + {0x18, 0x049F}, + {0x19, 0x3C01}, + {0x1C, 0x0934}, + {0x1D, 0xFF00},//for ver.D20120119for temperature 70 degree + //{0x1F, 0x01F8},//for ver.c20111109 + {0x1F, 0x0300},//for burst tx 不锁 + {0x20, 0x06E4}, + {0x21, 0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset + {0x22, 0x24DC}, + {0x23, 0x23FF}, + {0x24, 0x00FC}, + {0x26, 0x004F},//004F >> 005f premote pa + {0x27, 0x171D},///mdll*7 + {0x28, 0x031D},///mdll*7 + {0x2A, 0x2860},//et0x2849-8.5p :yd 0x2861-7pf C1,C2=6.8p + {0x2B, 0x0800},//bbpll,or ver.c20111116 + {0x32, 0x8a08}, + {0x33, 0x1D02},//liuyanan + //{;;//agc_gain}, + {0x36, 0x02f4}, //00F8;//gain_7 + {0x37, 0x01f4}, //0074;//aain_6 + {0x38, 0x21d4}, //0014;//gain_5 + {0x39, 0x25d4}, //0414;//aain_4 + {0x3A, 0x2584}, //1804;//gain_3 + {0x3B, 0x2dc4}, //1C04;//aain_2 + {0x3C, 0x2d04}, //1C02;//gain_1 + {0x3D, 0x2c02}, //3C01;//gain_0 + {0x33, 0x1502},//liuyanan + //{;;SET_channe},_to_11 + {0x1B, 0x0001},//set_channel + {0x30, 0x024D}, + {0x29, 0xD468}, + {0x29, 0x1468}, + {0x30, 0x0249}, + {0x3f, 0x0000}, +}; + +u16 wifi_uart_debug_data[][2] = +{ + {0x3F,0x0001}, + {0x28,0x80A1}, //BT_enable + {0x39,0x0004}, //uart switch to wf + {0x3f,0x0000}, +}; + +u16 wifi_tm_en_data[][2] = +{ + {0x3F,0x0001}, +#ifdef RDA5990_USE_DCDC /*houzhen update Mar 15 2012 */ + {0x23, 0x8FA1},//20111001 higher AVDD voltage to improve EVM to 0x8f21 download current -1db 0x8fA1>>0x8bA1 +#else + {0x23, 0x0FA1}, +#endif + {0x22,0xD3C7},//for ver.c 20111109, tx + {0x24, 0x80C8},//freq_osc_in[1:0]00 0x80C8 >> 0x80CB + {0x27,0x4925},//for ver.c20111109, txs + {0x28,0x80A1}, //BT_enable + {0x29,0x111F}, + {0x31,0x8140}, + {0x32,0x0113},//set_ rdenout_ldooff_wf + {0x39,0x0004},//uart switch to wf + {0x3f,0x0000}, +}; + +u16 wifi_tm_rf_init_data[][2] = +{ + {0x3f, 0x0000}, + //set_rf_switch + {0x06,0x0101}, + {0x07,0x0101}, + {0x08,0x0101}, + {0x09,0x0101}, + {0x0A,0x002C},//aain_0 + {0x0D,0x0507}, + {0x0E,0x2300},//2012_02_20 + {0x0F,0x5689},// + //set_RF + {0x10,0x0f78},//20110824 + {0x11,0x0602}, + {0x13,0x0652},//adc_tuning_bit[011] + {0x14,0x8886}, + {0x15,0x0990}, + {0x16,0x049f}, + {0x17,0x0990}, + {0x18,0x049F}, + {0x19,0x3C01},//sdm_vbit[3:0]=1111 + {0x1C,0x0934}, + {0x1D,0xCE00},//for ver.D20120119for temperature 70 degree + {0x1F,0x0300},//div2_band_48g_dr=1;div2_band_48g_reg[8:0]1000000000 + {0x20,0x06E4}, + {0x21,0x0ACF},//for ver.c20111109,dr dac reset,dr txflt reset + {0x22,0x24DC}, + {0x23,0x23FF}, + {0x24,0x00FC}, + {0x26,0x004F}, + {0x27,0x171D},///mdll*7 + {0x28,0x031D},///mdll*7 + {0x2A,0x2860}, + {0x2B,0x0800},//bbpll,or ver.c20111116 + {0x32,0x8a08}, + {0x33,0x1D02},//liuyanan + //agc_gain + {0x36,0x02f4}, //00F8;//gain_7 + {0x37,0x01f4}, //0074;//aain_6 + {0x38,0x21d4}, //0014;//gain_5 + {0x39,0x25d4}, //0414;//aain_4 + {0x3A,0x2584}, //1804;//gain_3 + {0x3B,0x2dc4}, //1C04;//aain_2 + {0x3C,0x2d04}, //1C02;//gain_1 + {0x3D,0x2c02}, //3C01;//gain_0 + //DC_CAL + {0x30,0x0248}, + {0x30,0x0249}, + //wait 200ms; + {0x33,0x1502},//liuyanan + //SET_channel_to_11 + {0x1B,0x0001},//set_channel + {0x3f,0x0000}, +}; + +/*houzhen update Mar 15 2012 + should be called when power up/down bt + */ +static int rda5990_wf_setup_A2_power(int enable) +{ + int ret; + u16 temp_data=0; + printk("***rda5990_wf_setup_A2_power start! \n"); + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001); + if(ret) + goto err; + + if(enable) + { + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, &temp_data); + if(ret) + goto err; + printk("***0xA2 readback value:0x%X \n", temp_data); + + temp_data |=0x0200; /*en reg4_pa bit*/ +#ifdef RDA5890_USE_CRYSTAL + temp_data &= ~(1 << 14); //disable xen_out +#endif + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, temp_data); + if(ret) + goto err; + //read wlan version + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x21, &temp_data); + if(ret) + goto err; + else + wlan_version = temp_data; + } + else + { + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x28, &temp_data); + if(ret) + goto err; + if(temp_data&0x8000) // bt is on + { + goto out; + } + else + { + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, &temp_data); + if(ret) + goto err; + temp_data&=0xfdff; + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, temp_data); + if(ret) + goto err; + } + wlan_version = 0; + } + printk("rda5990_wf_setup_A2_power, version:%d", wlan_version); + +out: + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000); + if(ret) + goto err; + printk("***rda5990_wf_setup_A2_power succeed! \n"); + return 0; + +err: + printk("***rda5990_wf_setup_A2_power failed! \n"); + return -1; +} + + +int rda_wifi_rf_init(void) +{ + unsigned int count = 0; + int ret = 0; + mutex_lock(&i2c_rw_lock); + if( (wlan_version&0x1f) == 7) + { + for(count = 0; count < sizeof(wifi_rf_init_data)/sizeof(wifi_rf_init_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + } + else if((wlan_version&0x1f) == 4 || (wlan_version&0x1f)==5) + { + for(count = 0; count < sizeof(wifi_rf_init_data_verE)/sizeof(wifi_rf_init_data_verE[0]); count ++) + { + 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]); + if(ret) + goto err; + } + } + else + { + printk("unknown version of this 5990 chip\n"); + goto err; + } + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_rf_init_succceed \n"); + msleep(5); //5ms delay + return 0; +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_rf_init failed! \n"); + return -1; +} + +int rda_wifi_dc_cal(void) +{ + unsigned int count = 0; + int ret = 0; + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(wifi_dc_cal_data)/sizeof(wifi_dc_cal_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_rf_dc_calsuccceed \n"); + msleep(50); //50ms delay + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_rf_dc_calf_failed! \n"); + return -1; +} + +int rda_wifi_en(void) +{ + unsigned int count = 0; + int ret = 0; + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(wifi_en_data)/sizeof(wifi_en_data[0]); count ++) + { + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_en_data[count][0], wifi_en_data[count][1]); + if(ret) + goto err; + + if(wifi_en_data[count][0] == 0x31) + msleep(12); + } + + ret=rda5990_wf_setup_A2_power(1); //en pa_reg for wf + if(ret) + goto err; + + mutex_unlock(&i2c_rw_lock); + msleep(8); //8ms delay + + printk(KERN_INFO "***rda_wifi_en_succceed \n"); + return 0; +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_power_on failed! \n"); + return -1; +} + +int rda_wifi_debug_en(void) +{ + unsigned int count = 0; + int ret = 0; + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(wifi_uart_debug_data)/sizeof(wifi_uart_debug_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + +err: + mutex_unlock(&i2c_rw_lock); + return ret; +} + +int rda_tm_wifi_en(void) +{ + unsigned int count = 0; + int ret = 0; + unsigned short temp_data; + + for(count = 0; count < sizeof(wifi_tm_en_data)/sizeof(wifi_tm_en_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + msleep(8); //8ms delay + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001); //PAGE UP + if(ret) + goto err; + + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR,0x21,&temp_data); + if(ret) + goto err; + else + wlan_version = temp_data; + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000); //PAGE DOWN + if(ret) + goto err; + + printk(KERN_INFO "***rda_wifi_en_succceed \n"); + return 0; +err: + printk(KERN_INFO "***rda_wifi_power_on failed! \n"); + return -1; +} + +int rda_tm_wifi_rf_init(void) +{ + unsigned int count = 0; + int ret = 0; + + for(count = 0; count < sizeof(wifi_tm_rf_init_data)/sizeof(wifi_tm_rf_init_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + printk(KERN_INFO "***rda_wifi_rf_init_succceed \n"); + msleep(5); //5ms delay + return 0; + +err: + printk(KERN_INFO "***rda_wifi_rf_init failed! \n"); + return -1; +} +/*houzhen add 2012 04 09 + add to ensure wf dig powerup + */ + +int rda_wifi_dig_reset(void) +{ + unsigned int count = 0; + int ret = 0; + msleep(8); //8ms delay + mutex_lock(&i2c_rw_lock); + + for(count = 0; count < sizeof(wifi_dig_reset_data)/sizeof(wifi_dig_reset_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + msleep(8); //8ms delay + printk(KERN_INFO "***rda_wifi_dig_reset \n"); + return 0; +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_dig_reset failed! \n"); + return -1; +} + +int rda_wlan_version(void) +{ + printk("******version %x \n", wlan_version); + return wlan_version; +} + +int rda_wifi_power_off(void) +{ + unsigned int count = 0; + int ret = 0; + u16 temp=0x0000; + printk(KERN_INFO "rda_wifi_power_off \n"); + + + mutex_lock(&i2c_rw_lock); + ret=rda5990_wf_setup_A2_power(0); //disable pa_reg for wf + if(ret) + goto err; + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001); //page up + if(ret) + goto err; + + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x28, &temp); //poll bt status + if(ret) + goto err; + + if(temp&0x8000) + { + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000); //page down + if(ret) + goto err; + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x0f, 0x2223); // set antenna for bt + if(ret) + goto err; + + } + + + for(count = 0; count < sizeof(wifi_off_data)/sizeof(wifi_off_data[0]); count ++) + { + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, wifi_off_data[count][0], wifi_off_data[count][1]); + if(ret) + goto err; + } + printk(KERN_INFO "***rda_wifi_power_off success!!! \n"); + + + + mutex_unlock(&i2c_rw_lock); + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_wifi_power_off failed! \n"); + return -1; + +} + +int rda_wifi_power_on(void) +{ + int ret; + char retry = 3; + + +_retry: + + if(!wifi_in_test_mode) + { + ret = rda_wifi_en(); + if(ret < 0) + goto err; + + ret = rda_wifi_rf_init(); + if(ret < 0) + goto err; + + ret = rda_wifi_dc_cal(); + if(ret < 0) + goto err; + + msleep(20); //20ms delay + ret=rda_wifi_dig_reset(); //houzhen add to ensure wf power up safely + + if(ret < 0) + goto err; + msleep(20); //20ms delay + } + else + { + ret = rda_tm_wifi_en(); + if(ret < 0) + goto err; + + ret = rda_tm_wifi_rf_init(); + if(ret < 0) + goto err; + } + printk(KERN_INFO "rda_wifi_power_on_succeed!! \n"); + + return 0; + +err: + printk(KERN_INFO "rda_wifi_power_on_failed retry:%d \n", retry); + if(retry -- > 0) + { + rda_wifi_power_off(); + goto _retry; + } + + return -1; +} + +int rda_fm_power_on(void) +{ + int ret = 0; + u16 temp = 0; + + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001); // page down + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, &temp); //read 0xA2 + if(ret < 0){ + printk(KERN_INFO "%s() read from address(0x%02x) failed! \n", __func__, 0x22); + return -1; + } + temp = temp & (~(1 << 15)); //clear bit[15] + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, temp); //write back + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000); // page up + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + + return 0; +} + +int rda_fm_power_off(void) +{ + int ret = 0; + u16 temp = 0; + + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0001); // page down + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, &temp); //read 0xA2 + if(ret < 0){ + printk(KERN_INFO "%s() read from address(0x%02x) failed! \n", __func__, 0x22); + return -1; + } + temp = temp | (1 << 15); //set bit[15] + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x22, temp); //write back + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_WIFI_RF_ADDR, 0x3f, 0x0000); // page up + if(ret < 0){ + printk(KERN_INFO "%s() write address(0x%02x) with value(0x%04x) failed! \n", __func__, 0x3f, 0x0001); + return -1; + } + + return 0; +} + + +u16 rda_5990_bt_off_data[][2] = +{ + {0x3f, 0x0001 }, //pageup + {0x28, 0x00A1 }, //power off bt + {0x3f, 0x0000 }, //pagedown +}; + +/*houzhen update 2012 03 06*/ +u16 rda_5990_bt_en_data[][2] = +{ + {0x3f, 0x0001 }, //pageup +#ifdef RDA5990_USE_DCDC + {0x23, 0x8FA1}, // //20111001 higher AVDD voltage to improve EVM +#else + {0x23, 0x0FA1}, +#endif + {0x24, 0x80C8}, // ;//freq_osc_in[1:0]00 + {0x26, 0x47A5}, // reg_vbit_normal_bt[2:0] =111 + {0x27, 0x4925}, // //for ver.c20111109, txswitch + {0x29, 0x111F}, // // rden4in_ldoon_bt=1 + {0x32, 0x0111}, // set_ rdenout_ldooff_wf=0; + {0x39, 0x0000}, // //uart switch to bt + + {0x28, 0x80A1}, // bt en + {0x3f, 0x0000}, //pagedown +}; + + +u16 rda_5990_bt_dc_cal[][2] = +{ + {0x3f, 0x0000 }, + {0x30, 0x0129 }, + {0x30, 0x012B }, + {0x3f, 0x0000 }, +}; + + +u16 rda_5990_bt_set_rf_switch_data[][2] = +{ + {0x3f, 0x0000 }, + {0x0F, 0x2223 }, + {0x3f, 0x0000 }, +}; + + +u16 RDA5990_bt_enable_clk_data[][2] = +{ + {0x3f, 0x0000 }, + {0x30, 0x0040 }, + {0x2a, 0x285d }, + {0x3f, 0x0000 }, +}; + +u16 RDA5990_bt_dig_reset_data[][2] = +{ + {0x3f, 0x0001 }, //pageup + {0x28, 0x86A1 }, + {0x28, 0x87A1 }, + {0x28, 0x85A1 }, + {0x3f, 0x0000 }, //pagedown +}; + +/*houzhen update 2012 03 06*/ +u16 rda_5990_bt_rf_data[][2] = +{ + {0x3f, 0x0000}, //pagedown + {0x01, 0x1FFF}, + {0x06, 0x07F7}, + {0x08, 0x29E7}, + {0x09, 0x0520}, + {0x0B, 0x03DF}, + {0x0C, 0x85E8}, + {0x0F, 0x0DBC}, + {0x12, 0x07F7}, + {0x13, 0x0327}, + {0x14, 0x0CCC}, + {0x15, 0x0526}, + {0x16, 0x8918}, + {0x18, 0x8800}, + {0x19, 0x10C8}, + {0x1A, 0x9078}, + {0x1B, 0x80E2}, + {0x1C, 0x361F}, + {0x1D, 0x4363}, + {0x1E, 0x303F}, + {0x23, 0x2222}, + {0x24, 0x359D}, + {0x27, 0x0011}, + {0x28, 0x124F}, + {0x39, 0xA5FC}, + {0x3f, 0x0001}, //page 1 + {0x00, 0x043F}, + {0x01, 0x467F}, + {0x02, 0x28FF}, + {0x03, 0x67FF}, + {0x04, 0x57FF}, + {0x05, 0x7BFF}, + {0x06, 0x3FFF}, + {0x07, 0x7FFF}, + {0x18, 0xF3F5}, + {0x19, 0xF3F5}, + {0x1A, 0xE7F3}, + {0x1B, 0xF1FF}, + {0x1C, 0xFFFF}, + {0x1D, 0xFFFF}, + {0x1E, 0xFFFF}, + {0x1F, 0xFFFF}, + // {0x22, 0xD3C7}, + // {0x23, 0x8fa1}, + // {0x24, 0x80c8}, + // {0x26, 0x47A5}, + // {0x27, 0x4925}, + // {0x28, 0x85a1}, + // {0x29, 0x111f}, + // {0x32, 0x0111}, + // {0x39, 0x0000}, + {0x3f, 0x0000}, //pagedown +}; + +/*houzhen update Mar 15 2012 + should be called when power up/down bt + */ +static int rda5990_bt_setup_A2_power(int enable) +{ + int ret; + u16 temp_data=0; + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0001); + if(ret) + goto err; + + if(enable) + { + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x22, &temp_data); + if(ret) + goto err; + printk(KERN_INFO "***0xA2 readback value:0x%X \n", temp_data); + + temp_data |=0x0200; /*en reg4_pa bit*/ + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x22, temp_data); + if(ret) + goto err; + } + else + { + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x31, &temp_data); + if(ret) + goto err; + if(temp_data&0x8000) // wf is on + { + goto out; + } + else + { + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x22, &temp_data); + if(ret) + goto err; + temp_data&=0xfdff; + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x22, temp_data); + if(ret) + goto err; + } + + } + + +out: + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0000); + if(ret) + goto err; + return 0; + +err: + printk(KERN_INFO "***rda5990_bt_setup_A2_power failed! \n"); + return -1; +} + +int rda_bt_power_off(void); + +int rda_bt_power_on(void) +{ + unsigned int count = 0; + int ret = 0; + + printk(KERN_INFO "rda_bt_power_on \n"); + + + mutex_lock(&i2c_rw_lock); + + for(count = 0; count < sizeof(rda_5990_bt_en_data)/sizeof(rda_5990_bt_en_data[0]); count ++) + { + 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]); + + if(ret) + goto err; + } + + ret=rda5990_bt_setup_A2_power(1); + if(ret) + { + printk(KERN_INFO "***rda5990_bt_setup_A2_power fail!!! \n"); + goto err; + } + + printk(KERN_INFO "***rda_bt_power_on success!!! \n"); + + mutex_unlock(&i2c_rw_lock); + /*houzhen update 2012 03 06*/ + msleep(10); //delay 10 ms after power on + + + return 0; +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_bt_power_on failed! \n"); + return -1; + +} + +int rda_bt_power_off(void) +{ + unsigned int count = 0; + int ret = 0; + printk(KERN_INFO "rda_bt_power_off \n"); + + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(rda_5990_bt_off_data)/sizeof(rda_5990_bt_off_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + msleep(10); //10ms + printk(KERN_INFO "***rda_bt_power_off success!!! \n"); + + ret=rda5990_bt_setup_A2_power(0);//disable ldo_pa reg + if(ret) + goto err; + + + + mutex_unlock(&i2c_rw_lock); + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_bt_power_off failed! \n"); + return -1; + +} + + +int RDA5990_bt_rf_init(void) +{ + unsigned int count = 0; + int ret = 0; + printk(KERN_INFO "RDA5990_bt_rf_init \n"); + + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(rda_5990_bt_rf_data)/sizeof(rda_5990_bt_rf_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_rf_init success!!! \n"); + msleep(5); //5ms + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_rf_init failed! \n"); + return -1; + +} + + + +/*houzhen add 2012 04 09 + add to ensure bt dig powerup + */ + +int RDA5990_bt_dig_reset(void) +{ + unsigned int count = 0; + int ret = 0; + + printk(KERN_INFO "RDA5990_bt_dig_reset \n"); + + mutex_lock(&i2c_rw_lock); + for(count = 0; count < sizeof(RDA5990_bt_dig_reset_data)/sizeof(RDA5990_bt_dig_reset_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_dig_reset success!!! \n"); + msleep(5); //5ms + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_dig_reset failed! \n"); + return -1; + +} + + +int RDA5990_bt_dc_cal(void) +{ + unsigned int count = 0; + int ret = 0; + printk(KERN_INFO "rda_bt_dc_cal \n"); + + mutex_lock(&i2c_rw_lock); + + for(count = 0; count < sizeof(rda_5990_bt_dc_cal)/sizeof(rda_5990_bt_dc_cal[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_bt_dc_cal success!!! \n"); + msleep(200); //200ms + + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***rda_bt_dc_cal failed! \n"); + return -1; + +} + + + +/*houzhen update Mar 15 2012 + bypass RDA5990_bt_set_rf_switch when wf is already on + */ + +int RDA5990_bt_set_rf_switch(void) +{ + unsigned int count = 0; + int ret = 0; + u16 temp_data=0; + printk(KERN_INFO "RDA5990_bt_set_rf_switch \n"); + + + mutex_lock(&i2c_rw_lock); + + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0001); + if(ret) + goto err; + + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x31, &temp_data); + + if(ret) + goto err; + + if(temp_data&0x8000) // if wf is already on + { + + printk(KERN_INFO "wf already en, bypass RDA5990_bt_set_rf_switch function \n"); + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0000); + if(ret) + goto err; + mutex_unlock(&i2c_rw_lock); + return 0; + } + + for(count = 0; count < sizeof(rda_5990_bt_set_rf_switch_data)/sizeof(rda_5990_bt_set_rf_switch_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_set_rf_switch success!!! \n"); + msleep(50); //50ms + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_set_rf_switch failed! \n"); + return -1; + +} + + +/*houzhen update Mar 15 2012 + bypass RDA5990_bt_enable_clk when wf is already on + */ + +int RDA5990_bt_enable_clk(void) +{ + unsigned int count = 0; + int ret = 0; + u16 temp_data=0; + printk(KERN_INFO "RDA5990_bt_enable_clk \n"); + + mutex_lock(&i2c_rw_lock); + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0001); + if(ret) + goto err; + + ret = rda_gpio_i2c_read_1_addr_2_data(RDA_BT_RF_ADDR, 0x31, &temp_data); + + if(ret) + goto err; + + if(temp_data&0x8000) // if wf is already on + { + + printk(KERN_INFO "wf already en, bypass RDA5990_bt_enable_clk function \n"); + ret = rda_gpio_i2c_write_1_addr_2_data(RDA_BT_RF_ADDR, 0x3f, 0x0000); + if(ret) + goto err; + mutex_unlock(&i2c_rw_lock); + return 0; + } + + + for(count = 0; count < sizeof(RDA5990_bt_enable_clk_data)/sizeof(RDA5990_bt_enable_clk_data[0]); count ++) + { + 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]); + if(ret) + goto err; + } + + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_enable_clk success!!! \n"); + msleep(50); //50ms + return 0; + +err: + mutex_unlock(&i2c_rw_lock); + printk(KERN_INFO "***RDA5990_bt_enable_clk failed! \n"); + return -1; + +} + +extern void mt_combo_bgf_enable_irq(void); +extern void mt_combo_bgf_disable_irq(void); + + +#define RDA_BT_IOCTL_MAGIC 'u' +#define RDA_BT_POWER_ON_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x01) +#define RD_BT_RF_INIT_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x02) +#define RD_BT_DC_CAL_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x03) +#define RD_BT_SET_RF_SWITCH_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x04) +#define RDA_BT_POWER_OFF_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x05) +#define RDA_BT_EN_CLK _IO(RDA_BT_IOCTL_MAGIC ,0x06) +#define RD_BT_DC_DIG_RESET_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x07) + +#define RDA_WIFI_POWER_ON_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x10) +#define RDA_WIFI_POWER_OFF_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x11) +#define RDA_WIFI_POWER_SET_TEST_MODE_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x12) +#define RDA_WIFI_POWER_CANCEL_TEST_MODE_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x13) +#define RDA_WIFI_DEBUG_MODE_IOCTL _IO(RDA_BT_IOCTL_MAGIC ,0x14) + +static int rda_5990_pw_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch(cmd) + { + case RDA_WIFI_POWER_ON_IOCTL: + ret = rda_wifi_power_on(); + break; + + case RDA_WIFI_POWER_OFF_IOCTL: + ret = rda_wifi_power_off(); + break; + + case RDA_WIFI_POWER_SET_TEST_MODE_IOCTL: + wifi_in_test_mode = 1; + printk("****set rda wifi in test mode"); + break; + + case RDA_WIFI_POWER_CANCEL_TEST_MODE_IOCTL: + wifi_in_test_mode = 0; + printk("****set rda wifi in normal mode"); + break; + + case RDA_WIFI_DEBUG_MODE_IOCTL: + ret = rda_wifi_debug_en(); + break; + case RDA_BT_POWER_ON_IOCTL: + { + ret = rda_bt_power_on(); + mt_combo_bgf_enable_irq(); + } + break; + + /* should call thif function after bt_power_on*/ + case RDA_BT_EN_CLK: + ret = RDA5990_bt_enable_clk(); + break; + + case RD_BT_RF_INIT_IOCTL: + ret = RDA5990_bt_rf_init(); + break; + + case RD_BT_DC_CAL_IOCTL: + ret = RDA5990_bt_dc_cal(); + break; + + case RD_BT_DC_DIG_RESET_IOCTL: + ret = RDA5990_bt_dig_reset(); + break; + + case RD_BT_SET_RF_SWITCH_IOCTL: + ret = RDA5990_bt_set_rf_switch(); + break; + + case RDA_BT_POWER_OFF_IOCTL: + { + mt_combo_bgf_disable_irq(); + ret = rda_bt_power_off(); + } + break; + + default: + break; + } + + printk(KERN_INFO "rda_bt_pw_ioctl cmd=0x%02x \n", cmd); + + return ret; +} + +static int rda_5990_major; +static struct class *rda_5990_class = NULL; +static const struct file_operations rda_5990_operations = { + .owner = THIS_MODULE, + .unlocked_ioctl = rda_5990_pw_ioctl, + .release = NULL +}; + +void rda_5990_sleep_worker_task(struct work_struct *work) +{ + printk("---rda_5990_sleep_worker_task end"); + wake_unlock(&rda_5990_wake_lock); +} + +void rda_5990_set_wake_lock(void) +{ + wake_lock(&rda_5990_wake_lock); + cancel_delayed_work(&rda_5990_sleep_worker); + schedule_delayed_work(&rda_5990_sleep_worker, 6*HZ); +} + +int rda_5990_power_ctrl_init(void) +{ + int ret = 0; + + printk("rda_5990_power_ctrl_init begin\n"); + + rda_5990_major = register_chrdev(0, "rda5990_power_ctrl", &rda_5990_operations); + if(rda_5990_major < 0) + { + printk(KERN_INFO "register rdabt_power_ctrl failed!!! \n"); + return rda_5990_major; + } + + rda_5990_class = class_create(THIS_MODULE, "rda_combo"); + if(IS_ERR(rda_5990_class)) + { + unregister_chrdev(rda_5990_major, "rdabt_power_ctrl"); + return PTR_ERR(rda_5990_class); + } + + device_create(rda_5990_class, NULL, MKDEV(rda_5990_major, 0), NULL, "rdacombo"); + + + INIT_DELAYED_WORK(&rda_5990_sleep_worker, rda_5990_sleep_worker_task); + wake_lock_init(&rda_5990_wake_lock, WAKE_LOCK_SUSPEND, "RDA_sleep_worker_wake_lock"); + + mutex_init(&i2c_rw_lock); + printk("rda_5990_power_ctrl_init end\n"); + return 0; +} + +void rda_5990_power_ctrl_exit(void) +{ + + unregister_chrdev(rda_5990_major, "rdabt_power_ctrl"); + if(rda_5990_class) + class_destroy(rda_5990_class); + + cancel_delayed_work_sync(&rda_5990_sleep_worker); + wake_lock_destroy(&rda_5990_wake_lock); +} + +unsigned char rda_5990_wifi_in_test_mode(void) +{ + return wifi_in_test_mode; +} + + +static __inline__ void cfmakeraw(struct termios *s) +{ + s->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + s->c_oflag &= ~OPOST; + s->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + s->c_cflag &= ~(CSIZE|PARENB); + s->c_cflag |= CS8; +} + +static __inline__ int cfsetospeed(struct termios *s, speed_t speed) +{ + s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD); + return 0; +} + +static __inline__ int cfsetispeed(struct termios *s, speed_t speed) +{ + s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD); + return 0; +} + +static int rda_wifi_init_uart(char *dev) +{ + int errno; + struct termios ti; + struct serial_struct ss; + int fd; + + fd = sys_open(dev, O_RDWR | O_NOCTTY, 0); + if (fd < 0) { + printk("Can't open serial port"); + return -1; + } + + sys_ioctl(fd, TCFLSH, TCIOFLUSH); + + /* Clear the cust flag */ + if((errno = sys_ioctl(fd, TIOCGSERIAL, &ss))<0){ + printk("BAUD: error to get the serial_struct info:%s\n", errno); + goto err; + } + + if (ss.flags & ASYNC_SPD_CUST) { + printk("clear ASYNC_SPD_CUST\r\n"); + ss.flags &= ~ASYNC_SPD_CUST; + } + if((errno = sys_ioctl(fd, TIOCSSERIAL, &ss))<0){ + printk("BAUD: error to set serial_struct:%s\n", errno); + goto err; + } + + if ((errno = sys_ioctl(fd, TCGETS, (long)&ti)) < 0) { + printk("unable to get UART port setting"); + printk("Can't get port settings"); + goto err; + } + + cfmakeraw(&ti); + + ti.c_cflag |= CLOCAL; + ti.c_cflag &= ~CRTSCTS; + ti.c_lflag = 0; + ti.c_cc[VTIME] = 5; /* 0.5 sec */ + ti.c_cc[VMIN] = 0; + + /* Set initial baudrate */ + cfsetospeed(&ti, B115200); + cfsetispeed(&ti, B115200); + + if ((errno = sys_ioctl(fd, TCSETS, (long)&ti)) < 0) { + printk("unable to set UART port setting"); + printk("Can't set port settings"); + goto err; + } + + errno = sys_ioctl(fd, TCFLSH, TCIOFLUSH); + if(errno < 0) + goto err; + + return fd; + +err: + if(fd > 0) + sys_close(fd); + + return -1; +} + +EXPORT_SYMBOL(rda_wlan_version); +EXPORT_SYMBOL(rda_wifi_init_uart); +EXPORT_SYMBOL(rda_5990_wifi_in_test_mode); + +EXPORT_SYMBOL(rda_5990_set_wake_lock); +EXPORT_SYMBOL(rda_wifi_power_off); +EXPORT_SYMBOL(rda_wifi_power_on); +EXPORT_SYMBOL(rda_fm_power_on); +EXPORT_SYMBOL(rda_fm_power_off); + +module_init(rda_5990_power_ctrl_init); +module_exit(rda_5990_power_ctrl_exit); + +MODULE_LICENSE("GPL"); + diff --git a/drivers/net/wireless/rda5990/rda_gpio_i2c/Makefile b/drivers/net/wireless/rda5990/rda_gpio_i2c/Makefile new file mode 100755 index 000000000000..9a192865bdbf --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_gpio_i2c/Makefile @@ -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 index 000000000000..01a4277b0290 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_gpio_i2c/rda_gpio_i2c.c @@ -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 +#include +#include + +#include +#include + +#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, ®Value); + 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, ®Value); + 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, ®Value); + 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 index 000000000000..e8c121fbc5bc --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/Makefile @@ -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 index 000000000000..92c6886421e2 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/Makefile_gwl @@ -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 index 000000000000..2ab7b1f0297e --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.c @@ -0,0 +1,593 @@ +#include +#include +#include +#include +#include +#include +#include + +#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;iprivate_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; ihw_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;ihw_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; ihw_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;ihw_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; idebugfs_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; idebugfs_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 index 000000000000..b42bc4e410c6 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_debugfs.h @@ -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 index 000000000000..ef8e114b006a --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_defs.h @@ -0,0 +1,71 @@ +#ifndef _RDA5890_DEFS_H_ +#define _RDA5890_DEFS_H_ + +#include + +#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 index 000000000000..8bea49c2a084 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.c @@ -0,0 +1,633 @@ +/** + * This file contains the major functions in WLAN + * driver. It includes init, exit, open, close and main + * thread etc.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + + +#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 index 000000000000..1f3656733be8 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_dev.h @@ -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 +#include +#include +#include +#include +#include +#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 index 000000000000..ff738d2f17e5 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.c @@ -0,0 +1,795 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000000..6c5a7583c4da --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_if_sdio.h @@ -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 index 000000000000..0dc1d4e22799 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_ioctl.h @@ -0,0 +1,17 @@ +#ifndef _RDA5890_IOCTL_H_ +#define _RDA5890_IOCTL_H_ + +#include "linux/sockios.h" + +#define IOCTL_RDA5890_BASE SIOCDEVPRIVATE +#define IOCTL_RDA5890_GET_MAGIC IOCTL_RDA5890_BASE +#define IOCTL_RDA5890_GET_DRIVER_VER (IOCTL_RDA5890_BASE + 1) +#define IOCTL_RDA5890_MAC_GET_FW_VER (IOCTL_RDA5890_BASE + 2) +#define IOCTL_RDA5890_MAC_WID (IOCTL_RDA5890_BASE + 3) +#define IOCTL_RDA5890_SET_WAPI_ASSOC_IE (IOCTL_RDA5890_BASE + 4) +#define IOCTL_RDA5890_GET_WAPI_ASSOC_IE (IOCTL_RDA5890_BASE + 5) + +#define RDA5890_MAGIC 0x5890face + +#endif /* _RDA5890_IOCTL_H_ */ + 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 index 000000000000..fb93a2e74b75 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_nvram.c @@ -0,0 +1,108 @@ +#include +#include + +#define WIFI_NVRAM_FILE_NAME "/data/nvram/APCFG/APRDEB/RDAWIFI" + +static int nvram_read(char *filename, char *buf, ssize_t len, int offset) +{ + struct file *fd; + //ssize_t ret; + int retLen = -1; + + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + + fd = filp_open(filename, O_WRONLY|O_CREAT, 0644); + + if(IS_ERR(fd)) { + printk("[rda5890][nvram_read] : failed to open!!\n"); + return -1; + } + do{ + if ((fd->f_op == NULL) || (fd->f_op->read == NULL)) + { + printk("[rda5890][nvram_read] : file can not be read!!\n"); + break; + } + + if (fd->f_pos != offset) { + if (fd->f_op->llseek) { + if(fd->f_op->llseek(fd, offset, 0) != offset) { + printk("[rda5890][nvram_read] : failed to seek!!\n"); + break; + } + } else { + fd->f_pos = offset; + } + } + + retLen = fd->f_op->read(fd, + buf, + len, + &fd->f_pos); + + }while(false); + + filp_close(fd, NULL); + + set_fs(old_fs); + + return retLen; +} + +static int nvram_write(char *filename, char *buf, ssize_t len, int offset) +{ + struct file *fd; + int retLen = -1; + + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + + fd = filp_open(filename, O_WRONLY|O_CREAT, 0644); + + if(IS_ERR(fd)) { + printk("[rda5890][nvram_write] : failed to open!!\n"); + return -1; + } + do{ + if ((fd->f_op == NULL) || (fd->f_op->write == NULL)) + { + printk("[rda5890][nvram_write] : file can not be write!!\n"); + break; + } /* End of if */ + + if (fd->f_pos != offset) { + if (fd->f_op->llseek) { + if(fd->f_op->llseek(fd, offset, 0) != offset) { + printk("[rda5890][nvram_write] : failed to seek!!\n"); + break; + } + } else { + fd->f_pos = offset; + } + } + + retLen = fd->f_op->write(fd, + buf, + len, + &fd->f_pos); + + }while(false); + + filp_close(fd, NULL); + + set_fs(old_fs); + + return retLen; +} + +int rda5890_read_mac(char* buf) +{ + return nvram_read(WIFI_NVRAM_FILE_NAME, buf, 6, 0); +} + +int rda5890_write_mac(char * buf) +{ + return nvram_write(WIFI_NVRAM_FILE_NAME, buf, 6, 0); +} + 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 index 000000000000..207595cf5a96 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_scan.c @@ -0,0 +1,369 @@ +#include +#include +#include +#include +#include + + +#include "rda5890_dev.h" +#include "rda5890_defs.h" + +//#define SCAN_RESULT_DEBUG + +//added by xiongzhi for wapi +#ifndef FCS_LEN +#define FCS_LEN 4 +#endif + +/* 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; + +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) +{ + union iwreq_data wrqu; + struct bss_descriptor *iter_bss; + struct bss_descriptor *safe; + struct bss_descriptor bss_desc; + struct bss_descriptor * bss = &bss_desc; + + unsigned char *pos, *end, *p; + unsigned char n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0; + struct ieee_ie_country_info_set *pcountryinfo; + int ret; + unsigned char* msa = &info[9]; + unsigned short msa_len = info[6] | (info[7] << 8); + + if(!priv->scan_running) + goto done; + + if((msa_len - 1 + 9 ) != info_len) + { + RDA5890_ERRP("rda5890_network_information verify lengh feild failed \n"); + } + + memset(bss, 0, sizeof (struct bss_descriptor)); + bss->rssi = info[8]; + msa_len -= 1; // has rssi + + get_BSSID(msa, bss->bssid); + + end = msa + msa_len; + + //mac head + pos = msa + 24; + //time stamp + pos += 8; + //beacon + bss->beaconperiod = *(pos) | (*(pos + 1) << 8); + pos += 2 ; + //capability + bss->capability = *(pos) | (*(pos + 1) << 8); + pos += 2; + + if (bss->capability & WLAN_CAPABILITY_IBSS) + bss->mode = IW_MODE_ADHOC; + else + bss->mode = IW_MODE_INFRA; + + /* process variable IE */ + while (pos + 2 <= end) { + if (pos + pos[1] > end) { +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("process_bss: error in processing IE, " + "bytes left < IE length\n"); +#endif + break; + } + + switch (pos[0]) { + case WLAN_EID_SSID: + bss->ssid_len = min_t(int, IEEE80211_MAX_SSID_LEN, pos[1]); + memcpy(bss->ssid, pos + 2, bss->ssid_len); +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got SSID IE: '%s', len %u %d\n", + bss->ssid, + bss->ssid_len, pos[1]); +#endif + break; + + case WLAN_EID_SUPP_RATES: + n_basic_rates = min_t(uint8_t, MAX_RATES, pos[1]); + memcpy(bss->rates, pos + 2, n_basic_rates); + got_basic_rates = 1; +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got RATES IE\n"); +#endif + break; + + case WLAN_EID_FH_PARAMS: +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got FH IE\n"); +#endif + break; + + case WLAN_EID_DS_PARAMS: +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got DS IE\n"); +#endif + break; + + case WLAN_EID_CF_PARAMS: +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got CF IE\n"); +#endif + break; + + case WLAN_EID_IBSS_PARAMS: +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got IBSS IE\n"); +#endif + break; + + case WLAN_EID_COUNTRY: + pcountryinfo = (struct ieee_ie_country_info_set *) pos; +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got COUNTRY IE\n"); +#endif + break; + + case WLAN_EID_EXT_SUPP_RATES: + /* only process extended supported rate if data rate is + * already found. Data rate IE should come before + * extended supported rate IE + */ +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got RATESEX IE\n"); +#endif + if (!got_basic_rates) { +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("... but ignoring it\n"); +#endif + break; + } + + n_ex_rates = pos[1]; + if (n_basic_rates + n_ex_rates > MAX_RATES) + n_ex_rates = MAX_RATES - n_basic_rates; + + p = bss->rates + n_basic_rates; + memcpy(p, pos + 2, n_ex_rates); + break; + + case WLAN_EID_GENERIC: + if (pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && + pos[4] == 0xf2 && pos[5] == 0x01) { + bss->wpa_ie_len = min(pos[1] + 2, MAX_WPA_IE_LEN); + memcpy(bss->wpa_ie, pos, bss->wpa_ie_len); +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got WPA IE \n"); +#endif + } + else { +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got generic IE: %02x:%02x:%02x:%02x, len %d\n", + pos[2], pos[3], + pos[4], pos[5], + pos[1]); +#endif + } + break; + + case WLAN_EID_RSN: +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got RSN IE\n"); +#endif + bss->rsn_ie_len = min(pos[1] + 2, MAX_WPA_IE_LEN); + memcpy(bss->rsn_ie, pos, bss->rsn_ie_len); + break; + + case IWAPI: +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("got WAPI IE\n"); +#endif + bss->wapi_ie_len = min(pos[1] + 2, 100); + memcpy(bss->wapi_ie, pos, bss->wapi_ie_len); + break; + + default: + break; + } + + pos += pos[1] + 2; + } + + bss->last_scanned = jiffies; + + /* add scaned bss into list */ + if (1) { + struct bss_descriptor *found = NULL; + struct bss_descriptor *oldest = NULL; + + /* 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, bss)) { + 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->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); + /* 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)); + } + +done: +#ifdef SCAN_RESULT_DEBUG + RDA5890_DBGP("rda5890_network_information ret %d \n", ret); +#endif + return; +} + + + 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 index 000000000000..f72d44fc69c7 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_sdio_patch.c @@ -0,0 +1,1406 @@ +#include "rda5890_dev.h" +#include "rda5890_wid.h" +#include "rda5890_defs.h" + +#ifdef WIFI_TEST_MODE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, ¶); + 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, ¶); + 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 index 000000000000..ad01fea20b1d --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000000..6f161b90e384 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_txrx.h @@ -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 index 000000000000..ae06196773c3 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.c @@ -0,0 +1,2562 @@ +/** + * This file contains ioctl functions + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 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 index 000000000000..35ff2f7c8780 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_wext.h @@ -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 index 000000000000..1a1fc1d6de83 --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.c @@ -0,0 +1,1957 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000000..7cf712f4c4cb --- /dev/null +++ b/drivers/net/wireless/rda5990/rda_wlan/rda5890_wid.h @@ -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 + -- 2.34.1