| /* |
| * Linux cfg80211 driver - Android initial functions |
| * |
| * Copyright 1999-2016, Broadcom Corporation |
| * All rights reserved, |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * This software is provided by the copyright holder "as is" and any express or |
| * implied warranties, including, but not limited to, the implied warranties of |
| * merchantability and fitness for a particular purpose are disclaimed. In no event |
| * shall copyright holder be liable for any direct, indirect, incidental, special, |
| * exemplary, or consequential damages (including, but not limited to, procurement |
| * of substitute goods or services; loss of use, data, or profits; or business |
| * interruption) however caused and on any theory of liability, whether in |
| * contract, strict liability, or tort (including negligence or otherwise) arising |
| * in any way out of the use of this software, even if advised of the possibility |
| * of such damage |
| * |
| * |
| * <<Broadcom-WL-IPTag/Open:>> |
| * |
| * $Id: wl_android.c 608788 2015-12-29 10:59:33Z $ |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/netdevice.h> |
| #include <net/netlink.h> |
| #ifdef CONFIG_COMPAT |
| #include <linux/compat.h> |
| #endif |
| |
| #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 <dhd_config.h> |
| #include <proto/bcmip.h> |
| #ifdef PNO_SUPPORT |
| #include <dhd_pno.h> |
| #endif |
| #ifdef BCMSDIO |
| #include <bcmsdbus.h> |
| #endif |
| #ifdef WL_CFG80211 |
| #ifdef WL_CFG80211_V1 |
| #include <wl_cfg80211_v1.h> |
| #else |
| #include <wl_cfg80211.h> |
| #endif /* WL_CFG80211_V1 */ |
| #endif |
| #ifdef WL_NAN |
| #include <wl_cfgnan.h> |
| #endif /* WL_NAN */ |
| #ifdef DHDTCPACK_SUPPRESS |
| #include <dhd_ip.h> |
| #endif /* DHDTCPACK_SUPPRESS */ |
| |
| #ifndef WL_CFG80211 |
| #define htod32(i) i |
| #define htod16(i) i |
| #define dtoh32(i) i |
| #define dtoh16(i) i |
| #define htodchanspec(i) i |
| #define dtohchanspec(i) i |
| #endif |
| |
| uint android_msg_level = ANDROID_ERROR_LEVEL; |
| |
| /** |
| * Extern function declarations (TODO: move them to dhd_linux.h) |
| */ |
| int dhd_net_bus_devreset(struct net_device *dev, uint8 flag); |
| int dhd_dev_init_ioctl(struct net_device *dev); |
| #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; |
| extern char iface_name[IFNAMSIZ]; |
| |
| /** |
| * 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 |
| */ |
| int g_wifi_on = TRUE; |
| |
| #ifdef WL_CFG80211 |
| static const char * |
| get_string_by_separator(char *result, int result_len, const char *src, char separator) |
| { |
| char *end = result + result_len - 1; |
| while ((result != end) && (*src != separator) && (*src)) { |
| *result++ = *src++; |
| } |
| *result = 0; |
| if (*src == separator) { |
| ++src; |
| } |
| return src; |
| } |
| |
| int |
| wl_android_set_roam_offload_bssid_list(struct net_device *dev, const char *cmd) |
| { |
| char sbuf[32]; |
| int i, cnt, size, err, ioctl_buf_len; |
| roamoffl_bssid_list_t *bssid_list; |
| const char *str = cmd; |
| char *ioctl_buf; |
| #ifdef WL_CFG80211_V1 |
| dhd_pub_t *dhdp = wl_cfg80211_get_dhdp(); |
| #else |
| dhd_pub_t *dhdp = wl_cfg80211_get_dhdp(dev); |
| #endif /* WL_CFG80211_V1 */ |
| |
| str = get_string_by_separator(sbuf, 32, str, ','); |
| cnt = bcm_atoi(sbuf); |
| cnt = MIN(cnt, MAX_ROAMOFFL_BSSID_NUM); |
| |
| if ((cnt > 0) && |
| (((dhdp->op_mode & DHD_FLAG_STA_MODE) && (dhdp->op_mode & DHD_FLAG_HOSTAP_MODE)) || |
| FALSE)) { |
| ANDROID_ERROR(("Can't set ROAMOFFL_BSSID when enabled STA-SoftAP or WES\n")); |
| return -EINVAL; |
| } |
| |
| size = sizeof(int32) + sizeof(struct ether_addr) * cnt; |
| ANDROID_ERROR(("ROAM OFFLOAD BSSID LIST %d BSSIDs, size %d\n", cnt, size)); |
| bssid_list = kmalloc(size, GFP_KERNEL); |
| if (bssid_list == NULL) { |
| ANDROID_ERROR(("%s: memory alloc for bssid list(%d) failed\n", |
| __FUNCTION__, size)); |
| return -ENOMEM; |
| } |
| ioctl_buf_len = size + 64; |
| ioctl_buf = kmalloc(ioctl_buf_len, GFP_KERNEL); |
| if (ioctl_buf == NULL) { |
| ANDROID_ERROR(("%s: memory alloc for ioctl_buf(%d) failed\n", |
| __FUNCTION__, ioctl_buf_len)); |
| kfree(bssid_list); |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < cnt; i++) { |
| str = get_string_by_separator(sbuf, 32, str, ','); |
| bcm_ether_atoe(sbuf, &bssid_list->bssid[i]); |
| } |
| |
| bssid_list->cnt = (int32)cnt; |
| err = wldev_iovar_setbuf(dev, "roamoffl_bssid_list", |
| bssid_list, size, ioctl_buf, ioctl_buf_len, NULL); |
| kfree(bssid_list); |
| kfree(ioctl_buf); |
| |
| return err; |
| } |
| |
| int |
| wl_android_set_ap_mac_list(struct net_device *dev, int macmode, struct maclist *maclist) |
| { |
| int i, j, match; |
| int ret = 0; |
| char mac_buf[MAX_NUM_OF_ASSOCLIST * |
| sizeof(struct ether_addr) + sizeof(uint)] = {0}; |
| struct maclist *assoc_maclist = (struct maclist *)mac_buf; |
| |
| /* set filtering mode */ |
| if ((ret = wldev_ioctl(dev, WLC_SET_MACMODE, &macmode, sizeof(macmode), true)) != 0) { |
| ANDROID_ERROR(("%s : WLC_SET_MACMODE error=%d\n", __FUNCTION__, ret)); |
| return ret; |
| } |
| if (macmode != MACLIST_MODE_DISABLED) { |
| /* set the MAC filter list */ |
| if ((ret = wldev_ioctl(dev, WLC_SET_MACLIST, maclist, |
| sizeof(int) + sizeof(struct ether_addr) * maclist->count, true)) != 0) { |
| ANDROID_ERROR(("%s : WLC_SET_MACLIST error=%d\n", __FUNCTION__, ret)); |
| return ret; |
| } |
| /* get the current list of associated STAs */ |
| assoc_maclist->count = MAX_NUM_OF_ASSOCLIST; |
| if ((ret = wldev_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, |
| sizeof(mac_buf), false)) != 0) { |
| ANDROID_ERROR(("%s : WLC_GET_ASSOCLIST error=%d\n", __FUNCTION__, ret)); |
| return ret; |
| } |
| /* do we have any STA associated? */ |
| if (assoc_maclist->count) { |
| /* iterate each associated STA */ |
| for (i = 0; i < assoc_maclist->count; i++) { |
| match = 0; |
| /* compare with each entry */ |
| for (j = 0; j < maclist->count; j++) { |
| ANDROID_INFO(("%s : associated="MACDBG " list="MACDBG "\n", |
| __FUNCTION__, MAC2STRDBG(assoc_maclist->ea[i].octet), |
| MAC2STRDBG(maclist->ea[j].octet))); |
| if (memcmp(assoc_maclist->ea[i].octet, |
| maclist->ea[j].octet, ETHER_ADDR_LEN) == 0) { |
| match = 1; |
| break; |
| } |
| } |
| /* do conditional deauth */ |
| /* "if not in the allow list" or "if in the deny list" */ |
| if ((macmode == MACLIST_MODE_ALLOW && !match) || |
| (macmode == MACLIST_MODE_DENY && match)) { |
| scb_val_t scbval; |
| |
| scbval.val = htod32(1); |
| memcpy(&scbval.ea, &assoc_maclist->ea[i], |
| ETHER_ADDR_LEN); |
| if ((ret = wldev_ioctl(dev, |
| WLC_SCB_DEAUTHENTICATE_FOR_REASON, |
| &scbval, sizeof(scb_val_t), true)) != 0) |
| ANDROID_ERROR(("%s WLC_SCB_DEAUTHENTICATE error=%d\n", |
| __FUNCTION__, ret)); |
| } |
| } |
| } |
| } |
| return ret; |
| } |
| #endif |
| |
| /** |
| * 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; |
| #ifdef IAPSTA_PREINIT |
| int bytes_written = 0; |
| struct dhd_conf *conf; |
| #endif |
| |
| if (!dev) { |
| ANDROID_ERROR(("%s: dev is null\n", __FUNCTION__)); |
| return -EINVAL; |
| } |
| |
| printf("%s in 1\n", __FUNCTION__); |
| dhd_net_if_lock(dev); |
| printf("%s in 2: g_wifi_on=%d\n", __FUNCTION__, g_wifi_on); |
| if (!g_wifi_on) { |
| do { |
| dhd_net_wifi_platform_set_power(dev, TRUE, WIFI_TURNON_DELAY); |
| #ifdef BCMSDIO |
| ret = dhd_net_bus_resume(dev, 0); |
| #endif /* BCMSDIO */ |
| #ifdef BCMPCIE |
| ret = dhd_net_bus_devreset(dev, FALSE); |
| #endif /* BCMPCIE */ |
| if (ret == 0) { |
| break; |
| } |
| ANDROID_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n", |
| retry)); |
| #ifdef BCMPCIE |
| dhd_net_bus_devreset(dev, TRUE); |
| #endif /* BCMPCIE */ |
| dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY); |
| } while (retry-- > 0); |
| if (ret != 0) { |
| ANDROID_ERROR(("\nfailed to power up wifi chip, max retry reached **\n\n")); |
| goto exit; |
| } |
| #ifdef BCMSDIO |
| ret = dhd_net_bus_devreset(dev, FALSE); |
| if (ret) |
| goto err; |
| dhd_net_bus_resume(dev, 1); |
| #endif /* BCMSDIO */ |
| |
| #ifndef BCMPCIE |
| if (!ret) { |
| if (dhd_dev_init_ioctl(dev) < 0) { |
| ret = -EFAULT; |
| goto err; |
| } |
| } |
| #endif /* !BCMPCIE */ |
| |
| #ifdef IAPSTA_PREINIT |
| conf = dhd_get_conf(dev); |
| if (conf) { |
| wl_android_ext_priv_cmd(dev, conf->iapsta_init, 0, &bytes_written); |
| wl_android_ext_priv_cmd(dev, conf->iapsta_config, 0, &bytes_written); |
| wl_android_ext_priv_cmd(dev, conf->iapsta_enable, 0, &bytes_written); |
| } |
| #endif |
| g_wifi_on = TRUE; |
| } |
| |
| exit: |
| printf("%s: Success\n", __FUNCTION__); |
| dhd_net_if_unlock(dev); |
| return ret; |
| |
| #ifdef BCMSDIO |
| err: |
| dhd_net_bus_devreset(dev, TRUE); |
| dhd_net_bus_suspend(dev); |
| dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY); |
| printf("%s: Failed\n", __FUNCTION__); |
| dhd_net_if_unlock(dev); |
| return ret; |
| #endif |
| } |
| |
| int wl_android_wifi_off(struct net_device *dev, bool on_failure) |
| { |
| int ret = 0; |
| |
| if (!dev) { |
| ANDROID_ERROR(("%s: dev is null\n", __FUNCTION__)); |
| return -EINVAL; |
| } |
| |
| printf("%s in 1\n", __FUNCTION__); |
| dhd_net_if_lock(dev); |
| printf("%s in 2: g_wifi_on=%d, on_failure=%d\n", __FUNCTION__, g_wifi_on, on_failure); |
| if (g_wifi_on || on_failure) { |
| #if defined(BCMSDIO) || defined(BCMPCIE) |
| ret = dhd_net_bus_devreset(dev, TRUE); |
| #ifdef BCMSDIO |
| dhd_net_bus_suspend(dev); |
| #endif /* BCMSDIO */ |
| #endif /* BCMSDIO || BCMPCIE */ |
| dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY); |
| g_wifi_on = FALSE; |
| } |
| printf("%s out\n", __FUNCTION__); |
| dhd_net_if_unlock(dev); |
| |
| return ret; |
| } |
| |
| #define NETLINK_OXYGEN 30 |
| #define AIBSS_BEACON_TIMEOUT 10 |
| |
| static struct sock *nl_sk = NULL; |
| |
| static void wl_netlink_recv(struct sk_buff *skb) |
| { |
| ANDROID_ERROR(("netlink_recv called\n")); |
| } |
| |
| static int wl_netlink_init(void) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) |
| struct netlink_kernel_cfg cfg = { |
| .input = wl_netlink_recv, |
| }; |
| #endif |
| |
| if (nl_sk != NULL) { |
| ANDROID_ERROR(("nl_sk already exist\n")); |
| return BCME_ERROR; |
| } |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) |
| nl_sk = netlink_kernel_create(&init_net, NETLINK_OXYGEN, |
| 0, wl_netlink_recv, NULL, THIS_MODULE); |
| #elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)) |
| nl_sk = netlink_kernel_create(&init_net, NETLINK_OXYGEN, THIS_MODULE, &cfg); |
| #else |
| nl_sk = netlink_kernel_create(&init_net, NETLINK_OXYGEN, &cfg); |
| #endif |
| |
| if (nl_sk == NULL) { |
| ANDROID_ERROR(("nl_sk is not ready\n")); |
| return BCME_ERROR; |
| } |
| |
| return BCME_OK; |
| } |
| |
| static void wl_netlink_deinit(void) |
| { |
| if (nl_sk) { |
| netlink_kernel_release(nl_sk); |
| nl_sk = NULL; |
| } |
| } |
| |
| s32 |
| wl_netlink_send_msg(int pid, int type, int seq, void *data, size_t size) |
| { |
| struct sk_buff *skb = NULL; |
| struct nlmsghdr *nlh = NULL; |
| int ret = -1; |
| |
| if (nl_sk == NULL) { |
| ANDROID_ERROR(("nl_sk was not initialized\n")); |
| goto nlmsg_failure; |
| } |
| |
| skb = alloc_skb(NLMSG_SPACE(size), GFP_ATOMIC); |
| if (skb == NULL) { |
| ANDROID_ERROR(("failed to allocate memory\n")); |
| goto nlmsg_failure; |
| } |
| |
| nlh = nlmsg_put(skb, 0, 0, 0, size, 0); |
| if (nlh == NULL) { |
| ANDROID_ERROR(("failed to build nlmsg, skb_tailroom:%d, nlmsg_total_size:%d\n", |
| skb_tailroom(skb), nlmsg_total_size(size))); |
| dev_kfree_skb(skb); |
| goto nlmsg_failure; |
| } |
| |
| memcpy(nlmsg_data(nlh), data, size); |
| nlh->nlmsg_seq = seq; |
| nlh->nlmsg_type = type; |
| |
| /* netlink_unicast() takes ownership of the skb and frees it itself. */ |
| ret = netlink_unicast(nl_sk, skb, pid, 0); |
| ANDROID_TRACE(("netlink_unicast() pid=%d, ret=%d\n", pid, ret)); |
| |
| nlmsg_failure: |
| 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 (!iface_name[0]) { |
| memset(iface_name, 0, IFNAMSIZ); |
| bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); |
| } |
| |
| wl_netlink_init(); |
| |
| return ret; |
| } |
| |
| int wl_android_exit(void) |
| { |
| int ret = 0; |
| |
| wl_netlink_deinit(); |
| |
| #ifdef WL_ANDROID_CMD |
| wl_android_cmd_exit(); |
| #endif |
| |
| 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) |
| g_wifi_on = FALSE; |
| } |