/* * Linux cfg80211 driver - Dongle Host Driver (DHD) related * * Copyright (C) 2021, Broadcom. * * 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. * * * <> * * $Id$ */ #include #include #include #include #include #include #ifdef PKT_FILTER_SUPPORT #include #include #endif #ifdef PKT_FILTER_SUPPORT extern uint dhd_pkt_filter_enable; extern uint dhd_master_mode; extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); #endif static int dhd_dongle_up = FALSE; #define PKT_FILTER_BUF_SIZE 64 #if defined(BCMDONGLEHOST) #include #include #include #include #include #include #endif /* defined(BCMDONGLEHOST) */ static s32 wl_dongle_up(struct net_device *ndev); static s32 wl_dongle_down(struct net_device *ndev); /** * Function implementations */ s32 dhd_cfg80211_init(struct bcm_cfg80211 *cfg) { dhd_dongle_up = FALSE; return 0; } s32 dhd_cfg80211_deinit(struct bcm_cfg80211 *cfg) { dhd_dongle_up = FALSE; return 0; } s32 dhd_cfg80211_down(struct bcm_cfg80211 *cfg) { struct net_device *ndev; s32 err = 0; WL_TRACE(("In\n")); if (!dhd_dongle_up) { WL_INFORM_MEM(("Dongle is already down\n")); err = 0; goto done; } ndev = bcmcfg_to_prmry_ndev(cfg); wl_dongle_down(ndev); done: return err; } s32 dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 *cfg, int val) { dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); dhd->op_mode |= val; WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode)); return 0; } s32 dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 *cfg) { dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE); WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode)); return 0; } #ifdef WL_STATIC_IF int32 wl_cfg80211_update_iflist_info(struct bcm_cfg80211 *cfg, struct net_device *ndev, int ifidx, uint8 *addr, int bssidx, char *name, int if_state) { return dhd_update_iflist_info(cfg->pub, ndev, ifidx, addr, bssidx, name, if_state); } #endif /* WL_STATIC_IF */ struct net_device* wl_cfg80211_allocate_if(struct bcm_cfg80211 *cfg, int ifidx, const char *name, uint8 *mac, uint8 bssidx, const char *dngl_name) { return dhd_allocate_if(cfg->pub, ifidx, name, mac, bssidx, FALSE, dngl_name); } int wl_cfg80211_register_if(struct bcm_cfg80211 *cfg, int ifidx, struct net_device* ndev, bool rtnl_lock_reqd) { return dhd_register_if(cfg->pub, ifidx, rtnl_lock_reqd); } int wl_cfg80211_remove_if(struct bcm_cfg80211 *cfg, int ifidx, struct net_device* ndev, bool rtnl_lock_reqd) { #ifdef DHD_PCIE_RUNTIMEPM dhdpcie_runtime_bus_wake(cfg->pub, CAN_SLEEP(), __builtin_return_address(0)); #endif /* DHD_PCIE_RUNTIMEPM */ return dhd_remove_if(cfg->pub, ifidx, rtnl_lock_reqd); } void wl_cfg80211_cleanup_if(struct net_device *net) { struct bcm_cfg80211 *cfg = wl_get_cfg(net); #ifdef DHD_PCIE_RUNTIMEPM dhdpcie_runtime_bus_wake(cfg->pub, CAN_SLEEP(), __builtin_return_address(0)); #else BCM_REFERENCE(cfg); #endif /* DHD_PCIE_RUNTIMEPM */ dhd_cleanup_if(net); } struct net_device * dhd_cfg80211_netdev_free(struct net_device *ndev) { struct bcm_cfg80211 *cfg; if (ndev) { cfg = wl_get_cfg(ndev); if (ndev->ieee80211_ptr) { MFREE(cfg->osh, ndev->ieee80211_ptr, sizeof(struct wireless_dev)); ndev->ieee80211_ptr = NULL; } free_netdev(ndev); return NULL; } return ndev; } void dhd_netdev_free(struct net_device *ndev) { #ifdef WL_CFG80211 ndev = dhd_cfg80211_netdev_free(ndev); #endif if (ndev) free_netdev(ndev); } static s32 wl_dongle_up(struct net_device *ndev) { s32 err = 0; u32 local_up = 0; #ifdef WLAN_ACCEL_BOOT u32 bus_host_access = 1; err = wldev_iovar_setint(ndev, "bus:host_access", bus_host_access); if (unlikely(err)) { WL_ERR(("bus:host_access(%d) error (%d)\n", bus_host_access, err)); } #endif /* WLAN_ACCEL_BOOT */ err = wldev_ioctl_set(ndev, WLC_UP, &local_up, sizeof(local_up)); if (unlikely(err)) { WL_ERR(("WLC_UP error (%d)\n", err)); } else { WL_INFORM_MEM(("wl up\n")); dhd_dongle_up = TRUE; } return err; } static s32 wl_dongle_down(struct net_device *ndev) { s32 err = 0; u32 local_down = 0; #ifdef WLAN_ACCEL_BOOT u32 bus_host_access = 0; #endif /* WLAN_ACCEL_BOOT */ err = wldev_ioctl_set(ndev, WLC_DOWN, &local_down, sizeof(local_down)); if (unlikely(err)) { WL_ERR(("WLC_DOWN error (%d)\n", err)); } #ifdef WLAN_ACCEL_BOOT err = wldev_iovar_setint(ndev, "bus:host_access", bus_host_access); if (unlikely(err)) { WL_ERR(("bus:host_access(%d) error (%d)\n", bus_host_access, err)); } #endif /* WLAN_ACCEL_BOOT */ WL_INFORM_MEM(("wl down\n")); dhd_dongle_up = FALSE; return err; } s32 wl_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout) { s32 err = 0; /* Setup timeout if Beacons are lost and roam is off to report link down */ if (roamvar) { err = wldev_iovar_setint(ndev, "bcn_timeout", bcn_timeout); if (unlikely(err)) { WL_ERR(("bcn_timeout error (%d)\n", err)); goto dongle_rom_out; } } /* Enable/Disable built-in roaming to allow supplicant to take care of roaming */ err = wldev_iovar_setint(ndev, "roam_off", roamvar); if (unlikely(err)) { WL_ERR(("roam_off error (%d)\n", err)); goto dongle_rom_out; } ROAMOFF_DBG_SAVE(ndev, SET_ROAM_FILS_TOGGLE, roamvar); dongle_rom_out: return err; } s32 dhd_config_dongle(struct bcm_cfg80211 *cfg) { #ifndef DHD_SDALIGN #define DHD_SDALIGN 32 #endif struct net_device *ndev; s32 err = 0; dhd_pub_t *dhd = NULL; WL_TRACE(("In\n")); ndev = bcmcfg_to_prmry_ndev(cfg); dhd = (dhd_pub_t *)(cfg->pub); err = wl_dongle_up(ndev); if (unlikely(err)) { WL_ERR(("wl_dongle_up failed\n")); goto default_conf_out; } if (dhd && dhd->fw_preinit) { /* Init config will be done by fw preinit context */ return BCME_OK; } default_conf_out: return err; } int dhd_cfgvendor_priv_string_handler(struct bcm_cfg80211 *cfg, struct wireless_dev *wdev, const struct bcm_nlmsg_hdr *nlioc, void *buf) { struct net_device *ndev = NULL; dhd_pub_t *dhd; dhd_ioctl_t ioc = { 0, NULL, 0, 0, 0, 0, 0}; int ret = 0; int8 index; WL_TRACE(("entry: cmd = %d\n", nlioc->cmd)); dhd = cfg->pub; DHD_OS_WAKE_LOCK(dhd); ndev = wdev_to_wlc_ndev(wdev, cfg); index = dhd_net2idx(dhd->info, ndev); if (index == DHD_BAD_IF) { WL_ERR(("Bad ifidx from wdev:%p\n", wdev)); ret = BCME_ERROR; goto done; } ioc.cmd = nlioc->cmd; ioc.len = nlioc->len; ioc.set = nlioc->set; ioc.driver = nlioc->magic; ioc.buf = buf; ret = dhd_ioctl_process(dhd, index, &ioc, buf); if (ret) { WL_TRACE(("dhd_ioctl_process return err %d\n", ret)); ret = OSL_ERROR(ret); goto done; } done: DHD_OS_WAKE_UNLOCK(dhd); return ret; } int dhd_set_wsec_info(dhd_pub_t *dhd, uint32 data, int tag) { struct net_device *ndev; uint32 wsec_info = data; int ret = BCME_OK; ndev = dhd_linux_get_primary_netdev(dhd); if (!ndev) { WL_ERR(("Cannot find primary netdev\n")); return -ENODEV; } BCM_REFERENCE(wsec_info); #if defined(WL_CFG80211) ret = wl_cfg80211_set_wsec_info(ndev, &wsec_info, sizeof(wsec_info), tag); if (unlikely(ret)) { WL_ERR(("Set wsec_info tag 0x%04x failed \n", tag)); } #endif /* WL_CFG80211 */ return ret; }