/* * 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 390499 2013-03-12 11:38:47Z $ */ #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 */ /* * 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" #ifdef CUSTOMER_HW4 #ifdef SUPPORT_AUTO_CHANNEL #define CMD_SET_HAPD_AUTO_CHANNEL "HAPD_AUTO_CHANNEL" #endif /* SUPPORT_AUTO_CHANNEL */ #ifdef SUPPORT_HIDDEN_AP /* Hostapd private command */ #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" #endif /* SUPPORT_HIDDEN_AP */ #ifdef SUPPORT_SOFTAP_SINGL_DISASSOC #define CMD_HAPD_STA_DISASSOC "HAPD_STA_DISASSOC" #endif /* SUPPORT_SOFTAP_SINGL_DISASSOC */ #ifdef SUPPORT_SET_LPC #define CMD_HAPD_LPC_ENABLED "HAPD_LPC_ENABLED" #endif /* SUPPORT_SET_LPC */ #ifdef SUPPORT_TRIGGER_HANG_EVENT #define CMD_TEST_FORCE_HANG "TEST_FORCE_HANG" #endif /* SUPPORT_TRIGGER_HANG_EVENT */ #endif /* CUSTOMER_HW4 */ /* CCX Private Commands */ #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 */ #define CMD_OKC_SET_PMK "SET_PMK" #define CMD_OKC_ENABLE "OKC_ENABLE" #ifdef CUSTOMER_HW4 #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_FULLROAMSCANPERIOD_SET "SETFULLROAMSCANPERIOD" #define CMD_FULLROAMSCANPERIOD_GET "GETFULLROAMSCANPERIOD" #define CMD_COUNTRYREV_SET "SETCOUNTRYREV" #define CMD_COUNTRYREV_GET "GETCOUNTRYREV" #endif /* ROAM_API */ #ifdef WES_SUPPORT #define CMD_GETROAMSCANCONTROL "GETROAMSCANCONTROL" #define CMD_SETROAMSCANCONTROL "SETROAMSCANCONTROL" #define CMD_GETROAMSCANCHANNELS "GETROAMSCANCHANNELS" #define CMD_SETROAMSCANCHANNELS "SETROAMSCANCHANNELS" #define CMD_GETSCANCHANNELTIME "GETSCANCHANNELTIME" #define CMD_SETSCANCHANNELTIME "SETSCANCHANNELTIME" #define CMD_GETSCANHOMETIME "GETSCANHOMETIME" #define CMD_SETSCANHOMETIME "SETSCANHOMETIME" #define CMD_GETSCANHOMEAWAYTIME "GETSCANHOMEAWAYTIME" #define CMD_SETSCANHOMEAWAYTIME "SETSCANHOMEAWAYTIME" #define CMD_GETSCANNPROBES "GETSCANNPROBES" #define CMD_SETSCANNPROBES "SETSCANNPROBES" #define CMD_SENDACTIONFRAME "SENDACTIONFRAME" #define CMD_REASSOC "REASSOC" #define CMD_GETWESMODE "GETWESMODE" #define CMD_SETWESMODE "SETWESMODE" #define CMD_GETOKCMODE "GETOKCMODE" #define CMD_SETOKCMODE "SETOKCMODE" #define ANDROID_WIFI_MAX_ROAM_SCAN_CHANNELS 100 typedef struct android_wifi_reassoc_params { unsigned char bssid[18]; int channel; } android_wifi_reassoc_params_t; #define ANDROID_WIFI_REASSOC_PARAMS_SIZE sizeof(struct android_wifi_reassoc_params) #define ANDROID_WIFI_ACTION_FRAME_SIZE 1040 typedef struct android_wifi_af_params { unsigned char bssid[18]; int channel; int dwell_time; int len; unsigned char data[ANDROID_WIFI_ACTION_FRAME_SIZE]; } android_wifi_af_params_t; #define ANDROID_WIFI_AF_PARAMS_SIZE sizeof(struct android_wifi_af_params) #endif /* WES_SUPPORT */ #ifdef SUPPORT_AMPDU_MPDU_CMD #define CMD_AMPDU_MPDU "AMPDU_MPDU" #endif /* SUPPORT_AMPDU_MPDU_CMD */ #define CMD_CHANGE_RL "CHANGE_RL" #define CMD_RESTORE_RL "RESTORE_RL" #endif /* CUSTOMER_HW4 */ typedef struct android_wifi_priv_cmd { char *buf; int used_len; int total_len; } android_wifi_priv_cmd; #ifdef WL_GENL static s32 wl_genl_handle_msg(struct sk_buff *skb, struct genl_info *info); static int wl_genl_init(void); static int wl_genl_deinit(void); extern struct net init_net; /* attribute policy: defines which attribute has which type (e.g int, char * etc) * possible values defined in net/netlink.h */ static struct nla_policy wl_genl_policy[BCM_GENL_ATTR_MAX + 1] = { [BCM_GENL_ATTR_STRING] = { .type = NLA_NUL_STRING }, [BCM_GENL_ATTR_MSG] = { .type = NLA_BINARY }, }; #define WL_GENL_VER 1 /* family definition */ static struct genl_family wl_genl_family = { .id = GENL_ID_GENERATE, /* Genetlink would generate the ID */ .hdrsize = 0, .name = "bcm-genl", /* Netlink I/F for Android */ .version = WL_GENL_VER, /* Version Number */ .maxattr = BCM_GENL_ATTR_MAX, }; /* commands: mapping between the command enumeration and the actual function */ struct genl_ops wl_genl_ops = { .cmd = BCM_GENL_CMD_MSG, .flags = 0, .policy = wl_genl_policy, .doit = wl_genl_handle_msg, .dumpit = NULL, }; static struct genl_multicast_group wl_genl_mcast = { .id = GENL_ID_GENERATE, /* Genetlink would generate the ID */ .name = "bcm-genl-mcast", }; #endif /* WL_GENL */ /** * 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); #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT) int wl_cfg80211_set_wes_mode(int mode); int wl_cfg80211_get_wes_mode(void); int wl_cfg80211_get_ioctl_version(void); #endif #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 /* WK_CFG80211 */ 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 #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT) /* wl_roam.c */ extern int get_roamscan_mode(struct net_device *dev, int *mode); extern int set_roamscan_mode(struct net_device *dev, int mode); extern int get_roamscan_channel_list(struct net_device *dev, unsigned char channels[]); extern int set_roamscan_channel_list(struct net_device *dev, unsigned char n, unsigned char channels[], int ioctl_ver); #endif #ifdef ENABLE_4335BT_WAR extern int bcm_bt_lock(int cookie); extern void bcm_bt_unlock(int cookie); static int lock_cookie_wifi = 'W' | 'i'<<8 | 'F'<<16 | 'i'<<24; /* cookie is "WiFi" */ #endif /* ENABLE_4335BT_WAR */ extern bool ap_fw_loaded; #if defined(CUSTOMER_HW2) || defined(CUSTOMER_HW4) extern char iface_name[IFNAMSIZ]; #endif /* CUSTOMER_HW2 || CUSTOMER_HW4 */ #ifndef WIFI_TURNOFF_DELAY #define WIFI_TURNOFF_DELAY 0 #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_HW4 if (!dhd_download_fw_on_driverload) { #endif /* CUSTOMER_HW4 */ 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))) DHD_INFO(("%s: Suspend Flag %d -> %d\n", __FUNCTION__, ret_now, suspend_flag)); else DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); } #ifdef CUSTOMER_HW4 } #endif /* CUSTOMER_HW4 */ 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))) DHD_INFO(("%s: Suspend Mode %d\n", __FUNCTION__, suspend_flag)); else DHD_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; } #ifdef CUSTOMER_HW4 #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 %10d", &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 %10d", &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 %10d", &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_full_roam_scan_period( struct net_device *dev, char* command, int total_len) { int error = 0; int full_roam_scan_period = 0; char smbuf[WLC_IOCTL_SMLEN]; sscanf(command+sizeof("SETFULLROAMSCANPERIOD"), "%d", &full_roam_scan_period); WL_TRACE(("fullroamperiod = %d\n", full_roam_scan_period)); error = wldev_iovar_setbuf(dev, "fullroamperiod", &full_roam_scan_period, sizeof(full_roam_scan_period), smbuf, sizeof(smbuf), NULL); if (error) { DHD_ERROR(("Failed to set full roam scan period, error = %d\n", error)); } return error; } static int wl_android_get_full_roam_scan_period( struct net_device *dev, char *command, int total_len) { int error; int bytes_written; int full_roam_scan_period = 0; error = wldev_iovar_getint(dev, "fullroamperiod", &full_roam_scan_period); if (error) { DHD_ERROR(("%s: get full roam scan period failed code %d\n", __func__, error)); return -1; } else { DHD_INFO(("%s: get full roam scan period %d\n", __func__, full_roam_scan_period)); } bytes_written = snprintf(command, total_len, "%s %d", CMD_FULLROAMSCANPERIOD_GET, full_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"), "%10s %10d", country_code, &rev); WL_TRACE(("country_code = %s, rev = %d\n", 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", __FUNCTION__, cspec.ccode, cspec.rev, error)); } else { dhd_bus_country_set(dev, &cspec); DHD_INFO(("%s: set country '%s/%d'\n", __FUNCTION__, 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", NULL, 0, smbuf, sizeof(smbuf), NULL); if (error) { DHD_ERROR(("%s: get country failed code %d\n", __FUNCTION__, error)); return -1; } else { memcpy(&cspec, smbuf, sizeof(cspec)); DHD_INFO(("%s: get country '%c%c %d'\n", __FUNCTION__, cspec.ccode[0], cspec.ccode[1], cspec.rev)); } bytes_written = snprintf(command, total_len, "%s %c%c %d", CMD_COUNTRYREV_GET, cspec.ccode[0], cspec.ccode[1], cspec.rev); return bytes_written; } #endif /* ROAM_API */ #ifdef WES_SUPPORT int wl_android_get_roam_scan_control(struct net_device *dev, char *command, int total_len) { int error = 0; int bytes_written = 0; int mode = 0; error = get_roamscan_mode(dev, &mode); if (error) { DHD_ERROR(("%s: Failed to get Scan Control, error = %d\n", __FUNCTION__, error)); return -1; } bytes_written = snprintf(command, total_len, "%s %d", CMD_GETROAMSCANCONTROL, mode); return bytes_written; } int wl_android_set_roam_scan_control(struct net_device *dev, char *command, int total_len) { int error = 0; int mode = 0; if (sscanf(command, "%*s %d", &mode) != 1) { DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); return -1; } error = set_roamscan_mode(dev, mode); if (error) { DHD_ERROR(("%s: Failed to set Scan Control %d, error = %d\n", __FUNCTION__, mode, error)); return -1; } return 0; } int wl_android_get_roam_scan_channels(struct net_device *dev, char *command, int total_len) { int bytes_written = 0; unsigned char channels[ANDROID_WIFI_MAX_ROAM_SCAN_CHANNELS] = {0}; int channel_cnt = 0; char channel_info[10 + (ANDROID_WIFI_MAX_ROAM_SCAN_CHANNELS * 3)] = {0}; int channel_info_len = 0; int i = 0; channel_cnt = get_roamscan_channel_list(dev, channels); channel_info_len += sprintf(&channel_info[channel_info_len], "%d ", channel_cnt); for (i = 0; i < channel_cnt; i++) { channel_info_len += sprintf(&channel_info[channel_info_len], "%d ", channels[i]); if (channel_info_len > (sizeof(channel_info) - 10)) break; } channel_info_len += sprintf(&channel_info[channel_info_len], "%s", "\0"); bytes_written = snprintf(command, total_len, "%s %s", CMD_GETROAMSCANCHANNELS, channel_info); return bytes_written; } int wl_android_set_roam_scan_channels(struct net_device *dev, char *command, int total_len) { int error = 0; unsigned char *p = (unsigned char *)(command + strlen(CMD_SETROAMSCANCHANNELS) + 1); int ioctl_version = wl_cfg80211_get_ioctl_version(); error = set_roamscan_channel_list(dev, p[0], &p[1], ioctl_version); if (error) { DHD_ERROR(("%s: Failed to set Scan Channels %d, error = %d\n", __FUNCTION__, p[0], error)); return -1; } return 0; } int wl_android_get_scan_channel_time(struct net_device *dev, char *command, int total_len) { int error = 0; int bytes_written = 0; int time = 0; error = wldev_ioctl(dev, WLC_GET_SCAN_CHANNEL_TIME, &time, sizeof(time), 0); if (error) { DHD_ERROR(("%s: Failed to get Scan Channel Time, error = %d\n", __FUNCTION__, error)); return -1; } bytes_written = snprintf(command, total_len, "%s %d", CMD_GETSCANCHANNELTIME, time); return bytes_written; } int wl_android_set_scan_channel_time(struct net_device *dev, char *command, int total_len) { int error = 0; int time = 0; if (sscanf(command, "%*s %d", &time) != 1) { DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); return -1; } error = wldev_ioctl(dev, WLC_SET_SCAN_CHANNEL_TIME, &time, sizeof(time), 1); if (error) { DHD_ERROR(("%s: Failed to set Scan Channel Time %d, error = %d\n", __FUNCTION__, time, error)); return -1; } return 0; } int wl_android_get_scan_home_time(struct net_device *dev, char *command, int total_len) { int error = 0; int bytes_written = 0; int time = 0; error = wldev_ioctl(dev, WLC_GET_SCAN_HOME_TIME, &time, sizeof(time), 0); if (error) { DHD_ERROR(("Failed to get Scan Home Time, error = %d\n", error)); return -1; } bytes_written = snprintf(command, total_len, "%s %d", CMD_GETSCANHOMETIME, time); return bytes_written; } int wl_android_set_scan_home_time(struct net_device *dev, char *command, int total_len) { int error = 0; int time = 0; if (sscanf(command, "%*s %d", &time) != 1) { DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); return -1; } error = wldev_ioctl(dev, WLC_SET_SCAN_HOME_TIME, &time, sizeof(time), 1); if (error) { DHD_ERROR(("%s: Failed to set Scan Home Time %d, error = %d\n", __FUNCTION__, time, error)); return -1; } return 0; } int wl_android_get_scan_home_away_time(struct net_device *dev, char *command, int total_len) { int error = 0; int bytes_written = 0; int time = 0; error = wldev_iovar_getint(dev, "scan_home_away_time", &time); if (error) { DHD_ERROR(("%s: Failed to get Scan Home Away Time, error = %d\n", __FUNCTION__, error)); return -1; } bytes_written = snprintf(command, total_len, "%s %d", CMD_GETSCANHOMEAWAYTIME, time); return bytes_written; } int wl_android_set_scan_home_away_time(struct net_device *dev, char *command, int total_len) { int error = 0; int time = 0; if (sscanf(command, "%*s %d", &time) != 1) { DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); return -1; } error = wldev_iovar_setint(dev, "scan_home_away_time", time); if (error) { DHD_ERROR(("%s: Failed to set Scan Home Away Time %d, error = %d\n", __FUNCTION__, time, error)); return -1; } return 0; } int wl_android_get_scan_nprobes(struct net_device *dev, char *command, int total_len) { int error = 0; int bytes_written = 0; int num = 0; error = wldev_ioctl(dev, WLC_GET_SCAN_NPROBES, &num, sizeof(num), 0); if (error) { DHD_ERROR(("%s: Failed to get Scan NProbes, error = %d\n", __FUNCTION__, error)); return -1; } bytes_written = snprintf(command, total_len, "%s %d", CMD_GETSCANNPROBES, num); return bytes_written; } int wl_android_set_scan_nprobes(struct net_device *dev, char *command, int total_len) { int error = 0; int num = 0; if (sscanf(command, "%*s %d", &num) != 1) { DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); return -1; } error = wldev_ioctl(dev, WLC_SET_SCAN_NPROBES, &num, sizeof(num), 1); if (error) { DHD_ERROR(("%s: Failed to set Scan NProbes %d, error = %d\n", __FUNCTION__, num, error)); return -1; } return 0; } int wl_android_send_action_frame(struct net_device *dev, char *command, int total_len) { int error = -1; android_wifi_af_params_t *params = NULL; wl_action_frame_t *action_frame = NULL; wl_af_params_t *af_params = NULL; char *smbuf = NULL; struct ether_addr tmp_bssid; int tmp_channel = 0; params = (android_wifi_af_params_t *)(command + strlen(CMD_SENDACTIONFRAME) + 1); if (params == NULL) { DHD_ERROR(("%s: Invalid params \n", __FUNCTION__)); goto send_action_frame_out; } smbuf = kmalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); if (smbuf == NULL) { DHD_ERROR(("%s: failed to allocated memory %d bytes\n", __FUNCTION__, WLC_IOCTL_MAXLEN)); goto send_action_frame_out; } af_params = (wl_af_params_t *) kzalloc(WL_WIFI_AF_PARAMS_SIZE, GFP_KERNEL); if (af_params == NULL) { DHD_ERROR(("%s: unable to allocate frame\n", __FUNCTION__)); goto send_action_frame_out; } memset(&tmp_bssid, 0, ETHER_ADDR_LEN); if (bcm_ether_atoe((const char *)params->bssid, (struct ether_addr *)&tmp_bssid) == 0) { memset(&tmp_bssid, 0, ETHER_ADDR_LEN); error = wldev_ioctl(dev, WLC_GET_BSSID, &tmp_bssid, ETHER_ADDR_LEN, false); if (error) { memset(&tmp_bssid, 0, ETHER_ADDR_LEN); DHD_ERROR(("%s: failed to get bssid, error=%d\n", __FUNCTION__, error)); goto send_action_frame_out; } } if (params->channel < 0) { struct channel_info ci; error = wldev_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci), false); if (error) { DHD_ERROR(("%s: failed to get channel, error=%d\n", __FUNCTION__, error)); goto send_action_frame_out; } tmp_channel = ci.hw_channel; } else { tmp_channel = params->channel; } af_params->channel = tmp_channel; af_params->dwell_time = params->dwell_time; memcpy(&af_params->BSSID, &tmp_bssid, ETHER_ADDR_LEN); action_frame = &af_params->action_frame; action_frame->packetId = 0; memcpy(&action_frame->da, &tmp_bssid, ETHER_ADDR_LEN); action_frame->len = params->len; memcpy(action_frame->data, params->data, action_frame->len); error = wldev_iovar_setbuf(dev, "actframe", af_params, sizeof(wl_af_params_t), smbuf, WLC_IOCTL_MAXLEN, NULL); if (error) { DHD_ERROR(("%s: failed to set action frame, error=%d\n", __FUNCTION__, error)); } send_action_frame_out: if (af_params) kfree(af_params); if (smbuf) kfree(smbuf); if (error) return -1; else return 0; } int wl_android_reassoc(struct net_device *dev, char *command, int total_len) { int error = 0; android_wifi_reassoc_params_t *params = NULL; uint band; chanspec_t channel; u32 params_size; wl_reassoc_params_t reassoc_params; params = (android_wifi_reassoc_params_t *)(command + strlen(CMD_REASSOC) + 1); if (params == NULL) { DHD_ERROR(("%s: Invalid params \n", __FUNCTION__)); return -1; } memset(&reassoc_params, 0, WL_REASSOC_PARAMS_FIXED_SIZE); if (bcm_ether_atoe((const char *)params->bssid, (struct ether_addr *)&reassoc_params.bssid) == 0) { DHD_ERROR(("%s: Invalid bssid \n", __FUNCTION__)); return -1; } if (params->channel < 0) { DHD_ERROR(("%s: Invalid Channel \n", __FUNCTION__)); return -1; } reassoc_params.chanspec_num = 1; channel = params->channel; #ifdef D11AC_IOTYPES if (wl_cfg80211_get_ioctl_version() == 1) { band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_LCHANSPEC_BAND_2G : WL_LCHANSPEC_BAND_5G); reassoc_params.chanspec_list[0] = channel | band | WL_LCHANSPEC_BW_20 | WL_LCHANSPEC_CTL_SB_NONE; } else { band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); reassoc_params.chanspec_list[0] = channel | band | WL_CHANSPEC_BW_20; } #else band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); reassoc_params.chanspec_list[0] = channel | band | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE; #endif /* D11AC_IOTYPES */ params_size = WL_REASSOC_PARAMS_FIXED_SIZE + sizeof(chanspec_t); error = wldev_ioctl(dev, WLC_REASSOC, &reassoc_params, params_size, true); if (error) { DHD_ERROR(("%s: failed to reassoc, error=%d\n", __FUNCTION__, error)); } if (error) return -1; else return 0; } int wl_android_get_wes_mode(struct net_device *dev, char *command, int total_len) { int bytes_written = 0; int mode = 0; mode = wl_cfg80211_get_wes_mode(); bytes_written = snprintf(command, total_len, "%s %d", CMD_GETWESMODE, mode); return bytes_written; } int wl_android_set_wes_mode(struct net_device *dev, char *command, int total_len) { int error = 0; int mode = 0; if (sscanf(command, "%*s %d", &mode) != 1) { DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); return -1; } error = wl_cfg80211_set_wes_mode(mode); if (error) { DHD_ERROR(("%s: Failed to set WES Mode %d, error = %d\n", __FUNCTION__, mode, error)); return -1; } return 0; } int wl_android_get_okc_mode(struct net_device *dev, char *command, int total_len) { int error = 0; int bytes_written = 0; int mode = 0; error = wldev_iovar_getint(dev, "okc_enable", &mode); if (error) { DHD_ERROR(("%s: Failed to get OKC Mode, error = %d\n", __FUNCTION__, error)); return -1; } bytes_written = snprintf(command, total_len, "%s %d", CMD_GETOKCMODE, mode); return bytes_written; } int wl_android_set_okc_mode(struct net_device *dev, char *command, int total_len) { int error = 0; int mode = 0; if (sscanf(command, "%*s %d", &mode) != 1) { DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); return -1; } error = wldev_iovar_setint(dev, "okc_enable", mode); if (error) { DHD_ERROR(("%s: Failed to set OKC Mode %d, error = %d\n", __FUNCTION__, mode, error)); return -1; } if (mode) wldev_iovar_setint(dev, "ccx_enable", 0); return error; } #endif /* WES_SUPPORT */ #endif /* CUSTOMER_HW4 */ #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 */ 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 && !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; } #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; } 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; printk("%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); 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); if (!ret) { if (dhd_dev_init_ioctl(dev) < 0) ret = -EFAULT; } #if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) dhd_wlfc_init(bcmsdh_get_drvdata()); #endif g_wifi_on = TRUE; } exit: dhd_net_if_unlock(dev); return ret; } int wl_android_wifi_off(struct net_device *dev) { int ret = 0; printk("%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) { #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; } 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_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)); } wldev_iovar_setint(dev, "ccx_enable", 0); return error; } #ifdef CUSTOMER_HW4 #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 = 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 /* SUPPORT_AMPDU_MPDU_CMD */ /* SoftAP feature */ #ifdef SUPPORT_AUTO_CHANNEL 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, 4, "%d", channel); DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command)); return 4; } #endif /* SUPPORT_AUTO_CHANNEL */ #ifdef SUPPORT_HIDDEN_AP 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); if (ssid.SSID_len > DOT11_MAX_SSID_LEN) { ssid.SSID_len = DOT11_MAX_SSID_LEN; DHD_ERROR(("%s : Too long SSID Length %d\n", __FUNCTION__, 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; } #endif /* SUPPORT_HIDDEN_AP */ #ifdef SUPPORT_SOFTAP_SINGL_DISASSOC static int wl_android_sta_diassoc(struct net_device *dev, const char* straddr) { scb_val_t scbval; int error = 0; DHD_INFO(("%s: deauth STA %s\n", __FUNCTION__, straddr)); /* Unspecified reason */ scbval.val = htod32(1); bcm_ether_atoe(straddr, &scbval.ea); DHD_ERROR(("%s: deauth STA: "MACDBG " scb_val.val %d\n", __FUNCTION__, MAC2STRDBG(scbval.ea.octet), scbval.val)); error = wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval, sizeof(scb_val_t), true); if (error) { DHD_ERROR(("Fail to DEAUTH station, error = %d\n", error)); } return 1; } #endif /* SUPPORT_SOFTAP_SINGL_DISASSOC */ #ifdef SUPPORT_SET_LPC static int wl_android_set_lpc(struct net_device *dev, const char* string_num) { int lpc_enabled, ret; s32 val = 1; lpc_enabled = bcm_atoi(string_num); DHD_INFO(("%s : HAPD_LPC_ENABLED = %d\n", __FUNCTION__, lpc_enabled)); ret = wldev_ioctl(dev, WLC_DOWN, &val, sizeof(s32), true); if (ret < 0) DHD_ERROR(("WLC_DOWN error %d\n", ret)); wldev_iovar_setint(dev, "lpc", lpc_enabled); ret = wldev_ioctl(dev, WLC_UP, &val, sizeof(s32), true); if (ret < 0) DHD_ERROR(("WLC_UP error %d\n", ret)); return 1; } #endif /* SUPPORT_SET_LPC */ 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 /* CUSTOMER_HW4 */ 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) { DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); return -1; } error = wldev_iovar_setint(dev, "roam_off", mode); if (error) { DHD_ERROR(("%s: Failed to set roaming Mode %d, error = %d\n", __FUNCTION__, mode, error)); return -1; } else DHD_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) { #ifdef CUSTOMER_HW4 /* DO NOT CHANGE THIS: Samsung JBP branch requires 16KB buffer size */ #define PRIVATE_COMMAND_MAX_LEN 16384 #else #define PRIVATE_COMMAND_MAX_LEN 8192 #endif 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) { DHD_ERROR(("%s: too long priavte command\n", __FUNCTION__)); ret = -EINVAL; goto exit; } command = kmalloc((priv_cmd.total_len + 1), 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; } command[priv_cmd.total_len] = '\0'; 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_INFO(("%s, Received regular START command\n", __FUNCTION__)); #ifdef SUPPORT_DEEP_SLEEP trigger_deep_sleep = 1; #else bytes_written = wl_android_wifi_on(net); #endif /* SUPPORT_DEEP_SLEEP */ } 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) { #ifdef SUPPORT_DEEP_SLEEP trigger_deep_sleep = 1; #else bytes_written = wl_android_wifi_off(net); #endif /* SUPPORT_DEEP_SLEEP */ } 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 s32 ret = 0; if ((ret = wl_cfg80211_set_band(net, band)) < 0) { if (ret == BCME_UNSUPPORTED) { /* If roam_var is unsupported, fallback to the original method */ WL_ERR(("WL_HOST_BAND_MGMT defined, " "but roam_band iovar unsupported in the firmware\n")); } else { bytes_written = -1; goto exit; } } if ((band == WLC_BAND_AUTO) || (ret == BCME_UNSUPPORTED)) 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 #ifndef CUSTOMER_SET_COUNTRY /* 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 /* CUSTOMER_SET_COUNTRY */ #endif /* WL_CFG80211 */ #ifdef CUSTOMER_HW4 #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_FULLROAMSCANPERIOD_SET, strlen(CMD_FULLROAMSCANPERIOD_SET)) == 0) { bytes_written = wl_android_set_full_roam_scan_period(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_FULLROAMSCANPERIOD_GET, strlen(CMD_FULLROAMSCANPERIOD_GET)) == 0) { bytes_written = wl_android_get_full_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); } 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 WES_SUPPORT else if (strnicmp(command, CMD_GETROAMSCANCONTROL, strlen(CMD_GETROAMSCANCONTROL)) == 0) { bytes_written = wl_android_get_roam_scan_control(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETROAMSCANCONTROL, strlen(CMD_SETROAMSCANCONTROL)) == 0) { bytes_written = wl_android_set_roam_scan_control(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_GETROAMSCANCHANNELS, strlen(CMD_GETROAMSCANCHANNELS)) == 0) { bytes_written = wl_android_get_roam_scan_channels(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETROAMSCANCHANNELS, strlen(CMD_SETROAMSCANCHANNELS)) == 0) { bytes_written = wl_android_set_roam_scan_channels(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SENDACTIONFRAME, strlen(CMD_SENDACTIONFRAME)) == 0) { bytes_written = wl_android_send_action_frame(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_REASSOC, strlen(CMD_REASSOC)) == 0) { bytes_written = wl_android_reassoc(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_GETSCANCHANNELTIME, strlen(CMD_GETSCANCHANNELTIME)) == 0) { bytes_written = wl_android_get_scan_channel_time(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETSCANCHANNELTIME, strlen(CMD_SETSCANCHANNELTIME)) == 0) { bytes_written = wl_android_set_scan_channel_time(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_GETSCANHOMETIME, strlen(CMD_GETSCANHOMETIME)) == 0) { bytes_written = wl_android_get_scan_home_time(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETSCANHOMETIME, strlen(CMD_SETSCANHOMETIME)) == 0) { bytes_written = wl_android_set_scan_home_time(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_GETSCANHOMEAWAYTIME, strlen(CMD_GETSCANHOMEAWAYTIME)) == 0) { bytes_written = wl_android_get_scan_home_away_time(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETSCANHOMEAWAYTIME, strlen(CMD_SETSCANHOMEAWAYTIME)) == 0) { bytes_written = wl_android_set_scan_home_away_time(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_GETSCANNPROBES, strlen(CMD_GETSCANNPROBES)) == 0) { bytes_written = wl_android_get_scan_nprobes(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETSCANNPROBES, strlen(CMD_SETSCANNPROBES)) == 0) { bytes_written = wl_android_set_scan_nprobes(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_GETWESMODE, strlen(CMD_GETWESMODE)) == 0) { bytes_written = wl_android_get_wes_mode(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETWESMODE, strlen(CMD_SETWESMODE)) == 0) { bytes_written = wl_android_set_wes_mode(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_GETOKCMODE, strlen(CMD_GETOKCMODE)) == 0) { bytes_written = wl_android_get_okc_mode(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETOKCMODE, strlen(CMD_SETOKCMODE)) == 0) { bytes_written = wl_android_set_okc_mode(net, command, priv_cmd.total_len); } #endif /* WES_SUPPORT */ #endif /* CUSTOMER_HW4 */ #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); #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 CUSTOMER_HW4 #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 /* SUPPORT_AMPDU_MPDU_CMD */ #ifdef SUPPORT_AUTO_CHANNEL 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; bytes_written = wl_android_set_auto_channel(net, (const char*)command+skip, command, priv_cmd.total_len); } #endif /* SUPPORT_AUTO_CHANNEL */ #ifdef SUPPORT_HIDDEN_AP 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); } #endif /* SUPPORT_HIDDEN_AP */ #ifdef SUPPORT_SOFTAP_SINGL_DISASSOC 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); } #endif /* SUPPORT_SOFTAP_SINGL_DISASSOC */ #ifdef SUPPORT_SET_LPC else if (strnicmp(command, CMD_HAPD_LPC_ENABLED, strlen(CMD_HAPD_LPC_ENABLED)) == 0) { int skip = strlen(CMD_HAPD_LPC_ENABLED) + 3; wl_android_set_lpc(net, (const char*)command+skip); } #endif /* SUPPORT_SET_LPC */ #ifdef SUPPORT_TRIGGER_HANG_EVENT else if (strnicmp(command, CMD_TEST_FORCE_HANG, strlen(CMD_TEST_FORCE_HANG)) == 0) { net_os_send_hang_message(net); } #endif /* SUPPORT_TRIGGER_HANG_EVENT */ 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 /* CUSTOMER_HW4 */ else if (strnicmp(command, CMD_SETROAMMODE, strlen(CMD_SETROAMMODE)) == 0) bytes_written = wl_android_set_roam_mode(net, command, priv_cmd.total_len); else { 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; #ifdef ENABLE_INSMOD_NO_FW_LOAD dhd_download_fw_on_driverload = FALSE; #endif /* ENABLE_INSMOD_NO_FW_LOAD */ #if defined(CUSTOMER_HW2) || defined(CUSTOMER_HW4) if (!iface_name[0]) { memset(iface_name, 0, IFNAMSIZ); bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); } #endif /* CUSTOMER_HW2 || CUSTOMER_HW4 */ #ifdef WL_GENL wl_genl_init(); #endif return ret; } int wl_android_exit(void) { int ret = 0; #ifdef WL_GENL wl_genl_deinit(); #endif /* WL_GENL */ return ret; } void wl_android_post_init(void) { #ifdef ENABLE_4335BT_WAR bcm_bt_unlock(lock_cookie_wifi); printk("%s: btlock released\n", __FUNCTION__); #endif /* ENABLE_4335BT_WAR */ 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; } } #ifdef WL_GENL /* Generic Netlink Initializaiton */ static int wl_genl_init(void) { int ret; WL_DBG(("GEN Netlink Init\n\n")); /* register new family */ ret = genl_register_family(&wl_genl_family); if (ret != 0) goto failure; /* register functions (commands) of the new family */ ret = genl_register_ops(&wl_genl_family, &wl_genl_ops); if (ret != 0) { WL_ERR(("register ops failed: %i\n", ret)); genl_unregister_family(&wl_genl_family); goto failure; } ret = genl_register_mc_group(&wl_genl_family, &wl_genl_mcast); if (ret != 0) { WL_ERR(("register mc_group failed: %i\n", ret)); genl_unregister_ops(&wl_genl_family, &wl_genl_ops); genl_unregister_family(&wl_genl_family); goto failure; } return 0; failure: WL_ERR(("Registering Netlink failed!!\n")); return -1; } /* Generic netlink deinit */ static int wl_genl_deinit(void) { if (genl_unregister_ops(&wl_genl_family, &wl_genl_ops) < 0) WL_ERR(("Unregister wl_genl_ops failed\n")); if (genl_unregister_family(&wl_genl_family) < 0) WL_ERR(("Unregister wl_genl_ops failed\n")); return 0; } s32 wl_event_to_bcm_event(u16 event_type) { u16 event = -1; switch (event_type) { case WLC_E_SERVICE_FOUND: event = BCM_E_SVC_FOUND; break; case WLC_E_P2PO_ADD_DEVICE: event = BCM_E_DEV_FOUND; break; case WLC_E_P2PO_DEL_DEVICE: event = BCM_E_DEV_LOST; break; /* Above events are supported from BCM Supp ver 47 Onwards */ default: WL_ERR(("Event not supported\n")); } return event; } s32 wl_genl_send_msg( struct net_device *ndev, u32 event_type, u8 *buf, u16 len, u8 *subhdr, u16 subhdr_len) { int ret = 0; struct sk_buff *skb; void *msg; u32 attr_type = 0; bcm_event_hdr_t *hdr = NULL; int mcast = 1; /* By default sent as mutlicast type */ int pid = 0; u8 *ptr = NULL, *p = NULL; u32 tot_len = sizeof(bcm_event_hdr_t) + subhdr_len + len; u16 kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL; WL_DBG(("Enter \n")); /* Decide between STRING event and Data event */ if (event_type == 0) attr_type = BCM_GENL_ATTR_STRING; else attr_type = BCM_GENL_ATTR_MSG; skb = genlmsg_new(NLMSG_GOODSIZE, kflags); if (skb == NULL) { ret = -ENOMEM; goto out; } msg = genlmsg_put(skb, 0, 0, &wl_genl_family, 0, BCM_GENL_CMD_MSG); if (msg == NULL) { ret = -ENOMEM; goto out; } if (attr_type == BCM_GENL_ATTR_STRING) { /* Add a BCM_GENL_MSG attribute. Since it is specified as a string. * make sure it is null terminated */ if (subhdr || subhdr_len) { WL_ERR(("No sub hdr support for the ATTR STRING type \n")); ret = -EINVAL; goto out; } ret = nla_put_string(skb, BCM_GENL_ATTR_STRING, buf); if (ret != 0) { WL_ERR(("nla_put_string failed\n")); goto out; } } else { /* ATTR_MSG */ /* Create a single buffer for all */ p = ptr = kzalloc(tot_len, kflags); if (!ptr) { ret = -ENOMEM; WL_ERR(("ENOMEM!!\n")); goto out; } /* Include the bcm event header */ hdr = (bcm_event_hdr_t *)ptr; hdr->event_type = wl_event_to_bcm_event(event_type); hdr->len = len + subhdr_len; ptr += sizeof(bcm_event_hdr_t); /* Copy subhdr (if any) */ if (subhdr && subhdr_len) { memcpy(ptr, subhdr, subhdr_len); ptr += subhdr_len; } /* Copy the data */ if (buf && len) { memcpy(ptr, buf, len); } ret = nla_put(skb, BCM_GENL_ATTR_MSG, tot_len, p); if (ret != 0) { WL_ERR(("nla_put_string failed\n")); goto out; } } if (mcast) { int err = 0; /* finalize the message */ genlmsg_end(skb, msg); /* NETLINK_CB(skb).dst_group = 1; */ if ((err = genlmsg_multicast(skb, 0, wl_genl_mcast.id, GFP_ATOMIC)) < 0) WL_ERR(("genlmsg_multicast for attr(%d) failed. Error:%d \n", attr_type, err)); else WL_DBG(("Multicast msg sent successfully. attr_type:%d len:%d \n", attr_type, tot_len)); } else { NETLINK_CB(skb).dst_group = 0; /* Not in multicast group */ /* finalize the message */ genlmsg_end(skb, msg); /* send the message back */ if (genlmsg_unicast(&init_net, skb, pid) < 0) WL_ERR(("genlmsg_unicast failed\n")); } out: if (p) kfree(p); if (ret) nlmsg_free(skb); return ret; } static s32 wl_genl_handle_msg( struct sk_buff *skb, struct genl_info *info) { struct nlattr *na; u8 *data = NULL; WL_DBG(("Enter \n")); if (info == NULL) { return -EINVAL; } na = info->attrs[BCM_GENL_ATTR_MSG]; if (!na) { WL_ERR(("nlattribute NULL\n")); return -EINVAL; } data = (char *)nla_data(na); if (!data) { WL_ERR(("Invalid data\n")); return -EINVAL; } else { /* Handle the data */ WL_DBG(("Data received from pid (%d) \n", info->snd_pid)); } return 0; } #endif /* WL_GENL */ /** * Functions for Android WiFi card detection */ #if defined(CONFIG_WIFI_CONTROL_FUNC) bool g_wifi_poweron = FALSE; 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) { int ret = 0; DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); if (wifi_control_data && wifi_control_data->set_power) { #ifdef ENABLE_4335BT_WAR if (on) { printk("WiFi: trying to acquire BT lock\n"); if (bcm_bt_lock(lock_cookie_wifi) != 0) printk("** WiFi: timeout in acquiring bt lock**\n"); printk("%s: btlock acquired\n", __FUNCTION__); } else { /* For a exceptional case, release btlock */ bcm_bt_unlock(lock_cookie_wifi); } #endif /* ENABLE_4335BT_WAR */ ret = wifi_control_data->set_power(on); } if (msec && !ret) msleep(msec); return ret; } #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) { int err; 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; err = wifi_set_power(1, 200); /* Power On */ if (unlikely(err)) { DHD_ERROR(("%s: set_power failed. err=%d\n", __FUNCTION__, err)); wifi_set_power(0, WIFI_TURNOFF_DELAY); /* WL_REG_ON state unknown, Power off forcely */ } else { wifi_set_carddetect(1); /* CardDetect (0->1) */ g_wifi_poweron = TRUE; } 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; if (g_wifi_poweron) { wifi_set_power(0, WIFI_TURNOFF_DELAY); /* Power Off */ wifi_set_carddetect(0); /* CardDetect (1->0) */ g_wifi_poweron = FALSE; } up(&wifi_control_sem); return 0; } static int wifi_suspend(struct platform_device *pdev, pm_message_t state) { DHD_TRACE(("##> %s\n", __FUNCTION__)); #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) { int ret = 0; DHD_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) { DHD_TRACE(("## Unregister platform_driver_register\n")); platform_driver_unregister(&wifi_device); platform_driver_unregister(&wifi_device_legacy); } #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */