/* * 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 372668 2012-12-04 14:07:12Z $ */ #include #include #include #include #include #include #include #include #include #include #include #ifdef WL_CFG80211 #include #endif #if defined(CONFIG_WIFI_CONTROL_FUNC) #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) #include #else #include #endif #endif /* CONFIG_WIFI_CONTROL_FUNC */ #ifndef WL_CFG80211 #define htod32(i) i #define htod16(i) i #define dtoh32(i) i #define dtoh16(i) i #define htodchanspec(i) i #define dtohchanspec(i) i #endif /* message levels */ #define ANDROID_ERROR_LEVEL 0x0001 #define ANDROID_TRACE_LEVEL 0x0002 #define ANDROID_INFO_LEVEL 0x0004 uint android_msg_level = ANDROID_ERROR_LEVEL; #define ANDROID_ERROR(x) \ do { \ if (android_msg_level & ANDROID_ERROR_LEVEL) { \ printk(KERN_ERR "ANDROID-ERROR) "); \ printk x; \ } \ } while (0) #define ANDROID_TRACE(x) \ do { \ if (android_msg_level & ANDROID_TRACE_LEVEL) { \ printk(KERN_ERR "ANDROID-TRACE) "); \ printk x; \ } \ } while (0) #define ANDROID_INFO(x) \ do { \ if (android_msg_level & ANDROID_INFO_LEVEL) { \ printk(KERN_ERR "ANDROID-INFO) "); \ printk x; \ } \ } while (0) /* * 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_SETSUSPENDMODE "SETSUSPENDMODE" #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" #if !defined WL_ENABLE_P2P_IF #define CMD_P2P_GET_NOA "P2P_GET_NOA" #endif #define CMD_P2P_SD_OFFLOAD "P2P_SD_" #define CMD_P2P_SET_PS "P2P_SET_PS" #define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" #define CMD_SETROAMMODE "SETROAMMODE" /* CCX Private Commands */ #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 */ #define CMD_OKC_SET_PMK "SET_PMK" #define CMD_OKC_ENABLE "OKC_ENABLE" 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); int dhd_dev_reset(struct net_device *dev, uint8 flag); int 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 /* WL_CFG80211 */ extern int dhd_os_check_wakelock(void *dhdp); extern int dhd_os_check_if_up(void *dhdp); extern void *bcmsdh_get_drvdata(void); #if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) extern int dhd_wlfc_init(dhd_pub_t *dhd); extern void dhd_wlfc_deinit(dhd_pub_t *dhd); #endif extern bool ap_fw_loaded; extern char iface_name[IFNAMSIZ]; #define WIFI_TURNOFF_DELAY 0 /** * 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); ANDROID_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; #if defined(RSSIOFFSET) rssi = wl_update_rssi_offset(rssi); #endif error = wldev_get_ssid(net, &ssid); if (error) return -1; if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) { ANDROID_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); ANDROID_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; 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, 1))) ANDROID_INFO(("%s: Suspend Flag %d -> %d\n", __FUNCTION__, ret_now, suspend_flag)); else ANDROID_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); } return ret; } static int wl_android_set_suspendmode(struct net_device *dev, char *command, int total_len) { int ret = 0; #if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND) int suspend_flag; suspend_flag = *(command + strlen(CMD_SETSUSPENDMODE) + 1) - '0'; if (suspend_flag != 0) suspend_flag = 1; if (!(ret = net_os_set_suspend(dev, suspend_flag, 0))) ANDROID_INFO(("%s: Suspend Mode %d\n",__FUNCTION__,suspend_flag)); else ANDROID_ERROR(("%s: failed %d\n",__FUNCTION__,ret)); #endif 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; } #if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) 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 */ ANDROID_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { ANDROID_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) { ANDROID_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)) { ANDROID_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); ANDROID_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); if (str_ptr[0] != 0) { if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { ANDROID_ERROR(("%s pno repeat : corrupted field\n", __FUNCTION__)); goto exit_proc; } str_ptr++; pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); ANDROID_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { ANDROID_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); ANDROID_INFO(("%s: pno_freq_expo_max=%d\n", __FUNCTION__, pno_freq_expo_max)); } } } else { ANDROID_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 && !WL_SCHED_SCAN */ 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; } /** * 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; if (!dev) { ANDROID_ERROR(("%s: dev is null\n", __FUNCTION__)); return -EINVAL; } printk("%s in 1\n", __FUNCTION__); dhd_net_if_lock(dev); printk("%s in 2: g_wifi_on=%d\n", __FUNCTION__, g_wifi_on); if (!g_wifi_on) { do { dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); ret = sdioh_start(NULL, 0); if (ret == 0) break; ANDROID_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) { ANDROID_ERROR(("\nfailed to power up wifi chip, max retry reached **\n\n")); goto exit; } ret = dhd_dev_reset(dev, FALSE); if (ret) goto err; sdioh_start(NULL, 1); if (!ret) { if (dhd_dev_init_ioctl(dev) < 0) { ret = -EFAULT; goto err; } } #if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) dhd_wlfc_init(bcmsdh_get_drvdata()); #endif g_wifi_on = TRUE; } exit: printk("%s: Success\n", __FUNCTION__); dhd_net_if_unlock(dev); return ret; err: dhd_dev_reset(dev, TRUE); sdioh_stop(NULL); dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); printk("%s: Failed\n", __FUNCTION__); dhd_net_if_unlock(dev); return ret; } int wl_android_wifi_off(struct net_device *dev) { int ret = 0; if (!dev) { ANDROID_ERROR(("%s: dev is null\n", __FUNCTION__)); return -EINVAL; } printk("%s in 1\n", __FUNCTION__); dhd_net_if_lock(dev); printk("%s in 2: g_wifi_on=%d\n", __FUNCTION__, g_wifi_on); if (g_wifi_on) { #if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) dhd_wlfc_deinit(bcmsdh_get_drvdata()); #endif ret = dhd_dev_reset(dev, TRUE); sdioh_stop(NULL); dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); g_wifi_on = FALSE; } printk("%s out\n", __FUNCTION__); 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) { ANDROID_INFO(("GOT APSTA FIRMWARE\n")); ap_fw_loaded = TRUE; } else { ANDROID_INFO(("GOT STA FIRMWARE\n")); ap_fw_loaded = FALSE; } return 0; } 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) { ANDROID_ERROR(("Failed to set PMK for OKC, error = %d\n", error)); } #ifdef OKC_DEBUG ANDROID_ERROR(("PMK is ")); for (i = 0; i < 32; i++) ANDROID_ERROR(("%02X ", pmk[i])); ANDROID_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) { ANDROID_ERROR(("Failed to %s OKC, error = %d\n", okc_enable ? "enable" : "disable", error)); } return error; } int wl_android_set_roam_mode(struct net_device *dev, char *command, int total_len) { int error = 0; int mode = 0; if (sscanf(command, "%*s %d", &mode) != 1) { ANDROID_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); return -1; } error = wldev_iovar_setint(dev, "roam_off", mode); if (error) { ANDROID_ERROR(("%s: Failed to set roaming Mode %d, error = %d\n", __FUNCTION__, mode, error)); return -1; } else ANDROID_ERROR(("%s: succeeded to set roaming Mode %d, error = %d\n", __FUNCTION__, mode, error)); return 0; } int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) { #define PRIVATE_COMMAND_MAX_LEN 8192 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; } if (priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) { ANDROID_ERROR(("%s: too long priavte command\n", __FUNCTION__)); ret = -EINVAL; } command = kmalloc(priv_cmd.total_len, GFP_KERNEL); if (!command) { ANDROID_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; } ANDROID_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name)); if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) { ANDROID_INFO(("%s, Received regular START command\n", __FUNCTION__)); bytes_written = wl_android_wifi_on(net); } 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) { ANDROID_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) { bytes_written = wl_android_wifi_off(net); } 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); } #ifdef PKT_FILTER_SUPPORT else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) { bytes_written = net_os_enable_packet_filter(net, 1); } else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) { bytes_written = net_os_enable_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); } #endif /* PKT_FILTER_SUPPORT */ 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) { #ifdef WL_CFG80211 bytes_written = wl_cfg80211_set_btcoex_dhcp(net, command); #else #ifdef PKT_FILTER_SUPPORT uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0'; if (mode == 1) net_os_enable_packet_filter(net, 0); /* DHCP starts */ else net_os_enable_packet_filter(net, 1); /* DHCP ends */ #endif /* PKT_FILTER_SUPPORT */ #endif /* WL_CFG80211 */ } 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_SETSUSPENDMODE, strlen(CMD_SETSUSPENDMODE)) == 0) { bytes_written = wl_android_set_suspendmode(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; #ifdef WL_HOST_BAND_MGMT if (wl_cfg80211_set_band(net, band) < 0) { bytes_written = -1; goto exit; } if (band == WLC_BAND_AUTO) bytes_written = wldev_set_band(net, band); #else bytes_written = wldev_set_band(net, band); #endif /* WL_HOST_BAND_MGMT */ } else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) { bytes_written = wl_android_get_band(net, command, priv_cmd.total_len); } #ifdef WL_CFG80211 /* CUSTOMER_SET_COUNTRY feature is define for only GGSM model */ 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); } #endif /* WL_CFG80211 */ #if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) 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 /* PNO_SUPPORT && !WL_SCHED_SCAN */ 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); } #if !defined WL_ENABLE_P2P_IF 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); } #endif /* WL_ENABLE_P2P_IF */ 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_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); else if (strnicmp(command, CMD_SETROAMMODE, strlen(CMD_SETROAMMODE)) == 0) bytes_written = wl_android_set_roam_mode(net, command, priv_cmd.total_len); else { ANDROID_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) { ANDROID_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)) { ANDROID_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 (!iface_name[0]) { memset(iface_name, 0, IFNAMSIZ); bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); } return ret; } int wl_android_exit(void) { int ret = 0; return ret; } void wl_android_post_init(void) { if (!dhd_download_fw_on_driverload) { sdioh_stop(NULL); /* 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) { ANDROID_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; ANDROID_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) { ANDROID_INFO(("success alloc section %d\n", section)); if (size != 0L) bzero(alloc_ptr, size); return alloc_ptr; } } ANDROID_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) { ANDROID_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) { ANDROID_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) { ANDROID_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) { ANDROID_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); 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, 0); /* 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); ANDROID_ERROR(("## %s\n", __FUNCTION__)); wifi_control_data = wifi_ctrl; wifi_set_power(0, WIFI_TURNOFF_DELAY); /* Power Off */ wifi_set_carddetect(0); /* CardDetect (1->0) */ up(&wifi_control_sem); return 0; } static int wifi_suspend(struct platform_device *pdev, pm_message_t state) { ANDROID_TRACE(("##> %s\n", __FUNCTION__)); #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI) if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) return -EBUSY; #endif /* defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI) */ #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) { ANDROID_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) { int ret = 0; ANDROID_TRACE(("## Calling platform_driver_register\n")); ret = platform_driver_register(&wifi_device); if (ret) return ret; ret = platform_driver_register(&wifi_device_legacy); return ret; } static void wifi_del_dev(void) { ANDROID_TRACE(("## Unregister platform_driver_register\n")); platform_driver_unregister(&wifi_device); platform_driver_unregister(&wifi_device_legacy); } #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ #if defined(RSSIAVG) void wl_free_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl) { wl_rssi_cache_t *node, *cur, **rssi_head; int i=0; rssi_head = &rssi_cache_ctrl->m_cache_head; node = *rssi_head; for (;node;) { ANDROID_INFO(("%s: Free %d with BSSID %pM\n", __FUNCTION__, i, &node->BSSID)); cur = node; node = cur->next; kfree(cur); i++; } *rssi_head = NULL; } void wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl) { wl_rssi_cache_t *node, *prev, **rssi_head; int i = -1, tmp = 0; #if defined(BSSCACHE) int max = BSSCACHE_LEN; #else int max = RSSICACHE_LEN; #endif rssi_head = &rssi_cache_ctrl->m_cache_head; node = *rssi_head; prev = node; for (;node;) { i++; if (node->dirty >= max || node->dirty >= RSSICACHE_LEN) { if (node == *rssi_head) { tmp = 1; *rssi_head = node->next; } else { tmp = 0; prev->next = node->next; } ANDROID_INFO(("%s: Del %d with BSSID %pM\n", __FUNCTION__, i, &node->BSSID)); kfree(node); if (tmp == 1) { node = *rssi_head; prev = node; } else { node = prev->next; } continue; } prev = node; node = node->next; } } void wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, u8 *bssid) { wl_rssi_cache_t *node, *prev, **rssi_head; int i = -1, tmp = 0; rssi_head = &rssi_cache_ctrl->m_cache_head; node = *rssi_head; prev = node; for (;node;) { i++; if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) { if (node == *rssi_head) { tmp = 1; *rssi_head = node->next; } else { tmp = 0; prev->next = node->next; } ANDROID_INFO(("%s: Del %d with BSSID %pM\n", __FUNCTION__, i, &node->BSSID)); kfree(node); if (tmp == 1) { node = *rssi_head; prev = node; } else { node = prev->next; } continue; } prev = node; node = node->next; } } void wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl) { wl_rssi_cache_t *node, **rssi_head; rssi_head = &rssi_cache_ctrl->m_cache_head; /* reset dirty */ node = *rssi_head; for (;node;) { node->dirty += 1; node = node->next; } } int wl_update_connected_rssi_cache(struct net_device *net, wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg) { wl_rssi_cache_t *node, *prev, *leaf, **rssi_head; int j, k=0; int rssi, error=0; struct ether_addr bssid; error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), false); if (error == BCME_NOTASSOCIATED) { ANDROID_INFO(("%s: Not Associated! res:%d\n", __FUNCTION__, error)); return 0; } if (error) { ANDROID_ERROR(("Could not get bssid (%d)\n", error)); } error = wldev_get_rssi(net, &rssi); if (error) { ANDROID_ERROR(("Could not get rssi (%d)\n", error)); return error; } /* update RSSI */ rssi_head = &rssi_cache_ctrl->m_cache_head; node = *rssi_head; prev = NULL; for (;node;) { if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) { ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%d\n", __FUNCTION__, k, &bssid, rssi)); for(j=0; jRSSI[j] = node->RSSI[j+1]; node->RSSI[j] = rssi; node->dirty = 0; goto exit; } prev = node; node = node->next; k++; } leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL); if (!leaf) { ANDROID_ERROR(("%s: Memory alloc failure %d\n", __FUNCTION__, sizeof(wl_rssi_cache_t))); return 0; } ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%d in the leaf\n", __FUNCTION__, k, &bssid, rssi)); leaf->next = NULL; leaf->dirty = 0; memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN); for (j=0; jRSSI[j] = rssi; if (!prev) *rssi_head = leaf; else prev->next = leaf; exit: *rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid); return error; } void wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, wl_scan_results_t *ss_list) { wl_rssi_cache_t *node, *prev, *leaf, **rssi_head; wl_bss_info_t *bi = NULL; int i, j, k; if (!ss_list->count) return; rssi_head = &rssi_cache_ctrl->m_cache_head; /* update RSSI */ for (i = 0; i < ss_list->count; i++) { node = *rssi_head; prev = NULL; k = 0; bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info; for (;node;) { if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) { ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%d, SSID \"%s\"\n", __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); for(j=0; jRSSI[j] = node->RSSI[j+1]; node->RSSI[j] = dtoh16(bi->RSSI); node->dirty = 0; break; } prev = node; node = node->next; k++; } if (node) continue; leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL); if (!leaf) { ANDROID_ERROR(("%s: Memory alloc failure %d\n", __FUNCTION__, sizeof(wl_rssi_cache_t))); return; } ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%d, SSID \"%s\" in the leaf\n", __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); leaf->next = NULL; leaf->dirty = 0; memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN); for (j=0; jRSSI[j] = dtoh16(bi->RSSI); if (!prev) *rssi_head = leaf; else prev->next = leaf; } } int16 wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr) { wl_rssi_cache_t *node, **rssi_head; int j, rssi_sum, rssi=-200; rssi_head = &rssi_cache_ctrl->m_cache_head; /* reset dirty */ node = *rssi_head; for (;node;) { if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) { rssi_sum = 0; rssi = 0; for (j=0; jRSSI[RSSIAVG_LEN-j-1]; rssi = rssi_sum / j; break; } node = node->next; } if (rssi >= -2) rssi = -2; if (rssi == -200) { ANDROID_ERROR(("%s: BSSID %pM does not in RSSI cache\n", __FUNCTION__, addr)); } return (int16)rssi; } #endif #if defined(RSSIOFFSET) int wl_update_rssi_offset(int rssi) { uint chip, chiprev; chip = dhd_bus_chip_id(bcmsdh_get_drvdata()); chiprev = dhd_bus_chiprev_id(bcmsdh_get_drvdata()); if (chip == BCM4330_CHIP_ID && chiprev == BCM4330B2_CHIP_REV) { #if defined(RSSIOFFSET_NEW) int j; for (j=0; j= -2) rssi = -2; return rssi; } #endif #if defined(BSSCACHE) #define WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN 32 void wl_free_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl) { wl_bss_cache_t *node, *cur, **bss_head; int i=0; ANDROID_TRACE(("%s called\n", __FUNCTION__)); bss_head = &bss_cache_ctrl->m_cache_head; node = *bss_head; for (;node;) { ANDROID_TRACE(("%s: Free %d with BSSID %pM\n", __FUNCTION__, i, &node->results.bss_info->BSSID)); cur = node; node = cur->next; kfree(cur); i++; } *bss_head = NULL; } void wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl) { wl_bss_cache_t *node, *prev, **bss_head; int i = -1, tmp = 0; bss_head = &bss_cache_ctrl->m_cache_head; node = *bss_head; prev = node; for (;node;) { i++; if (node->dirty >= BSSCACHE_LEN) { if (node == *bss_head) { tmp = 1; *bss_head = node->next; } else { tmp = 0; prev->next = node->next; } ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%d, SSID \"%s\"\n", __FUNCTION__, i, &node->results.bss_info->BSSID, dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID)); kfree(node); if (tmp == 1) { node = *bss_head; prev = node; } else { node = prev->next; } continue; } prev = node; node = node->next; } } void wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, u8 *bssid) { wl_bss_cache_t *node, *prev, **bss_head; int i = -1, tmp = 0; bss_head = &bss_cache_ctrl->m_cache_head; node = *bss_head; prev = node; for (;node;) { i++; if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) { if (node == *bss_head) { tmp = 1; *bss_head = node->next; } else { tmp = 0; prev->next = node->next; } ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%d, SSID \"%s\"\n", __FUNCTION__, i, &node->results.bss_info->BSSID, dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID)); kfree(node); if (tmp == 1) { node = *bss_head; prev = node; } else { node = prev->next; } continue; } prev = node; node = node->next; } } void wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl) { wl_bss_cache_t *node, **bss_head; bss_head = &bss_cache_ctrl->m_cache_head; /* reset dirty */ node = *bss_head; for (;node;) { node->dirty += 1; node = node->next; } } void wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, wl_scan_results_t *ss_list) { wl_bss_cache_t *node, *prev, *leaf, *tmp, **bss_head; wl_bss_info_t *bi = NULL; int i, k=0; if (!ss_list->count) return; bss_head = &bss_cache_ctrl->m_cache_head; for (i=0; i < ss_list->count; i++) { node = *bss_head; prev = NULL; bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info; for (;node;) { if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) { tmp = node; leaf = kmalloc(dtoh32(bi->length) + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN, GFP_KERNEL); if (!leaf) { ANDROID_ERROR(("%s: Memory alloc failure %d and keep old BSS info\n", __FUNCTION__, dtoh32(bi->length) + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN)); break; } memcpy(leaf->results.bss_info, bi, dtoh32(bi->length)); leaf->next = node->next; leaf->dirty = 0; leaf->results.count = 1; leaf->results.version = ss_list->version; ANDROID_TRACE(("%s: Update %d with BSSID %pM, RSSI=%d, SSID \"%s\"\n", __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); if (!prev) *bss_head = leaf; else prev->next = leaf; node = leaf; prev = node; kfree(tmp); k++; break; } prev = node; node = node->next; } if (node) continue; leaf = kmalloc(dtoh32(bi->length) + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN, GFP_KERNEL); if (!leaf) { ANDROID_ERROR(("%s: Memory alloc failure %d\n", __FUNCTION__, dtoh32(bi->length) + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN)); return; } ANDROID_TRACE(("%s: Add %d with cached BSSID %pM, RSSI=%d, SSID \"%s\" in the leaf\n", __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); memcpy(leaf->results.bss_info, bi, dtoh32(bi->length)); leaf->next = NULL; leaf->dirty = 0; leaf->results.count = 1; leaf->results.version = ss_list->version; k++; if (!prev) *bss_head = leaf; else prev->next = leaf; } } void wl_run_bss_cache_timer(wl_bss_cache_ctrl_t *bss_cache_ctrl, int kick_off) { struct timer_list **timer; timer = &bss_cache_ctrl->m_timer; if (*timer) { if (kick_off) { (*timer)->expires = jiffies + BSSCACHE_TIME * HZ / 1000; add_timer(*timer); ANDROID_TRACE(("%s: timer starts\n", __FUNCTION__)); } else { del_timer_sync(*timer); ANDROID_TRACE(("%s: timer stops\n", __FUNCTION__)); } } } void wl_set_bss_cache_timer_flag(ulong data) { wl_bss_cache_ctrl_t *bss_cache_ctrl = (wl_bss_cache_ctrl_t *)data; bss_cache_ctrl->m_timer_expired = 1; ANDROID_TRACE(("%s called\n", __FUNCTION__)); } void wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl) { ANDROID_TRACE(("%s:\n", __FUNCTION__)); wl_free_bss_cache(bss_cache_ctrl); wl_run_bss_cache_timer(bss_cache_ctrl, 0); if (bss_cache_ctrl->m_timer) { kfree(bss_cache_ctrl->m_timer); } } void wl_init_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl) { ANDROID_TRACE(("%s:\n", __FUNCTION__)); bss_cache_ctrl->m_timer_expired = 0; bss_cache_ctrl->m_timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL); if (!bss_cache_ctrl->m_timer) { ANDROID_ERROR(("%s: Memory alloc failure\n", __FUNCTION__ )); return; } init_timer(bss_cache_ctrl->m_timer); bss_cache_ctrl->m_timer->function = (void *)wl_set_bss_cache_timer_flag; bss_cache_ctrl->m_timer->data = (ulong)bss_cache_ctrl; } #endif