diff options
Diffstat (limited to 'drivers/net/wireless/bcmdhd/src/wl/sys/wl_android.c')
-rw-r--r-- | drivers/net/wireless/bcmdhd/src/wl/sys/wl_android.c | 1469 |
1 files changed, 1469 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd/src/wl/sys/wl_android.c b/drivers/net/wireless/bcmdhd/src/wl/sys/wl_android.c new file mode 100644 index 0000000..7f1b86c --- /dev/null +++ b/drivers/net/wireless/bcmdhd/src/wl/sys/wl_android.c @@ -0,0 +1,1469 @@ +/* + * Linux cfg80211 driver - Android related functions + * + * Copyright (C) 1999-2012, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_android.c 309571 2012-01-20 01:45:10Z $ + */ + +#include <linux/module.h> +#include <linux/netdevice.h> + +#include <wl_android.h> +#include <wldev_common.h> +#include <wlioctl.h> +#include <bcmutils.h> +#include <linux_osl.h> +#include <dhd_dbg.h> +#include <dngl_stats.h> +#include <dhd.h> +#include <bcmsdbus.h> +#ifdef WL_CFG80211 +#include <wl_cfg80211.h> +#endif +#if defined(CONFIG_WIFI_CONTROL_FUNC) +#include <linux/platform_device.h> +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) +#include <linux/wlan_plat.h> +#else +#include <linux/wifi_tiwlan.h> +#endif +#endif /* CONFIG_WIFI_CONTROL_FUNC */ + +/* + * Android private command strings, PLEASE define new private commands here + * so they can be updated easily in the future (if needed) + */ + +#define CMD_START "START" +#define CMD_STOP "STOP" +#define CMD_SCAN_ACTIVE "SCAN-ACTIVE" +#define CMD_SCAN_PASSIVE "SCAN-PASSIVE" +#define CMD_RSSI "RSSI" +#define CMD_LINKSPEED "LINKSPEED" +#define CMD_RXFILTER_START "RXFILTER-START" +#define CMD_RXFILTER_STOP "RXFILTER-STOP" +#define CMD_RXFILTER_ADD "RXFILTER-ADD" +#define CMD_RXFILTER_REMOVE "RXFILTER-REMOVE" +#define CMD_BTCOEXSCAN_START "BTCOEXSCAN-START" +#define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" +#define CMD_BTCOEXMODE "BTCOEXMODE" +#define CMD_SETSUSPENDOPT "SETSUSPENDOPT" +#define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR" +#define CMD_SETFWPATH "SETFWPATH" +#define CMD_SETBAND "SETBAND" +#define CMD_GETBAND "GETBAND" +#define CMD_COUNTRY "COUNTRY" +#define CMD_P2P_SET_NOA "P2P_SET_NOA" +#define CMD_P2P_GET_NOA "P2P_GET_NOA" +#define CMD_P2P_SET_PS "P2P_SET_PS" +#define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" + + +/* Hostapd private command */ +#define CMD_SET_HAPD_AUTO_CHANNEL "HAPD_AUTO_CHANNEL" +#define CMD_SET_HAPD_MAX_NUM_STA "HAPD_MAX_NUM_STA" +#define CMD_SET_HAPD_SSID "HAPD_SSID" +#define CMD_SET_HAPD_HIDE_SSID "HAPD_HIDE_SSID" +#define CMD_HAPD_STA_DISASSOC "HAPD_STA_DISASSOC" +#ifdef BCMCCX +#define CMD_GETCCKM_RN "get cckm_rn" +#define CMD_SETCCKM_KRK "set cckm_krk" +#define CMD_GET_ASSOC_RES_IES "get assoc_res_ies" +#endif + +#ifdef PNO_SUPPORT +#define CMD_PNOSSIDCLR_SET "PNOSSIDCLR" +#define CMD_PNOSETUP_SET "PNOSETUP " +#define CMD_PNOENABLE_SET "PNOFORCE" +#define CMD_PNODEBUG_SET "PNODEBUG" + +#define PNO_TLV_PREFIX 'S' +#define PNO_TLV_VERSION '1' +#define PNO_TLV_SUBVERSION '2' +#define PNO_TLV_RESERVED '0' +#define PNO_TLV_TYPE_SSID_IE 'S' +#define PNO_TLV_TYPE_TIME 'T' +#define PNO_TLV_FREQ_REPEAT 'R' +#define PNO_TLV_FREQ_EXPO_MAX 'M' + +typedef struct cmd_tlv { + char prefix; + char version; + char subver; + char reserved; +} cmd_tlv_t; +#endif /* PNO_SUPPORT */ + +#ifdef ROAM_API +#define CMD_ROAMTRIGGER_SET "SETROAMTRIGGER" +#define CMD_ROAMTRIGGER_GET "GETROAMTRIGGER" +#define CMD_ROAMDELTA_SET "SETROAMDELTA" +#define CMD_ROAMDELTA_GET "GETROAMDELTA" +#define CMD_ROAMSCANPERIOD_SET "SETROAMSCANPERIOD" +#define CMD_ROAMSCANPERIOD_GET "GETROAMSCANPERIOD" +#define CMD_COUNTRYREV_SET "SETCOUNTRYREV" +#define CMD_COUNTRYREV_GET "GETCOUNTRYREV" +#endif /* ROAM_API */ + +#ifdef OKC_SUPPORT +#define CMD_OKC_SET_PMK "SET_PMK" +#define CMD_OKC_ENABLE "OKC_ENABLE" +#endif + +#ifdef SUPPORT_AMPDU_MPDU_CMD +#define CMD_AMPDU_MPDU "AMPDU_MPDU" +#endif + +#ifdef VSDB +#define CMD_CHANGE_RL "CHANGE_RL" +#define CMD_RESTORE_RL "RESTORE_RL" +#endif +typedef struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; + +/** + * Extern function declarations (TODO: move them to dhd_linux.h) + */ +void dhd_customer_gpio_wlan_ctrl(int onoff); +uint dhd_dev_reset(struct net_device *dev, uint8 flag); +void dhd_dev_init_ioctl(struct net_device *dev); +#ifdef WL_CFG80211 +int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); +int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); +#else +int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) +{ return 0; } +int wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) +{ return 0; } +int wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len) +{ return 0; } +int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) +{ return 0; } +#endif +extern int dhd_os_check_if_up(void *dhdp); +extern void *bcmsdh_get_drvdata(void); + +extern bool ap_fw_loaded; +#if defined(CUSTOMER_HW2) || defined(CUSTOMER_HW_SAMSUNG) +extern char iface_name[IFNAMSIZ]; +#endif + +/** + * Local (static) functions and variables + */ + +/* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first + * time (only) in dhd_open, subsequential wifi on will be handled by + * wl_android_wifi_on + */ +static int g_wifi_on = TRUE; + +/** + * Local (static) function definitions + */ +static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len) +{ + int link_speed; + int bytes_written; + int error; + + error = wldev_get_link_speed(net, &link_speed); + if (error) + return -1; + + /* Convert Kbps to Android Mbps */ + link_speed = link_speed / 1000; + bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed); + DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command)); + return bytes_written; +} + +static int wl_android_get_rssi(struct net_device *net, char *command, int total_len) +{ + wlc_ssid_t ssid = {0}; + int rssi; + int bytes_written = 0; + int error; + + error = wldev_get_rssi(net, &rssi); + if (error) + return -1; + + error = wldev_get_ssid(net, &ssid); + if (error) + return -1; + if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) { + DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__)); + } else { + memcpy(command, ssid.SSID, ssid.SSID_len); + bytes_written = ssid.SSID_len; + } + bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi); + DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written)); + return bytes_written; +} + +static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len) +{ + int suspend_flag; + int ret_now; + int ret = 0; + +#ifdef CUSTOMER_HW_SAMSUNG + if (!dhd_download_fw_on_driverload) { +#endif /* CUSTOMER_HW_SAMSUNG */ + suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + ret_now = net_os_set_suspend_disable(dev, suspend_flag); + + if (ret_now != suspend_flag) { + if (!(ret = net_os_set_suspend(dev, ret_now))) + DHD_INFO(("%s: Suspend Flag %d -> %d\n", + __FUNCTION__, ret_now, suspend_flag)); + else + DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); + } +#ifdef CUSTOMER_HW_SAMSUNG + } +#endif /* CUSTOMER_HW_SAMSUNG */ + return ret; +} + +static int wl_android_get_band(struct net_device *dev, char *command, int total_len) +{ + uint band; + int bytes_written; + int error; + + error = wldev_get_band(dev, &band); + if (error) + return -1; + bytes_written = snprintf(command, total_len, "Band %d", band); + return bytes_written; +} + +#ifdef ROAM_API +int wl_android_set_roam_trigger( + struct net_device *dev, char* command, int total_len) +{ + int roam_trigger[2]; + + sscanf(command, "%*s %d", &roam_trigger[0]); + roam_trigger[1] = WLC_BAND_ALL; + + return wldev_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger, + sizeof(roam_trigger), 1); +} + +static int wl_android_get_roam_trigger( + struct net_device *dev, char *command, int total_len) +{ + int bytes_written; + int roam_trigger[2] = {0, 0}; + + roam_trigger[1] = WLC_BAND_2G; + if (wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, + sizeof(roam_trigger), 0)) { + roam_trigger[1] = WLC_BAND_5G; + if (wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, + sizeof(roam_trigger), 0)) + return -1; + } + + bytes_written = snprintf(command, total_len, "%s %d", + CMD_ROAMTRIGGER_GET, roam_trigger[0]); + + return bytes_written; +} + +int wl_android_set_roam_delta( + struct net_device *dev, char* command, int total_len) +{ + int roam_delta[2]; + + sscanf(command, "%*s %d", &roam_delta[0]); + roam_delta[1] = WLC_BAND_ALL; + + return wldev_ioctl(dev, WLC_SET_ROAM_DELTA, roam_delta, + sizeof(roam_delta), 1); +} + +static int wl_android_get_roam_delta( + struct net_device *dev, char *command, int total_len) +{ + int bytes_written; + int roam_delta[2] = {0, 0}; + + roam_delta[1] = WLC_BAND_2G; + if (wldev_ioctl(dev, WLC_GET_ROAM_DELTA, roam_delta, + sizeof(roam_delta), 0)) { + roam_delta[1] = WLC_BAND_5G; + if (wldev_ioctl(dev, WLC_GET_ROAM_DELTA, roam_delta, + sizeof(roam_delta), 0)) + return -1; + } + + bytes_written = snprintf(command, total_len, "%s %d", + CMD_ROAMDELTA_GET, roam_delta[0]); + + return bytes_written; +} + +int wl_android_set_roam_scan_period( + struct net_device *dev, char* command, int total_len) +{ + int roam_scan_period = 0; + + sscanf(command, "%*s %d", &roam_scan_period); + return wldev_ioctl(dev, WLC_SET_ROAM_SCAN_PERIOD, &roam_scan_period, + sizeof(roam_scan_period), 1); +} + +static int wl_android_get_roam_scan_period( + struct net_device *dev, char *command, int total_len) +{ + int bytes_written; + int roam_scan_period = 0; + + if (wldev_ioctl(dev, WLC_GET_ROAM_SCAN_PERIOD, &roam_scan_period, + sizeof(roam_scan_period), 0)) + return -1; + + bytes_written = snprintf(command, total_len, "%s %d", + CMD_ROAMSCANPERIOD_GET, roam_scan_period); + + return bytes_written; +} + +int wl_android_set_country_rev( + struct net_device *dev, char* command, int total_len) +{ + int error = 0; + wl_country_t cspec = {{0}, 0, {0} }; + char country_code[WLC_CNTRY_BUF_SZ]; + char smbuf[WLC_IOCTL_SMLEN]; + int rev = 0; + + memset(country_code, 0, sizeof(country_code)); + sscanf(command+sizeof("SETCOUNTRYREV"), "%s %d", country_code, &rev); + WL_TRACE(("%s: country_code = %s, rev = %d\n", __func__, + country_code, rev)); + + memcpy(cspec.country_abbrev, country_code, sizeof(country_code)); + memcpy(cspec.ccode, country_code, sizeof(country_code)); + cspec.rev = rev; + + error = wldev_iovar_setbuf(dev, "country", (char *)&cspec, + sizeof(cspec), smbuf, sizeof(smbuf), NULL); + + if (error) { + DHD_ERROR(("%s: set country '%s/%d' failed code %d\n", + __func__, cspec.ccode, cspec.rev, error)); + } else { + dhd_bus_country_set(dev, &cspec); + DHD_INFO(("%s: set country '%s/%d'\n", + __func__, cspec.ccode, cspec.rev)); + } + + return error; +} + +static int wl_android_get_country_rev( + struct net_device *dev, char *command, int total_len) +{ + int error; + int bytes_written; + char smbuf[WLC_IOCTL_SMLEN]; + wl_country_t cspec; + + error = wldev_iovar_getbuf(dev, "country", &cspec, sizeof(cspec), smbuf, + sizeof(smbuf), NULL); + + if (error) { + DHD_ERROR(("%s: get country failed code %d\n", + __func__, error)); + return -1; + } else { + DHD_INFO(("%s: get country '%s %d'\n", __func__, smbuf, smbuf[WLC_CNTRY_BUF_SZ])); + } + bytes_written = snprintf(command, total_len, "%s %s %d", CMD_COUNTRYREV_GET, smbuf, smbuf[WLC_CNTRY_BUF_SZ]); + return bytes_written; +} +#endif /* ROAM_API */ + +#ifdef PNO_SUPPORT +static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) +{ + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + int res = -1; + int nssid = 0; + cmd_tlv_t *cmd_tlv_temp; + char *str_ptr; + int tlv_size_left; + int pno_time = 0; + int pno_repeat = 0; + int pno_freq_expo_max = 0; + +#ifdef PNO_SET_DEBUG + int i; + char pno_in_example[] = { + 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', + 'S', '1', '2', '0', + 'S', + 0x05, + 'd', 'l', 'i', 'n', 'k', + 'S', + 0x04, + 'G', 'O', 'O', 'G', + 'T', + '0', 'B', + 'R', + '2', + 'M', + '2', + 0x00 + }; +#endif /* PNO_SET_DEBUG */ + + DHD_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { + DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); + goto exit_proc; + } + + +#ifdef PNO_SET_DEBUG + memcpy(command, pno_in_example, sizeof(pno_in_example)); + for (i = 0; i < sizeof(pno_in_example); i++) + printf("%02X ", command[i]); + printf("\n"); + total_len = sizeof(pno_in_example); +#endif + + str_ptr = command + strlen(CMD_PNOSETUP_SET); + tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET); + + cmd_tlv_temp = (cmd_tlv_t *)str_ptr; + memset(ssids_local, 0, sizeof(ssids_local)); + + if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && + (cmd_tlv_temp->version == PNO_TLV_VERSION) && + (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) { + + str_ptr += sizeof(cmd_tlv_t); + tlv_size_left -= sizeof(cmd_tlv_t); + + if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, + MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) { + DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); + goto exit_proc; + } else { + if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) { + DHD_ERROR(("%s scan duration corrupted field size %d\n", + __FUNCTION__, tlv_size_left)); + goto exit_proc; + } + str_ptr++; + pno_time = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); + + if (str_ptr[0] != 0) { + if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { + DHD_ERROR(("%s pno repeat : corrupted field\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); + if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { + DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s: pno_freq_expo_max=%d\n", + __FUNCTION__, pno_freq_expo_max)); + } + } + } else { + DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); + goto exit_proc; + } + + res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max); + +exit_proc: + return res; +} +#endif /* PNO_SUPPORT */ + +static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len) +{ + int ret; + int bytes_written = 0; + + ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command); + if (ret) + return 0; + bytes_written = sizeof(struct ether_addr); + return bytes_written; +} +#ifdef BCMCCX +static int wl_android_get_cckm_rn(struct net_device *dev, char *command) +{ + int error, rn; + + WL_TRACE(("%s:wl_android_get_cckm_rn\n", dev->name)); + + error = wldev_iovar_getint(dev, "cckm_rn", &rn); + if (unlikely(error)) { + WL_ERR(("wl_android_get_cckm_rn error (%d)\n", error)); + return -1; + } + //WL_ERR(("wl_android_get_cckm_rn = %d\n", rn)); + memcpy(command, &rn, sizeof(int)); + + return sizeof(int); +} + +static int wl_android_set_cckm_krk(struct net_device *dev, char *command) +{ + int error; + unsigned char key[16]; + + static char iovar_buf[WLC_IOCTL_MEDLEN]; + + WL_TRACE(("%s: wl_iw_set_cckm_krk\n", dev->name)); + + memset(iovar_buf, 0, sizeof(iovar_buf)); + memcpy(key, command+strlen("set cckm_krk")+1, 16); + + error = wldev_iovar_setbuf(dev,"cckm_krk", key, sizeof(key), iovar_buf, WLC_IOCTL_MEDLEN, NULL); + if (unlikely(error)) + { + WL_ERR((" cckm_krk set error (%d)\n", error)); + return -1; + } + return 0; +} +static int wl_android_get_assoc_res_ies(struct net_device *dev, char *command) +{ + int error; + u8 buf[WL_ASSOC_INFO_MAX]; + wl_assoc_info_t assoc_info; + u32 resp_ies_len = 0; + int bytes_written = 0; + + WL_TRACE(("%s: wl_iw_get_assoc_res_ies\n", dev->name)); + + error = wldev_iovar_getbuf(dev, "assoc_info", NULL, 0, buf, WL_ASSOC_INFO_MAX, NULL); + if (unlikely(error)) { + WL_ERR(("could not get assoc info (%d)\n", error)); + return -1; + } + + memcpy(&assoc_info, buf, sizeof(wl_assoc_info_t)); + assoc_info.req_len = htod32(assoc_info.req_len); + assoc_info.resp_len = htod32(assoc_info.resp_len); + assoc_info.flags = htod32(assoc_info.flags); + + if (assoc_info.resp_len) { + resp_ies_len = assoc_info.resp_len - sizeof(struct dot11_assoc_resp); + } + + /* first 4 bytes are ie len */ + memcpy(command, &resp_ies_len, sizeof(u32)); + bytes_written= sizeof(u32); + + /* get the association resp IE's if there are any */ + if (resp_ies_len) { + error = wldev_iovar_getbuf(dev, "assoc_resp_ies", NULL, 0, buf, WL_ASSOC_INFO_MAX, NULL); + if (unlikely(error)) { + WL_ERR(("could not get assoc resp_ies (%d)\n", error)); + return -1; + } + + memcpy(command+sizeof(u32), buf, resp_ies_len); + bytes_written += resp_ies_len; + } + return bytes_written; +} + +#endif /* BCMCCX */ + +/** + * Global function definitions (declared in wl_android.h) + */ + +int wl_android_wifi_on(struct net_device *dev) +{ + int ret = 0; + int retry = POWERUP_MAX_RETRY; + + DHD_ERROR(("%s in\n", __FUNCTION__)); + if (!dev) { + DHD_ERROR(("%s: dev is null\n", __FUNCTION__)); + return -EINVAL; + } + + dhd_net_if_lock(dev); + if (!g_wifi_on) { + do { + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); + if (dhd_download_fw_on_driverload) + msleep(300); + + ret = sdioh_start(NULL, 0); + if (ret == 0) + break; + DHD_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n", + retry+1)); + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + } while (retry-- >= 0); + if (ret != 0) { + DHD_ERROR(("\nfailed to power up wifi chip, max retry reached **\n\n")); + goto exit; + } + ret = dhd_dev_reset(dev, FALSE); + sdioh_start(NULL, 1); + dhd_dev_init_ioctl(dev); + g_wifi_on = TRUE; + } + +exit: + dhd_net_if_unlock(dev); + + return ret; +} + +int wl_android_wifi_off(struct net_device *dev) +{ + int ret = 0; + + DHD_ERROR(("%s in\n", __FUNCTION__)); + if (!dev) { + DHD_TRACE(("%s: dev is null\n", __FUNCTION__)); + return -EINVAL; + } + + dhd_net_if_lock(dev); + if (g_wifi_on) { + dhd_dev_reset(dev, 1); + if (dhd_download_fw_on_driverload) + msleep(100); + sdioh_stop(NULL); + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + g_wifi_on = FALSE; + } + dhd_net_if_unlock(dev); + + return ret; +} + +static int wl_android_set_fwpath(struct net_device *net, char *command, int total_len) +{ + if ((strlen(command) - strlen(CMD_SETFWPATH)) > MOD_PARAM_PATHLEN) + return -1; + bcm_strncpy_s(fw_path, sizeof(fw_path), + command + strlen(CMD_SETFWPATH) + 1, MOD_PARAM_PATHLEN - 1); + if (strstr(fw_path, "apsta") != NULL) { + DHD_INFO(("GOT APSTA FIRMWARE\n")); + ap_fw_loaded = TRUE; + } else { + DHD_INFO(("GOT STA FIRMWARE\n")); + ap_fw_loaded = FALSE; + } + return 0; +} + + +static int +wl_android_set_auto_channel(struct net_device *dev, const char* string_num, + char* command, int total_len) +{ + int channel; + int chosen = 0; + int retry = 0; + int ret = 0; + + /* Restrict channel to 1 - 7: 2GHz, 20MHz BW, No SB */ + u32 req_buf[8] = {7, 0x2B01, 0x2B02, 0x2B03, 0x2B04, 0x2B05, 0x2B06, + 0x2B07}; + + /* Auto channel select */ + wl_uint32_list_t request; + + channel = bcm_atoi(string_num); + DHD_INFO(("%s : HAPD_AUTO_CHANNEL = %d\n", __FUNCTION__, channel)); + + if (channel == 20) + ret = wldev_ioctl(dev, WLC_START_CHANNEL_SEL, (void *)&req_buf, + sizeof(req_buf), true); + else { /* channel == 0 */ + request.count = htod32(0); + ret = wldev_ioctl(dev, WLC_START_CHANNEL_SEL, (void *)&request, + sizeof(request), true); + } + + if (ret < 0) { + DHD_ERROR(("%s: can't start auto channel scan, err = %d\n", + __FUNCTION__, ret)); + channel = 0; + goto done; + } + + /* Wait for auto channel selection, max 2500 ms */ + bcm_mdelay(500); + + retry = 10; + while(retry--) { + ret = wldev_ioctl(dev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen), + false); + if (ret < 0 || dtoh32(chosen) == 0) { + DHD_INFO(("%s: %d tried, ret = %d, chosen = %d\n", + __FUNCTION__, (10 - retry), ret, chosen)); + bcm_mdelay(200); + } + else { + channel = (u16)chosen & 0x00FF; + DHD_ERROR(("%s: selected channel = %d\n", __FUNCTION__, channel)); + break; + } + } + + if (retry == 0) { + DHD_ERROR(("%s: auto channel timed out, failed\n", __FUNCTION__)); + channel = 0; + } + +done: +// snprintf(command, total_len, "%d", channel); + snprintf(command, 4, "%d", channel); + DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command)); + +// return 1; + return 4; +} + +static int +wl_android_set_max_num_sta(struct net_device *dev, const char* string_num) +{ + int max_assoc; + + max_assoc = bcm_atoi(string_num); + DHD_INFO(("%s : HAPD_MAX_NUM_STA = %d\n", __FUNCTION__, max_assoc)); + wldev_iovar_setint(dev, "maxassoc", max_assoc); + return 1; +} + +static int +wl_android_set_ssid (struct net_device *dev, const char* hapd_ssid) +{ + wlc_ssid_t ssid; + s32 ret; + + ssid.SSID_len = strlen(hapd_ssid); + bcm_strncpy_s(ssid.SSID, sizeof(ssid.SSID), hapd_ssid, ssid.SSID_len); + DHD_INFO(("%s: HAPD_SSID = %s\n", __FUNCTION__, ssid.SSID)); + ret = wldev_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(wlc_ssid_t), true); + if (ret < 0) { + DHD_ERROR(("%s : WLC_SET_SSID Error:%d\n", __FUNCTION__, ret)); + } + return 1; + +} + +static int +wl_android_set_hide_ssid(struct net_device *dev, const char* string_num) +{ + int hide_ssid; + int enable = 0; + + hide_ssid = bcm_atoi(string_num); + DHD_INFO(("%s: HAPD_HIDE_SSID = %d\n", __FUNCTION__, hide_ssid)); + if (hide_ssid) + enable = 1; + wldev_iovar_setint(dev, "closednet", enable); + return 1; +} + +static int +wl_android_sta_diassoc(struct net_device *dev, const char* straddr) +{ + scb_val_t scbval; + s32 ret; + + DHD_INFO(("%s: deauth STA %s\n", __FUNCTION__, straddr)); + + /* Unspecified reason */ + scbval.val = htod32(1); + bcm_ether_atoe(straddr, &scbval.ea); + + DHD_INFO(("%s: deauth STA: %02X:%02X:%02X:%02X:%02X:%02X\n", __FUNCTION__, + scbval.ea.octet[0], scbval.ea.octet[1], scbval.ea.octet[2], + scbval.ea.octet[3], scbval.ea.octet[4], scbval.ea.octet[5])); + + if ((ret = wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval, + sizeof(scb_val_t), true)) < 0) { + DHD_ERROR(("%s : WLC_SCB_DEAUTHENTICATE_FOR_REASON error:%d\n", __FUNCTION__ , ret)); + } + + return 1; +} + +#ifdef OKC_SUPPORT + +static int +wl_android_set_pmk(struct net_device *dev, char *command, int total_len) +{ + uchar pmk[33]; + int error = 0; + char smbuf[WLC_IOCTL_SMLEN]; +#ifdef OKC_DEBUG + int i = 0; +#endif + + bzero(pmk, sizeof(pmk)); + memcpy((char *)pmk, command + strlen("SET_PMK "), 32); + error = wldev_iovar_setbuf(dev, "okc_info_pmk", pmk, 32, smbuf, sizeof(smbuf), NULL); + if (error) { + DHD_ERROR(("Failed to set PMK for OKC, error = %d\n", error)); + } +#ifdef OKC_DEBUG + DHD_ERROR(("PMK is ")); + for (i = 0; i < 32; i++) + DHD_ERROR(("%02X ", pmk[i])); + + DHD_ERROR(("\n")); +#endif + return error; +} + +static int +wl_android_okc_enable(struct net_device *dev, char *command, int total_len) +{ + int error = 0; + char okc_enable = 0; + + okc_enable = command[strlen(CMD_OKC_ENABLE) + 1] - '0'; + error = wldev_iovar_setint(dev, "okc_enable", okc_enable); + if (error) { + DHD_ERROR(("Failed to %s OKC, error = %d\n", + okc_enable ? "enable" : "disable", error)); + } + + return error; +} + +#endif /* OKC_ SUPPORT */ +#ifdef VSDB +static int +wl_android_ch_res_rl(struct net_device *dev, bool change) +{ + int error = 0; + s32 srl = 7; + s32 lrl = 4; + printk("%s enter\n", __FUNCTION__); + if (change) { + srl = 4; + lrl = 2; + } + error = wldev_ioctl(dev, WLC_SET_SRL, &srl, + sizeof(s32), true); + if (error) { + DHD_ERROR(("Failed to set SRL, error = %d\n", error)); + } + error = wldev_ioctl(dev, WLC_SET_LRL, &lrl, + sizeof(s32), true); + if (error) { + DHD_ERROR(("Failed to set LRL, error = %d\n", error)); + } + return error; +} +#endif + +#ifdef SUPPORT_AMPDU_MPDU_CMD +/* CMD_AMPDU_MPDU */ +static int +wl_android_set_ampdu_mpdu(struct net_device *dev, const char* string_num) +{ + int err = 0; + int ampdu_mpdu; + + //ampdu_mpdu = my_atoi(string_num); + ampdu_mpdu = bcm_atoi(string_num); + + if (ampdu_mpdu > 32) { + DHD_ERROR(("%s : ampdu_mpdu MAX value is 32.\n", __FUNCTION__)); + return -1; + } + DHD_ERROR(("%s : ampdu_mpdu = %d\n", __FUNCTION__, ampdu_mpdu)); + err = wldev_iovar_setint(dev, "ampdu_mpdu", ampdu_mpdu); + if (err < 0) { + DHD_ERROR(("%s : ampdu_mpdu set error. %d\n", __FUNCTION__, err)); + return -1; + } + + return 0; +} +#endif + +int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) +{ + int ret = 0; + char *command = NULL; + int bytes_written = 0; + android_wifi_priv_cmd priv_cmd; + + net_os_wake_lock(net); + + if (!ifr->ifr_data) { + ret = -EINVAL; + goto exit; + } + if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { + ret = -EFAULT; + goto exit; + } + command = kmalloc(priv_cmd.total_len, GFP_KERNEL); + if (!command) + { + DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__)); + ret = -ENOMEM; + goto exit; + } + if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto exit; + } + + DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name)); + + if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) { + DHD_ERROR(("%s, Received regular START command\n", __FUNCTION__)); +#ifdef CUSTOMER_HW_SAMSUNG + sleep_never = 1; +#else + bytes_written = wl_android_wifi_on(net); +#endif /* CUSTOMER_HW_SAMSUNG */ + } + else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) { + bytes_written = wl_android_set_fwpath(net, command, priv_cmd.total_len); + } + + if (!g_wifi_on) { + DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n", + __FUNCTION__, command, ifr->ifr_name)); + ret = 0; + goto exit; + } + + if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) { + DHD_ERROR(("%s, Received regular STOP command\n", __FUNCTION__)); +#ifdef CUSTOMER_HW_SAMSUNG + sleep_never = 1; +#else + bytes_written = wl_android_wifi_off(net); +#endif /* CUSTOMER_HW_SAMSUNG */ + } + else if (strnicmp(command, CMD_SCAN_ACTIVE, strlen(CMD_SCAN_ACTIVE)) == 0) { + /* TBD: SCAN-ACTIVE */ + } + else if (strnicmp(command, CMD_SCAN_PASSIVE, strlen(CMD_SCAN_PASSIVE)) == 0) { + /* TBD: SCAN-PASSIVE */ + } + else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) { + bytes_written = wl_android_get_rssi(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) { + bytes_written = wl_android_get_link_speed(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) { + bytes_written = net_os_set_packet_filter(net, 1); + } + else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) { + bytes_written = net_os_set_packet_filter(net, 0); + } + else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) { + int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0'; + bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num); + } + else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) { + int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0'; + bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num); + } + else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) { + /* TBD: BTCOEXSCAN-START */ + } + else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) { + /* TBD: BTCOEXSCAN-STOP */ + } + else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) { +#if !defined(CUSTOMER_HW_SAMSUNG) + uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0'; + + if (mode == 1) + net_os_set_packet_filter(net, 0); /* DHCP starts */ + else + net_os_set_packet_filter(net, 1); /* DHCP ends */ +#endif +#ifdef WL_CFG80211 + bytes_written = wl_cfg80211_set_btcoex_dhcp(net, command); +#endif + } + else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { + bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { + uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; + bytes_written = wldev_set_band(net, band); + wl_update_wiphybands(NULL); + } + else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) { + bytes_written = wl_android_get_band(net, command, priv_cmd.total_len); + } +#ifndef GLOBALCONFIG_WLAN_COUNTRY_CODE + + else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) { + char *country_code = command + strlen(CMD_COUNTRY) + 1; + bytes_written = wldev_set_country(net, country_code); + wl_update_wiphybands(NULL); + } +#endif +#ifdef ROAM_API + else if (strnicmp(command, CMD_ROAMTRIGGER_SET, + strlen(CMD_ROAMTRIGGER_SET)) == 0) { + bytes_written = wl_android_set_roam_trigger(net, command, + priv_cmd.total_len); + } else if (strnicmp(command, CMD_ROAMTRIGGER_GET, + strlen(CMD_ROAMTRIGGER_GET)) == 0) { + bytes_written = wl_android_get_roam_trigger(net, command, + priv_cmd.total_len); + } else if (strnicmp(command, CMD_ROAMDELTA_SET, + strlen(CMD_ROAMDELTA_SET)) == 0) { + bytes_written = wl_android_set_roam_delta(net, command, + priv_cmd.total_len); + } else if (strnicmp(command, CMD_ROAMDELTA_GET, + strlen(CMD_ROAMDELTA_GET)) == 0) { + bytes_written = wl_android_get_roam_delta(net, command, + priv_cmd.total_len); + } else if (strnicmp(command, CMD_ROAMSCANPERIOD_SET, + strlen(CMD_ROAMSCANPERIOD_SET)) == 0) { + bytes_written = wl_android_set_roam_scan_period(net, command, + priv_cmd.total_len); + } else if (strnicmp(command, CMD_ROAMSCANPERIOD_GET, + strlen(CMD_ROAMSCANPERIOD_GET)) == 0) { + bytes_written = wl_android_get_roam_scan_period(net, command, + priv_cmd.total_len); + } else if (strnicmp(command, CMD_COUNTRYREV_SET, + strlen(CMD_COUNTRYREV_SET)) == 0) { + bytes_written = wl_android_set_country_rev(net, command, + priv_cmd.total_len); + wl_update_wiphybands(NULL); + } else if (strnicmp(command, CMD_COUNTRYREV_GET, + strlen(CMD_COUNTRYREV_GET)) == 0) { + bytes_written = wl_android_get_country_rev(net, command, + priv_cmd.total_len); + } +#endif /* ROAM_API */ +#ifdef PNO_SUPPORT + else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) { + bytes_written = dhd_dev_pno_reset(net); + } + else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) { + bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) { + uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0'; + bytes_written = dhd_dev_pno_enable(net, pfn_enabled); + } +#endif + else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) { + bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) { + int skip = strlen(CMD_P2P_SET_NOA) + 1; + bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, + priv_cmd.total_len - skip); + } + else if (strnicmp(command, CMD_P2P_GET_NOA, strlen(CMD_P2P_GET_NOA)) == 0) { + bytes_written = wl_cfg80211_get_p2p_noa(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_P2P_SET_PS, strlen(CMD_P2P_SET_PS)) == 0) { + int skip = strlen(CMD_P2P_SET_PS) + 1; + bytes_written = wl_cfg80211_set_p2p_ps(net, command + skip, + priv_cmd.total_len - skip); + } +#ifdef WL_CFG80211 + else if (strnicmp(command, CMD_SET_AP_WPS_P2P_IE, + strlen(CMD_SET_AP_WPS_P2P_IE)) == 0) { + int skip = strlen(CMD_SET_AP_WPS_P2P_IE) + 3; + bytes_written = wl_cfg80211_set_wps_p2p_ie(net, command + skip, + priv_cmd.total_len - skip, *(command + skip - 2) - '0'); + } +#endif /* WL_CFG80211 */ + else if (strnicmp(command, CMD_SET_HAPD_AUTO_CHANNEL, + strlen(CMD_SET_HAPD_AUTO_CHANNEL)) == 0) { + int skip = strlen(CMD_SET_HAPD_AUTO_CHANNEL) + 3; +// wl_android_set_auto_channel(net, (const char*)command+skip, command, +// priv_cmd.total_len); + bytes_written = wl_android_set_auto_channel(net, (const char*)command+skip, command, + priv_cmd.total_len); + } + else if (strnicmp(command, CMD_SET_HAPD_MAX_NUM_STA, + strlen(CMD_SET_HAPD_MAX_NUM_STA)) == 0) { + int skip = strlen(CMD_SET_HAPD_MAX_NUM_STA) + 3; + wl_android_set_max_num_sta(net, (const char*)command+skip); + } + else if (strnicmp(command, CMD_SET_HAPD_SSID, + strlen(CMD_SET_HAPD_SSID)) == 0) { + int skip = strlen(CMD_SET_HAPD_SSID) + 3; + wl_android_set_ssid(net, (const char*)command+skip); + } + else if (strnicmp(command, CMD_SET_HAPD_HIDE_SSID, + strlen(CMD_SET_HAPD_HIDE_SSID)) == 0) { + int skip = strlen(CMD_SET_HAPD_HIDE_SSID) + 3; + wl_android_set_hide_ssid(net, (const char*)command+skip); + } + else if (strnicmp(command, CMD_HAPD_STA_DISASSOC, + strlen(CMD_HAPD_STA_DISASSOC)) == 0) { + int skip = strlen(CMD_HAPD_STA_DISASSOC) + 1; + wl_android_sta_diassoc(net, (const char*)command+skip); + } +#ifdef OKC_SUPPORT + else if (strnicmp(command, CMD_OKC_SET_PMK, strlen(CMD_OKC_SET_PMK)) == 0) + bytes_written = wl_android_set_pmk(net, command, priv_cmd.total_len); + else if (strnicmp(command, CMD_OKC_ENABLE, strlen(CMD_OKC_ENABLE)) == 0) + bytes_written = wl_android_okc_enable(net, command, priv_cmd.total_len); +#endif /* OKC_SUPPORT */ +#ifdef BCMCCX + else if (strnicmp(command, CMD_GETCCKM_RN, strlen(CMD_GETCCKM_RN)) == 0) { + bytes_written = wl_android_get_cckm_rn(net, command); + } + else if (strnicmp(command, CMD_SETCCKM_KRK, strlen(CMD_SETCCKM_KRK)) == 0) { + bytes_written = wl_android_set_cckm_krk(net, command); + } + else if (strnicmp(command, CMD_GET_ASSOC_RES_IES, strlen(CMD_GET_ASSOC_RES_IES)) == 0) { + bytes_written = wl_android_get_assoc_res_ies(net, command); + } +#endif /* BCMCCX */ +#ifdef SUPPORT_AMPDU_MPDU_CMD + /* CMD_AMPDU_MPDU */ + else if (strnicmp(command, CMD_AMPDU_MPDU,strlen(CMD_AMPDU_MPDU)) == 0) { + int skip = strlen(CMD_AMPDU_MPDU) + 1; + bytes_written = wl_android_set_ampdu_mpdu(net, (const char*)command+skip); + } +#endif +#ifdef VSDB + else if (strnicmp(command, CMD_CHANGE_RL, strlen(CMD_CHANGE_RL)) == 0) + bytes_written = wl_android_ch_res_rl(net, true); + else if (strnicmp(command, CMD_RESTORE_RL, strlen(CMD_RESTORE_RL)) == 0) + bytes_written = wl_android_ch_res_rl(net, false); +#endif + else { + if ((strnicmp(command, CMD_START, strlen(CMD_START)) != 0) && + (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) != 0)) + DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command)); + snprintf(command, 3, "OK"); + bytes_written = strlen("OK"); + } + + if (bytes_written >= 0) { + if ((bytes_written == 0) && (priv_cmd.total_len > 0)) + command[0] = '\0'; + if (bytes_written >= priv_cmd.total_len) { + DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written)); + bytes_written = priv_cmd.total_len; + } else { + bytes_written++; + } + priv_cmd.used_len = bytes_written; + if (copy_to_user(priv_cmd.buf, command, bytes_written)) { + DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__)); + ret = -EFAULT; + } + } + else { + ret = bytes_written; + } + +exit: + net_os_wake_unlock(net); + if (command) { + kfree(command); + } + + return ret; +} + +int wl_android_init(void) +{ + int ret = 0; + + dhd_msg_level = DHD_ERROR_VAL; +#ifdef ENABLE_INSMOD_NO_FW_LOAD + dhd_download_fw_on_driverload = FALSE; +#endif /* ENABLE_INSMOD_NO_FW_LOAD */ +#if defined(CUSTOMER_HW2) || defined(CUSTOMER_HW_SAMSUNG) + if (!iface_name[0]) { + memset(iface_name, 0, IFNAMSIZ); + bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); + } +#endif /* CUSTOMER_HW2 || CUSTOMER_HW_SAMSUNG */ + return ret; +} + +int wl_android_exit(void) +{ + int ret = 0; + + return ret; +} + +void wl_android_post_init(void) +{ + if (!dhd_download_fw_on_driverload) { + /* Call customer gpio to turn off power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + g_wifi_on = 0; + } +} +/** + * Functions for Android WiFi card detection + */ +#if defined(CONFIG_WIFI_CONTROL_FUNC) + +static int g_wifidev_registered = 0; +static struct semaphore wifi_control_sem; +static struct wifi_platform_data *wifi_control_data = NULL; +static struct resource *wifi_irqres = NULL; + +static int wifi_add_dev(void); +static void wifi_del_dev(void); + +int wl_android_wifictrl_func_add(void) +{ + int ret = 0; + sema_init(&wifi_control_sem, 0); + + ret = wifi_add_dev(); + if (ret) { + DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__)); + return ret; + } + g_wifidev_registered = 1; + + /* Waiting callback after platform_driver_register is done or exit with error */ + if (down_timeout(&wifi_control_sem, msecs_to_jiffies(1000)) != 0) { + ret = -EINVAL; + DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__)); + } + + return ret; +} + +void wl_android_wifictrl_func_del(void) +{ + if (g_wifidev_registered) + { + wifi_del_dev(); + g_wifidev_registered = 0; + } +} + +void* wl_android_prealloc(int section, unsigned long size) +{ + void *alloc_ptr = NULL; + if (wifi_control_data && wifi_control_data->mem_prealloc) { + alloc_ptr = wifi_control_data->mem_prealloc(section, size); + if (alloc_ptr) { + DHD_INFO(("success alloc section %d\n", section)); + if (size != 0L) + bzero(alloc_ptr, size); + return alloc_ptr; + } + } + + DHD_ERROR(("can't alloc section %d\n", section)); + return NULL; +} + +int wifi_get_irq_number(unsigned long *irq_flags_ptr) +{ + if (wifi_irqres) { + *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK; + return (int)wifi_irqres->start; + } +#ifdef CUSTOM_OOB_GPIO_NUM + return CUSTOM_OOB_GPIO_NUM; +#else + return -1; +#endif +} + +int wifi_set_power(int on, unsigned long msec) +{ + DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); + if (wifi_control_data && wifi_control_data->set_power) { + wifi_control_data->set_power(on); + } + if (msec) + msleep(msec); + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) +int wifi_get_mac_addr(unsigned char *buf) +{ + DHD_ERROR(("%s\n", __FUNCTION__)); + if (!buf) + return -EINVAL; + if (wifi_control_data && wifi_control_data->get_mac_addr) { + return wifi_control_data->get_mac_addr(buf); + } + return -EOPNOTSUPP; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) +void *wifi_get_country_code(char *ccode) +{ + DHD_TRACE(("%s\n", __FUNCTION__)); + if (!ccode) + return NULL; + if (wifi_control_data && wifi_control_data->get_country_code) { + return wifi_control_data->get_country_code(ccode); + } + return NULL; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */ + +static int wifi_set_carddetect(int on) +{ + DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); + if (wifi_control_data && wifi_control_data->set_carddetect) { + wifi_control_data->set_carddetect(on); + } + return 0; +} + +static int wifi_probe(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *)(pdev->dev.platform_data); + + DHD_ERROR(("## %s\n", __FUNCTION__)); + wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq"); + if (wifi_irqres == NULL) + wifi_irqres = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, "bcm4329_wlan_irq"); + wifi_control_data = wifi_ctrl; + + wifi_set_power(1, 200); /* Power On */ + wifi_set_carddetect(1); /* CardDetect (0->1) */ + + up(&wifi_control_sem); + return 0; +} + +static int wifi_remove(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *)(pdev->dev.platform_data); + + DHD_ERROR(("## %s\n", __FUNCTION__)); + wifi_control_data = wifi_ctrl; + + wifi_set_power(0, 100); /* Power Off */ + wifi_set_carddetect(0); /* CardDetect (1->0) */ + + up(&wifi_control_sem); + return 0; +} +int dhd_os_check_wakelock(void *dhdp); + +static int wifi_suspend(struct platform_device *pdev, pm_message_t state) +{ + DHD_TRACE(("##> %s\n", __FUNCTION__)); +#if defined(BCMHOST) + if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) + return -EBUSY; +#endif +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1 + bcmsdh_oob_intr_set(0); +#endif /* (OOB_INTR_ONLY) */ + return 0; +} + +static int wifi_resume(struct platform_device *pdev) +{ + DHD_TRACE(("##> %s\n", __FUNCTION__)); +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) && 1 + if (dhd_os_check_if_up(bcmsdh_get_drvdata())) + bcmsdh_oob_intr_set(1); +#endif /* (OOB_INTR_ONLY) */ + return 0; +} + +static struct platform_driver wifi_device = { + .probe = wifi_probe, + .remove = wifi_remove, + .suspend = wifi_suspend, + .resume = wifi_resume, + .driver = { + .name = "bcmdhd_wlan", + } +}; + +static struct platform_driver wifi_device_legacy = { + .probe = wifi_probe, + .remove = wifi_remove, + .suspend = wifi_suspend, + .resume = wifi_resume, + .driver = { + .name = "bcm4329_wlan", + } +}; + +static int wifi_add_dev(void) +{ + DHD_TRACE(("## Calling platform_driver_register\n")); + platform_driver_register(&wifi_device); + platform_driver_register(&wifi_device_legacy); + return 0; +} + +static void wifi_del_dev(void) +{ + DHD_TRACE(("## Unregister platform_driver_register\n")); + platform_driver_unregister(&wifi_device); + platform_driver_unregister(&wifi_device_legacy); +} +#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ |