| /* |
| * 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 323797 2012-03-27 01:27:20Z $ |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/netdevice.h> |
| |
| #include <wl_android.h> |
| #include <wldev_common.h> |
| #include <wlioctl.h> |
| #include <bcmutils.h> |
| #include <linux_osl.h> |
| #include <dhd_dbg.h> |
| #include <dngl_stats.h> |
| #include <dhd.h> |
| #include <bcmsdbus.h> |
| #ifdef WL_CFG80211 |
| #include <wl_cfg80211.h> |
| #endif |
| #if defined(CONFIG_WIFI_CONTROL_FUNC) |
| #include <linux/platform_device.h> |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) |
| #include <linux/wlan_plat.h> |
| #else |
| #include <linux/wifi_tiwlan.h> |
| #endif |
| #endif /* CONFIG_WIFI_CONTROL_FUNC */ |
| |
| /* |
| * Android private command strings, PLEASE define new private commands here |
| * so they can be updated easily in the future (if needed) |
| */ |
| |
| #define CMD_START "START" |
| #define CMD_STOP "STOP" |
| #define CMD_SCAN_ACTIVE "SCAN-ACTIVE" |
| #define CMD_SCAN_PASSIVE "SCAN-PASSIVE" |
| #define CMD_RSSI "RSSI" |
| #define CMD_LINKSPEED "LINKSPEED" |
| #define CMD_RXFILTER_START "RXFILTER-START" |
| #define CMD_RXFILTER_STOP "RXFILTER-STOP" |
| #define CMD_RXFILTER_ADD "RXFILTER-ADD" |
| #define CMD_RXFILTER_REMOVE "RXFILTER-REMOVE" |
| #define CMD_BTCOEXSCAN_START "BTCOEXSCAN-START" |
| #define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" |
| #define CMD_BTCOEXMODE "BTCOEXMODE" |
| #define CMD_SETSUSPENDOPT "SETSUSPENDOPT" |
| #define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR" |
| #define CMD_SETFWPATH "SETFWPATH" |
| #define CMD_SETBAND "SETBAND" |
| #define CMD_GETBAND "GETBAND" |
| #define CMD_COUNTRY "COUNTRY" |
| #define CMD_P2P_SET_NOA "P2P_SET_NOA" |
| #define CMD_P2P_SET_PS "P2P_SET_PS" |
| #define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" |
| |
| |
| #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 */ |
| |
| typedef struct android_wifi_priv_cmd { |
| char *buf; |
| int used_len; |
| int total_len; |
| } android_wifi_priv_cmd; |
| |
| /** |
| * Extern function declarations (TODO: move them to dhd_linux.h) |
| */ |
| void dhd_customer_gpio_wlan_ctrl(int onoff); |
| uint dhd_dev_reset(struct net_device *dev, uint8 flag); |
| void dhd_dev_init_ioctl(struct net_device *dev); |
| #ifdef WL_CFG80211 |
| int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); |
| int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); |
| #else |
| int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) |
| { return 0; } |
| int wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) |
| { return 0; } |
| int wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len) |
| { return 0; } |
| int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) |
| { return 0; } |
| #endif |
| extern int dhd_os_check_if_up(void *dhdp); |
| extern void *bcmsdh_get_drvdata(void); |
| #ifdef PROP_TXSTATUS |
| extern int dhd_wlfc_init(dhd_pub_t *dhd); |
| extern void dhd_wlfc_deinit(dhd_pub_t *dhd); |
| #endif |
| |
| extern bool ap_fw_loaded; |
| #ifdef CUSTOMER_HW2 |
| extern char iface_name[IFNAMSIZ]; |
| #endif |
| |
| /** |
| * Local (static) functions and variables |
| */ |
| |
| /* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first |
| * time (only) in dhd_open, subsequential wifi on will be handled by |
| * wl_android_wifi_on |
| */ |
| static int g_wifi_on = TRUE; |
| |
| /** |
| * Local (static) function definitions |
| */ |
| static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len) |
| { |
| int link_speed; |
| int bytes_written; |
| int error; |
| |
| error = wldev_get_link_speed(net, &link_speed); |
| if (error) |
| return -1; |
| |
| /* Convert Kbps to Android Mbps */ |
| link_speed = link_speed / 1000; |
| bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed); |
| DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command)); |
| return bytes_written; |
| } |
| |
| static int wl_android_get_rssi(struct net_device *net, char *command, int total_len) |
| { |
| wlc_ssid_t ssid = {0}; |
| int rssi; |
| int bytes_written = 0; |
| int error; |
| |
| error = wldev_get_rssi(net, &rssi); |
| if (error) |
| return -1; |
| |
| error = wldev_get_ssid(net, &ssid); |
| if (error) |
| return -1; |
| if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) { |
| DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__)); |
| } else { |
| memcpy(command, ssid.SSID, ssid.SSID_len); |
| bytes_written = ssid.SSID_len; |
| } |
| bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi); |
| DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written)); |
| return bytes_written; |
| } |
| |
| static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len) |
| { |
| int suspend_flag; |
| int ret_now; |
| int ret = 0; |
| |
| suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0'; |
| |
| if (suspend_flag != 0) |
| suspend_flag = 1; |
| ret_now = net_os_set_suspend_disable(dev, suspend_flag); |
| |
| if (ret_now != suspend_flag) { |
| if (!(ret = net_os_set_suspend(dev, ret_now))) |
| DHD_INFO(("%s: Suspend Flag %d -> %d\n", |
| __FUNCTION__, ret_now, suspend_flag)); |
| else |
| DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); |
| } |
| 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 PNO_SUPPORT |
| static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) |
| { |
| wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; |
| int res = -1; |
| int nssid = 0; |
| cmd_tlv_t *cmd_tlv_temp; |
| char *str_ptr; |
| int tlv_size_left; |
| int pno_time = 0; |
| int pno_repeat = 0; |
| int pno_freq_expo_max = 0; |
| |
| #ifdef PNO_SET_DEBUG |
| int i; |
| char pno_in_example[] = { |
| 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', |
| 'S', '1', '2', '0', |
| 'S', |
| 0x05, |
| 'd', 'l', 'i', 'n', 'k', |
| 'S', |
| 0x04, |
| 'G', 'O', 'O', 'G', |
| 'T', |
| '0', 'B', |
| 'R', |
| '2', |
| 'M', |
| '2', |
| 0x00 |
| }; |
| #endif /* PNO_SET_DEBUG */ |
| |
| DHD_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); |
| |
| if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { |
| DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); |
| goto exit_proc; |
| } |
| |
| |
| #ifdef PNO_SET_DEBUG |
| memcpy(command, pno_in_example, sizeof(pno_in_example)); |
| for (i = 0; i < sizeof(pno_in_example); i++) |
| printf("%02X ", command[i]); |
| printf("\n"); |
| total_len = sizeof(pno_in_example); |
| #endif |
| |
| str_ptr = command + strlen(CMD_PNOSETUP_SET); |
| tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET); |
| |
| cmd_tlv_temp = (cmd_tlv_t *)str_ptr; |
| memset(ssids_local, 0, sizeof(ssids_local)); |
| |
| if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && |
| (cmd_tlv_temp->version == PNO_TLV_VERSION) && |
| (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) { |
| |
| str_ptr += sizeof(cmd_tlv_t); |
| tlv_size_left -= sizeof(cmd_tlv_t); |
| |
| if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, |
| MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) { |
| DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); |
| goto exit_proc; |
| } else { |
| if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) { |
| DHD_ERROR(("%s scan duration corrupted field size %d\n", |
| __FUNCTION__, tlv_size_left)); |
| goto exit_proc; |
| } |
| str_ptr++; |
| pno_time = simple_strtoul(str_ptr, &str_ptr, 16); |
| DHD_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); |
| |
| if (str_ptr[0] != 0) { |
| if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { |
| DHD_ERROR(("%s pno repeat : corrupted field\n", |
| __FUNCTION__)); |
| goto exit_proc; |
| } |
| str_ptr++; |
| pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); |
| DHD_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); |
| if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { |
| DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", |
| __FUNCTION__)); |
| goto exit_proc; |
| } |
| str_ptr++; |
| pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); |
| DHD_INFO(("%s: pno_freq_expo_max=%d\n", |
| __FUNCTION__, pno_freq_expo_max)); |
| } |
| } |
| } else { |
| DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); |
| goto exit_proc; |
| } |
| |
| res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max); |
| |
| exit_proc: |
| return res; |
| } |
| #endif /* PNO_SUPPORT */ |
| |
| static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len) |
| { |
| int ret; |
| int bytes_written = 0; |
| |
| ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command); |
| if (ret) |
| return 0; |
| bytes_written = sizeof(struct ether_addr); |
| return bytes_written; |
| } |
| |
| /** |
| * 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); |
| dhd_dev_init_ioctl(dev); |
| #ifdef PROP_TXSTATUS |
| 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) { |
| #ifdef PROP_TXSTATUS |
| dhd_wlfc_deinit(bcmsdh_get_drvdata()); |
| #endif |
| dhd_dev_reset(dev, 1); |
| 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; |
| } |
| |
| int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) |
| { |
| int ret = 0; |
| char *command = NULL; |
| int bytes_written = 0; |
| android_wifi_priv_cmd priv_cmd; |
| |
| net_os_wake_lock(net); |
| |
| if (!ifr->ifr_data) { |
| ret = -EINVAL; |
| goto exit; |
| } |
| if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { |
| ret = -EFAULT; |
| goto exit; |
| } |
| command = kmalloc(priv_cmd.total_len, GFP_KERNEL); |
| if (!command) |
| { |
| DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__)); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) { |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name)); |
| |
| if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) { |
| DHD_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) { |
| 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) { |
| 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); |
| } |
| else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) { |
| bytes_written = net_os_set_packet_filter(net, 1); |
| } |
| else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) { |
| bytes_written = net_os_set_packet_filter(net, 0); |
| } |
| else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) { |
| int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0'; |
| bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num); |
| } |
| else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) { |
| int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0'; |
| bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num); |
| } |
| else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) { |
| /* TBD: BTCOEXSCAN-START */ |
| } |
| else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) { |
| /* TBD: BTCOEXSCAN-STOP */ |
| } |
| else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) { |
| uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0'; |
| |
| if (mode == 1) |
| net_os_set_packet_filter(net, 0); /* DHCP starts */ |
| else |
| net_os_set_packet_filter(net, 1); /* DHCP ends */ |
| #ifdef WL_CFG80211 |
| bytes_written = wl_cfg80211_set_btcoex_dhcp(net, command); |
| #endif |
| } |
| else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { |
| bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); |
| } |
| else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { |
| uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; |
| bytes_written = wldev_set_band(net, band); |
| } |
| else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) { |
| bytes_written = wl_android_get_band(net, command, priv_cmd.total_len); |
| } |
| 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); |
| } |
| #ifdef PNO_SUPPORT |
| else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) { |
| bytes_written = dhd_dev_pno_reset(net); |
| } |
| else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) { |
| bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len); |
| } |
| else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) { |
| uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0'; |
| bytes_written = dhd_dev_pno_enable(net, pfn_enabled); |
| } |
| #endif |
| else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) { |
| bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len); |
| } |
| else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) { |
| int skip = strlen(CMD_P2P_SET_NOA) + 1; |
| bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, |
| priv_cmd.total_len - skip); |
| } |
| else if (strnicmp(command, CMD_P2P_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 { |
| 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 */ |
| #ifdef CUSTOMER_HW2 |
| if (!iface_name[0]) { |
| memset(iface_name, 0, IFNAMSIZ); |
| bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); |
| } |
| #endif /* CUSTOMER_HW2 */ |
| return ret; |
| } |
| |
| int wl_android_exit(void) |
| { |
| int ret = 0; |
| |
| return ret; |
| } |
| |
| void wl_android_post_init(void) |
| { |
| if (!dhd_download_fw_on_driverload) { |
| /* Call customer gpio to turn off power with WL_REG_ON signal */ |
| dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); |
| g_wifi_on = 0; |
| } |
| } |
| /** |
| * Functions for Android WiFi card detection |
| */ |
| #if defined(CONFIG_WIFI_CONTROL_FUNC) |
| |
| static int g_wifidev_registered = 0; |
| static struct semaphore wifi_control_sem; |
| static struct wifi_platform_data *wifi_control_data = NULL; |
| static struct resource *wifi_irqres = NULL; |
| |
| static int wifi_add_dev(void); |
| static void wifi_del_dev(void); |
| |
| int wl_android_wifictrl_func_add(void) |
| { |
| int ret = 0; |
| sema_init(&wifi_control_sem, 0); |
| |
| ret = wifi_add_dev(); |
| if (ret) { |
| DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__)); |
| return ret; |
| } |
| g_wifidev_registered = 1; |
| |
| /* Waiting callback after platform_driver_register is done or exit with error */ |
| if (down_timeout(&wifi_control_sem, msecs_to_jiffies(1000)) != 0) { |
| ret = -EINVAL; |
| DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__)); |
| } |
| |
| return ret; |
| } |
| |
| void wl_android_wifictrl_func_del(void) |
| { |
| if (g_wifidev_registered) |
| { |
| wifi_del_dev(); |
| g_wifidev_registered = 0; |
| } |
| } |
| |
| void* wl_android_prealloc(int section, unsigned long size) |
| { |
| void *alloc_ptr = NULL; |
| if (wifi_control_data && wifi_control_data->mem_prealloc) { |
| alloc_ptr = wifi_control_data->mem_prealloc(section, size); |
| if (alloc_ptr) { |
| DHD_INFO(("success alloc section %d\n", section)); |
| if (size != 0L) |
| bzero(alloc_ptr, size); |
| return alloc_ptr; |
| } |
| } |
| |
| DHD_ERROR(("can't alloc section %d\n", section)); |
| return NULL; |
| } |
| |
| int wifi_get_irq_number(unsigned long *irq_flags_ptr) |
| { |
| if (wifi_irqres) { |
| *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK; |
| return (int)wifi_irqres->start; |
| } |
| #ifdef CUSTOM_OOB_GPIO_NUM |
| return CUSTOM_OOB_GPIO_NUM; |
| #else |
| return -1; |
| #endif |
| } |
| |
| int wifi_set_power(int on, unsigned long msec) |
| { |
| DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); |
| if (wifi_control_data && wifi_control_data->set_power) { |
| wifi_control_data->set_power(on); |
| } |
| if (msec) |
| msleep(msec); |
| return 0; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) |
| int wifi_get_mac_addr(unsigned char *buf) |
| { |
| DHD_ERROR(("%s\n", __FUNCTION__)); |
| if (!buf) |
| return -EINVAL; |
| if (wifi_control_data && wifi_control_data->get_mac_addr) { |
| return wifi_control_data->get_mac_addr(buf); |
| } |
| return -EOPNOTSUPP; |
| } |
| #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) */ |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) |
| void *wifi_get_country_code(char *ccode) |
| { |
| DHD_TRACE(("%s\n", __FUNCTION__)); |
| if (!ccode) |
| return NULL; |
| if (wifi_control_data && wifi_control_data->get_country_code) { |
| return wifi_control_data->get_country_code(ccode); |
| } |
| return NULL; |
| } |
| #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */ |
| |
| static int wifi_set_carddetect(int on) |
| { |
| DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); |
| if (wifi_control_data && wifi_control_data->set_carddetect) { |
| wifi_control_data->set_carddetect(on); |
| } |
| return 0; |
| } |
| |
| static int wifi_probe(struct platform_device *pdev) |
| { |
| struct wifi_platform_data *wifi_ctrl = |
| (struct wifi_platform_data *)(pdev->dev.platform_data); |
| |
| 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); |
| |
| DHD_ERROR(("## %s\n", __FUNCTION__)); |
| wifi_control_data = wifi_ctrl; |
| |
| wifi_set_power(0, 0); /* 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) |
| { |
| 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) |
| { |
| DHD_TRACE(("## Calling platform_driver_register\n")); |
| platform_driver_register(&wifi_device); |
| platform_driver_register(&wifi_device_legacy); |
| return 0; |
| } |
| |
| static void wifi_del_dev(void) |
| { |
| DHD_TRACE(("## Unregister platform_driver_register\n")); |
| platform_driver_unregister(&wifi_device); |
| platform_driver_unregister(&wifi_device_legacy); |
| } |
| #endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ |