| /* |
| * Copyright (c) 2012, The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /*======================================================================== |
| |
| \file wlan_hdd_main.c |
| |
| \brief WLAN Host Device Driver implementation |
| |
| Copyright 2008 (c) Qualcomm, Incorporated. All Rights Reserved. |
| |
| Qualcomm Confidential and Proprietary. |
| |
| ========================================================================*/ |
| |
| /**========================================================================= |
| |
| EDIT HISTORY FOR FILE |
| |
| |
| This section contains comments describing changes made to the module. |
| Notice that changes are listed in reverse chronological order. |
| |
| |
| $Header:$ $DateTime: $ $Author: $ |
| |
| |
| when who what, where, why |
| -------- --- -------------------------------------------------------- |
| 04/5/09 Shailender Created module. |
| 02/24/10 Sudhir.S.Kohalli Added to support param for SoftAP module |
| 06/03/10 js - Added support to hostapd driven deauth/disassoc/mic failure |
| ==========================================================================*/ |
| |
| /*-------------------------------------------------------------------------- |
| Include Files |
| ------------------------------------------------------------------------*/ |
| //#include <wlan_qct_driver.h> |
| #include <wlan_hdd_includes.h> |
| #ifdef ANI_BUS_TYPE_SDIO |
| #include <wlan_sal_misc.h> |
| #endif // ANI_BUS_TYPE_SDIO |
| #include <vos_api.h> |
| #include <vos_sched.h> |
| #include <vos_power.h> |
| #include <linux/etherdevice.h> |
| #include <linux/firmware.h> |
| #ifdef ANI_BUS_TYPE_SDIO |
| #include <linux/mmc/sdio_func.h> |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) |
| // added in 2.6.32, need to define locally if using an earlier kernel |
| #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) |
| #endif |
| #endif // ANI_BUS_TYPE_SDIO |
| #ifdef ANI_BUS_TYPE_PLATFORM |
| #include <linux/wcnss_wlan.h> |
| #endif //ANI_BUS_TYPE_PLATFORM |
| #ifdef ANI_BUS_TYPE_PCI |
| #include "wcnss_wlan.h" |
| #endif /* ANI_BUS_TYPE_PCI */ |
| #include <wlan_hdd_tx_rx.h> |
| #include <palTimer.h> |
| #include <wniApi.h> |
| #include <wlan_nlink_srv.h> |
| #include <wlan_btc_svc.h> |
| #include <wlan_hdd_cfg.h> |
| #include <wlan_ptt_sock_svc.h> |
| #include <wlan_hdd_wowl.h> |
| #include <wlan_hdd_misc.h> |
| #include <wlan_hdd_wext.h> |
| #ifdef WLAN_BTAMP_FEATURE |
| #include <bap_hdd_main.h> |
| #include <bapInternal.h> |
| #endif // WLAN_BTAMP_FEATURE |
| |
| #ifdef CONFIG_CFG80211 |
| #include <linux/wireless.h> |
| #include <net/cfg80211.h> |
| #include "wlan_hdd_cfg80211.h" |
| #include "wlan_hdd_p2p.h" |
| #endif |
| #include <linux/rtnetlink.h> |
| #ifdef ANI_MANF_DIAG |
| int wlan_hdd_ftm_start(hdd_context_t *pAdapter); |
| #endif |
| #ifdef WLAN_SOFTAP_FEATURE |
| #include "sapApi.h" |
| #include <linux/semaphore.h> |
| #include <mach/subsystem_restart.h> |
| #include <wlan_hdd_hostapd.h> |
| #include <wlan_hdd_softap_tx_rx.h> |
| #endif |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| #include "cfgApi.h" |
| #endif |
| #include "wlan_hdd_dev_pwr.h" |
| #ifdef WLAN_BTAMP_FEATURE |
| #include "bap_hdd_misc.h" |
| #endif |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| #include "wlan_qct_pal_trace.h" |
| #endif /* FEATURE_WLAN_INTEGRATED_SOC */ |
| #include "qwlan_version.h" |
| |
| #ifdef MODULE |
| #define WLAN_MODULE_NAME module_name(THIS_MODULE) |
| #else |
| #define WLAN_MODULE_NAME "wlan" |
| #endif |
| |
| #ifdef TIMER_MANAGER |
| #define TIMER_MANAGER_STR " +TIMER_MANAGER" |
| #else |
| #define TIMER_MANAGER_STR "" |
| #endif |
| |
| #ifdef MEMORY_DEBUG |
| #define MEMORY_DEBUG_STR " +MEMORY_DEBUG" |
| #else |
| #define MEMORY_DEBUG_STR "" |
| #endif |
| |
| /* the Android framework expects this param even though we don't use it */ |
| #define BUF_LEN 20 |
| static char fwpath[BUF_LEN]; |
| #ifndef MODULE |
| static int wlan_hdd_inited = 0; |
| #endif |
| |
| /* |
| * The rate at which the driver sends RESTART event to supplicant |
| * once the function 'vos_wlanRestart()' is called |
| * |
| */ |
| #define WLAN_HDD_RESTART_RETRY_DELAY_MS 5000 /* 5 second */ |
| #define WLAN_HDD_RESTART_RETRY_MAX_CNT 5 /* 5 retries */ |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,5)) |
| static struct wake_lock wlan_wake_lock; |
| #endif |
| /* set when SSR is needed after unload */ |
| static v_U8_t isSsrRequired; |
| |
| //internal function declaration |
| static VOS_STATUS wlan_hdd_framework_restart(hdd_context_t *pHddCtx); |
| static void wlan_hdd_restart_init(hdd_context_t *pHddCtx); |
| static void wlan_hdd_restart_deinit(hdd_context_t *pHddCtx); |
| void wlan_hdd_restart_timer_cb(v_PVOID_t usrDataForCallback); |
| |
| v_U16_t hdd_select_queue(struct net_device *dev, |
| struct sk_buff *skb); |
| |
| #ifdef WLAN_FEATURE_PACKET_FILTERING |
| static void hdd_set_multicast_list(struct net_device *dev); |
| #endif |
| |
| void hdd_wlan_initial_scan(hdd_adapter_t *pAdapter); |
| |
| extern int hdd_setBand_helper(struct net_device *dev, tANI_U8* ptr); |
| |
| static int hdd_netdev_notifier_call(struct notifier_block * nb, |
| unsigned long state, |
| void *ndev) |
| { |
| struct net_device *dev = ndev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter ); |
| #ifdef WLAN_BTAMP_FEATURE |
| VOS_STATUS status; |
| hdd_context_t *pHddCtx; |
| #endif |
| |
| //Make sure that this callback corresponds to our device. |
| if((strncmp( dev->name, "wlan", 4 )) && |
| (strncmp( dev->name, "p2p", 3)) |
| ) |
| return NOTIFY_DONE; |
| |
| #ifdef CONFIG_CFG80211 |
| if (!dev->ieee80211_ptr) |
| return NOTIFY_DONE; |
| #endif |
| |
| |
| if(NULL == pAdapter) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD Adaptor Null Pointer", __func__); |
| VOS_ASSERT(0); |
| return NOTIFY_DONE; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO,"%s: New Net Device State = %lu", __func__, state); |
| |
| switch (state) { |
| case NETDEV_REGISTER: |
| break; |
| |
| case NETDEV_UNREGISTER: |
| break; |
| |
| case NETDEV_UP: |
| break; |
| |
| case NETDEV_DOWN: |
| break; |
| |
| case NETDEV_CHANGE: |
| if(TRUE == pAdapter->isLinkUpSvcNeeded) |
| complete(&pAdapter->linkup_event_var); |
| break; |
| |
| case NETDEV_GOING_DOWN: |
| if( pHddCtx->scan_info.mScanPending != FALSE ) |
| { |
| int result; |
| INIT_COMPLETION(pAdapter->abortscan_event_var); |
| hdd_abort_mac_scan(pAdapter->pHddCtx); |
| result = wait_for_completion_interruptible_timeout( |
| &pAdapter->abortscan_event_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_ABORTSCAN)); |
| if(!result) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Timeout occured while waiting for abortscan" , |
| __FUNCTION__); |
| } |
| } |
| else |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Scan is not Pending from user" , __FUNCTION__); |
| } |
| #ifdef WLAN_BTAMP_FEATURE |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"%s: disabling AMP", __FUNCTION__); |
| status = WLANBAP_StopAmp(); |
| if(VOS_STATUS_SUCCESS != status ) |
| { |
| pHddCtx->isAmpAllowed = VOS_TRUE; |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to stop AMP", __func__); |
| } |
| else |
| { |
| //a state m/c implementation in PAL is TBD to avoid this delay |
| msleep(500); |
| if ( pHddCtx->isAmpAllowed ) |
| { |
| WLANBAP_DeregisterFromHCI(); |
| pHddCtx->isAmpAllowed = VOS_FALSE; |
| } |
| } |
| #endif //WLAN_BTAMP_FEATURE |
| break; |
| |
| default: |
| break; |
| } |
| |
| return NOTIFY_DONE; |
| } |
| |
| struct notifier_block hdd_netdev_notifier = { |
| .notifier_call = hdd_netdev_notifier_call, |
| }; |
| |
| /*--------------------------------------------------------------------------- |
| * Function definitions |
| *-------------------------------------------------------------------------*/ |
| extern int isWDresetInProgress(void); |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| extern void register_wlan_suspend(void); |
| extern void unregister_wlan_suspend(void); |
| void hdd_unregister_mcast_bcast_filter(hdd_context_t *pHddCtx); |
| void hdd_register_mcast_bcast_filter(hdd_context_t *pHddCtx); |
| #endif |
| //variable to hold the insmod parameters |
| static int con_mode = 0; |
| #ifndef MODULE |
| /* current con_mode - used only for statically linked driver |
| * con_mode is changed by userspace to indicate a mode change which will |
| * result in calling the module exit and init functions. The module |
| * exit function will clean up based on the value of con_mode prior to it |
| * being changed by userspace. So curr_con_mode records the current con_mode |
| * for exit when con_mode becomes the next mode for init |
| */ |
| static int curr_con_mode = 0; |
| #endif |
| |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_wdi_trace_enable() - Configure initial WDI Trace enable |
| |
| Called immediately after the cfg.ini is read in order to configure |
| the desired trace levels in the WDI. |
| |
| \param - moduleId - module whose trace level is being configured |
| \param - bitmask - bitmask of log levels to be enabled |
| |
| \return - void |
| |
| --------------------------------------------------------------------------*/ |
| static void hdd_wdi_trace_enable(wpt_moduleid moduleId, v_U32_t bitmask) |
| { |
| wpt_tracelevel level; |
| |
| /* if the bitmask is the default value, then a bitmask was not |
| specified in cfg.ini, so leave the logging level alone (it |
| will remain at the "compiled in" default value) */ |
| if (CFG_WDI_TRACE_ENABLE_DEFAULT == bitmask) |
| { |
| return; |
| } |
| |
| /* a mask was specified. start by disabling all logging */ |
| wpalTraceSetLevel(moduleId, eWLAN_PAL_TRACE_LEVEL_NONE, 0); |
| |
| /* now cycle through the bitmask until all "set" bits are serviced */ |
| level = eWLAN_PAL_TRACE_LEVEL_FATAL; |
| while (0 != bitmask) |
| { |
| if (bitmask & 1) |
| { |
| wpalTraceSetLevel(moduleId, level, 1); |
| } |
| level++; |
| bitmask >>= 1; |
| } |
| } |
| #endif /* FEATURE_WLAN_INTEGRATED_SOC */ |
| |
| int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_priv_data_t priv_data; |
| tANI_U8 *command = NULL; |
| int ret = 0; |
| |
| if (NULL == pAdapter) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: HDD adapter context is Null", __FUNCTION__); |
| ret = -ENODEV; |
| goto exit; |
| } |
| |
| if ((!ifr) || (!ifr->ifr_data)) |
| { |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(hdd_priv_data_t))) |
| { |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| command = kmalloc(priv_data.total_len, GFP_KERNEL); |
| if (!command) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: failed to allocate memory\n", __FUNCTION__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| if (copy_from_user(command, priv_data.buf, priv_data.total_len)) |
| { |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| if ((SIOCDEVPRIVATE + 1) == cmd) |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t*)pAdapter->pHddCtx; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "***Received %s cmd from Wi-Fi GUI***", command); |
| |
| if (strncmp(command, "P2P_DEV_ADDR", 12) == 0 ) |
| { |
| if (copy_to_user(priv_data.buf, pHddCtx->p2pDeviceAddress.bytes, |
| sizeof(tSirMacAddr))) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: failed to copy data to user buffer\n", __FUNCTION__); |
| ret = -EFAULT; |
| } |
| } |
| else if(strncmp(priv_data.buf, "SETBAND", 7) == 0) |
| { |
| tANI_U8 *ptr = (tANI_U8*)priv_data.buf ; |
| int ret = 0 ; |
| |
| /* Change band request received */ |
| |
| /* First 8 bytes will have "SETBAND " and |
| * 9 byte will have band setting value */ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: SetBandCommand Info comm %s UL %d, TL %d", __FUNCTION__, priv_data.buf, priv_data.used_len, priv_data.total_len); |
| |
| /* Change band request received */ |
| ret = hdd_setBand_helper(dev, ptr); |
| } |
| else if ( strncasecmp(command, "COUNTRY", 7) == 0 ) |
| { |
| char *country_code; |
| |
| country_code = command + 8; |
| ret = (int)sme_ChangeCountryCode(pHddCtx->hHal, NULL, country_code, |
| pAdapter, pHddCtx->pvosContext); |
| if( 0 != ret ) |
| { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: SME Change Country code fail ret=%d\n",__func__, ret); |
| |
| } |
| } |
| /* |
| command should be a string having format |
| SET_SAP_CHANNEL_LIST <num of channels> <the channels seperated by spaces> |
| */ |
| else if(strncmp(priv_data.buf, "SET_SAP_CHANNEL_LIST", 20) == 0) |
| { |
| tANI_U8 *ptr = (tANI_U8*)priv_data.buf; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| " Received Command to Set Preferred Channels for SAP in %s", __FUNCTION__); |
| |
| ret = sapSetPreferredChannel(dev,ptr); |
| } |
| } |
| exit: |
| if (command) |
| { |
| kfree(command); |
| } |
| return ret; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_open() - HDD Open function |
| |
| This is called in response to ifconfig up |
| |
| \param - dev Pointer to net_device structure |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| int hdd_open (struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| v_BOOL_t in_standby = TRUE; |
| |
| if (NULL == pAdapter) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: HDD adapter context is Null", __FUNCTION__); |
| return -ENODEV; |
| } |
| |
| pHddCtx = (hdd_context_t*)pAdapter->pHddCtx; |
| if (NULL == pHddCtx) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: HDD context is Null", __FUNCTION__); |
| return -ENODEV; |
| } |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| while ( (NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status) ) |
| { |
| if( pAdapterNode->pAdapter->event_flags & DEVICE_IFACE_OPENED) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: chip already out of " |
| "standby", __func__, pAdapter->device_mode); |
| in_standby = FALSE; |
| break; |
| } |
| else |
| { |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| } |
| |
| if (TRUE == in_standby) |
| { |
| if (VOS_STATUS_SUCCESS != wlan_hdd_exit_lowpower(pHddCtx, pAdapter)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failed to bring " |
| "wlan out of power save", __func__); |
| return -EINVAL; |
| } |
| } |
| |
| pAdapter->event_flags |= DEVICE_IFACE_OPENED; |
| if (hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Enabling Tx Queues", __FUNCTION__); |
| /* Enable TX queues only when we are connected */ |
| netif_tx_start_all_queues(dev); |
| } |
| |
| return 0; |
| } |
| |
| int hdd_mon_open (struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| if(pAdapter == NULL) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: HDD adapter context is Null", __FUNCTION__); |
| return -1; |
| } |
| |
| netif_start_queue(dev); |
| |
| return 0; |
| } |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_stop() - HDD stop function |
| |
| This is called in response to ifconfig down |
| |
| \param - dev Pointer to net_device structure |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| int hdd_stop (struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| v_BOOL_t enter_standby = TRUE; |
| |
| ENTER(); |
| |
| if (NULL == pAdapter) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: HDD adapter context is Null", __FUNCTION__); |
| return -ENODEV; |
| } |
| |
| pHddCtx = (hdd_context_t*)pAdapter->pHddCtx; |
| if (NULL == pHddCtx) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: HDD context is Null", __FUNCTION__); |
| return -ENODEV; |
| } |
| |
| pAdapter->event_flags &= ~(DEVICE_IFACE_OPENED); |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: Disabling OS Tx queues", __func__); |
| netif_tx_disable(pAdapter->dev); |
| netif_carrier_off(pAdapter->dev); |
| |
| |
| /* SoftAP ifaces should never go in power save mode |
| making sure same here. */ |
| if ( (WLAN_HDD_SOFTAP == pAdapter->device_mode ) |
| || (WLAN_HDD_MONITOR == pAdapter->device_mode ) |
| #ifdef WLAN_FEATURE_P2P |
| || (WLAN_HDD_P2P_GO == pAdapter->device_mode ) |
| #endif |
| ) |
| { |
| /* SoftAP mode, so return from here */ |
| EXIT(); |
| return 0; |
| } |
| |
| /* Find if any iface is up then |
| if any iface is up then can't put device to sleep/ power save mode. */ |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| while ( (NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status) ) |
| { |
| if ( pAdapterNode->pAdapter->event_flags & DEVICE_IFACE_OPENED) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: Still other ifaces are up cannot " |
| "put device to sleep", __func__, pAdapter->device_mode); |
| enter_standby = FALSE; |
| break; |
| } |
| else |
| { |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| } |
| |
| if (TRUE == enter_standby) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: All Interfaces are Down " |
| "entering standby", __func__); |
| if (VOS_STATUS_SUCCESS != wlan_hdd_enter_lowpower(pHddCtx)) |
| { |
| /*log and return success*/ |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failed to put " |
| "wlan in power save", __func__); |
| } |
| } |
| |
| EXIT(); |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_uninit() - HDD uninit function |
| |
| This is called during the netdev unregister to uninitialize all data |
| associated with the device |
| |
| \param - dev Pointer to net_device structure |
| |
| \return - void |
| |
| --------------------------------------------------------------------------*/ |
| static void hdd_uninit (struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| ENTER(); |
| |
| do |
| { |
| if (NULL == pAdapter) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: NULL pAdapter", __func__); |
| break; |
| } |
| |
| if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Invalid magic", __func__); |
| break; |
| } |
| |
| if (NULL == pAdapter->pHddCtx) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: NULL pHddCtx", __func__); |
| break; |
| } |
| |
| if (dev != pAdapter->dev) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Invalid device reference", __func__); |
| /* we haven't validated all cases so let this go for now */ |
| } |
| |
| hdd_deinit_adapter(pAdapter->pHddCtx, pAdapter); |
| |
| /* after uninit our adapter structure will no longer be valid */ |
| pAdapter->dev = NULL; |
| pAdapter->magic = 0; |
| } while (0); |
| |
| EXIT(); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_release_firmware() - |
| |
| This function calls the release firmware API to free the firmware buffer. |
| |
| \param - pFileName Pointer to the File Name. |
| pCtx - Pointer to the adapter . |
| |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| VOS_STATUS hdd_release_firmware(char *pFileName,v_VOID_t *pCtx) |
| { |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| hdd_context_t *pHddCtx = (hdd_context_t*)pCtx; |
| ENTER(); |
| |
| |
| if (!strcmp(WLAN_FW_FILE, pFileName)) { |
| |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH,"%s: Loaded firmware file is %s",__func__,pFileName); |
| |
| if(pHddCtx->fw) { |
| release_firmware(pHddCtx->fw); |
| pHddCtx->fw = NULL; |
| } |
| else |
| status = VOS_STATUS_E_FAILURE; |
| } |
| else if (!strcmp(WLAN_NV_FILE,pFileName)) { |
| if(pHddCtx->nv) { |
| release_firmware(pHddCtx->nv); |
| pHddCtx->nv = NULL; |
| } |
| else |
| status = VOS_STATUS_E_FAILURE; |
| |
| } |
| |
| EXIT(); |
| return status; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_request_firmware() - |
| |
| This function reads the firmware file using the request firmware |
| API and returns the the firmware data and the firmware file size. |
| |
| \param - pfileName - Pointer to the file name. |
| - pCtx - Pointer to the adapter . |
| - ppfw_data - Pointer to the pointer of the firmware data. |
| - pSize - Pointer to the file size. |
| |
| \return - VOS_STATUS_SUCCESS for success, VOS_STATUS_E_FAILURE for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| |
| VOS_STATUS hdd_request_firmware(char *pfileName,v_VOID_t *pCtx,v_VOID_t **ppfw_data, v_SIZE_t *pSize) |
| { |
| int status; |
| VOS_STATUS retval = VOS_STATUS_SUCCESS; |
| hdd_context_t *pHddCtx = (hdd_context_t*)pCtx; |
| ENTER(); |
| |
| if( (!strcmp(WLAN_FW_FILE, pfileName)) ) { |
| |
| status = request_firmware(&pHddCtx->fw, pfileName, pHddCtx->parent_dev); |
| |
| if(status || !pHddCtx->fw || !pHddCtx->fw->data) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Firmware %s download failed", |
| __func__, pfileName); |
| retval = VOS_STATUS_E_FAILURE; |
| } |
| |
| else { |
| *ppfw_data = (v_VOID_t *)pHddCtx->fw->data; |
| *pSize = pHddCtx->fw->size; |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: Firmware size = %d", |
| __func__, *pSize); |
| } |
| } |
| else if(!strcmp(WLAN_NV_FILE, pfileName)) { |
| |
| status = request_firmware(&pHddCtx->nv, pfileName, pHddCtx->parent_dev); |
| |
| if(status || !pHddCtx->nv || !pHddCtx->nv->data) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: nv %s download failed", |
| __func__, pfileName); |
| retval = VOS_STATUS_E_FAILURE; |
| } |
| |
| else { |
| *ppfw_data = (v_VOID_t *)pHddCtx->nv->data; |
| *pSize = pHddCtx->nv->size; |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: nv file size = %d", |
| __func__, *pSize); |
| } |
| } |
| |
| EXIT(); |
| return retval; |
| } |
| /**--------------------------------------------------------------------------- |
| \brief hdd_full_pwr_cbk() - HDD full power callbackfunction |
| |
| This is the function invoked by SME to inform the result of a full power |
| request issued by HDD |
| |
| \param - callbackcontext - Pointer to cookie |
| status - result of request |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_full_pwr_cbk(void *callbackContext, eHalStatus status) |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t*)callbackContext; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH,"HDD full Power callback status = %d", status); |
| if(&pHddCtx->full_pwr_comp_var) |
| { |
| complete(&pHddCtx->full_pwr_comp_var); |
| } |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_req_bmps_cbk() - HDD Request BMPS callback function |
| |
| This is the function invoked by SME to inform the result of BMPS |
| request issued by HDD |
| |
| \param - callbackcontext - Pointer to cookie |
| status - result of request |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_req_bmps_cbk(void *callbackContext, eHalStatus status) |
| { |
| |
| struct completion *completion_var = (struct completion*) callbackContext; |
| |
| hddLog(VOS_TRACE_LEVEL_ERROR, "HDD BMPS request Callback, status = %d\n", status); |
| if(completion_var != NULL) |
| { |
| complete(completion_var); |
| } |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_get_cfg_file_size() - |
| |
| This function reads the configuration file using the request firmware |
| API and returns the configuration file size. |
| |
| \param - pCtx - Pointer to the adapter . |
| - pFileName - Pointer to the file name. |
| - pBufSize - Pointer to the buffer size. |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| VOS_STATUS hdd_get_cfg_file_size(v_VOID_t *pCtx, char *pFileName, v_SIZE_t *pBufSize) |
| { |
| int status; |
| hdd_context_t *pHddCtx = (hdd_context_t*)pCtx; |
| |
| ENTER(); |
| |
| status = request_firmware(&pHddCtx->fw, pFileName, pHddCtx->parent_dev); |
| |
| if(status || !pHddCtx->fw || !pHddCtx->fw->data) { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: CFG download failed",__func__); |
| status = VOS_STATUS_E_FAILURE; |
| } |
| else { |
| *pBufSize = pHddCtx->fw->size; |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: CFG size = %d", __func__, *pBufSize); |
| release_firmware(pHddCtx->fw); |
| pHddCtx->fw = NULL; |
| } |
| |
| EXIT(); |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_read_cfg_file() - |
| |
| This function reads the configuration file using the request firmware |
| API and returns the cfg data and the buffer size of the configuration file. |
| |
| \param - pCtx - Pointer to the adapter . |
| - pFileName - Pointer to the file name. |
| - pBuffer - Pointer to the data buffer. |
| - pBufSize - Pointer to the buffer size. |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| VOS_STATUS hdd_read_cfg_file(v_VOID_t *pCtx, char *pFileName, |
| v_VOID_t *pBuffer, v_SIZE_t *pBufSize) |
| { |
| int status; |
| hdd_context_t *pHddCtx = (hdd_context_t*)pCtx; |
| |
| ENTER(); |
| |
| status = request_firmware(&pHddCtx->fw, pFileName, pHddCtx->parent_dev); |
| |
| if(status || !pHddCtx->fw || !pHddCtx->fw->data) { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: CFG download failed",__func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| else { |
| if(*pBufSize != pHddCtx->fw->size) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Caller sets invalid CFG " |
| "file size", __func__); |
| release_firmware(pHddCtx->fw); |
| pHddCtx->fw = NULL; |
| return VOS_STATUS_E_FAILURE; |
| } |
| else { |
| if(pBuffer) { |
| vos_mem_copy(pBuffer,pHddCtx->fw->data,*pBufSize); |
| } |
| release_firmware(pHddCtx->fw); |
| pHddCtx->fw = NULL; |
| } |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_set_mac_addr_cb() - |
| |
| This function is the call back function for setting the station |
| mac adrress called by ccm module to indicate the |
| success/failure result. |
| |
| \param - hHal - Pointer to the hal module. |
| - result - returns the result of the set mac address. |
| |
| \return - void |
| |
| --------------------------------------------------------------------------*/ |
| #ifndef FEATURE_WLAN_INTEGRATED_SOC |
| static void hdd_set_mac_addr_cb( tHalHandle hHal, tANI_S32 result ) |
| { |
| // ignore the STA_ID response for now. |
| |
| VOS_ASSERT( CCM_IS_RESULT_SUCCESS( result ) ); |
| } |
| #endif |
| |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_set_mac_address() - |
| |
| This function sets the user specified mac address using |
| the command ifconfig wlanX hw ether <mac adress>. |
| |
| \param - dev - Pointer to the net device. |
| - addr - Pointer to the sockaddr. |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| static int hdd_set_mac_address(struct net_device *dev, void *addr) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct sockaddr *psta_mac_addr = addr; |
| eHalStatus halStatus = eHAL_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| memcpy(&pAdapter->macAddressCurrent, psta_mac_addr->sa_data, ETH_ALEN); |
| |
| #ifdef HDD_SESSIONIZE |
| // set the MAC address though the STA ID CFG. |
| halStatus = ccmCfgSetStr( pAdapter->hHal, WNI_CFG_STA_ID, |
| (v_U8_t *)&pAdapter->macAddressCurrent, |
| sizeof( pAdapter->macAddressCurrent ), |
| hdd_set_mac_addr_cb, VOS_FALSE ); |
| #endif |
| |
| memcpy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN); |
| |
| EXIT(); |
| return halStatus; |
| } |
| |
| tANI_U8* wlan_hdd_get_intf_addr(hdd_context_t* pHddCtx) |
| { |
| int i; |
| for ( i = 0; i < VOS_MAX_CONCURRENCY_PERSONA; i++) |
| { |
| if( 0 == (pHddCtx->cfg_ini->intfAddrMask >> i)) |
| break; |
| } |
| |
| if( VOS_MAX_CONCURRENCY_PERSONA == i) |
| return NULL; |
| |
| pHddCtx->cfg_ini->intfAddrMask |= (1 << i); |
| return &pHddCtx->cfg_ini->intfMacAddr[i].bytes[0]; |
| } |
| |
| void wlan_hdd_release_intf_addr(hdd_context_t* pHddCtx, tANI_U8* releaseAddr) |
| { |
| int i; |
| for ( i = 0; i < VOS_MAX_CONCURRENCY_PERSONA; i++) |
| { |
| if ( !memcmp(releaseAddr, &pHddCtx->cfg_ini->intfMacAddr[i].bytes[0], 6) ) |
| { |
| pHddCtx->cfg_ini->intfAddrMask &= ~(1 << i); |
| break; |
| } |
| } |
| return; |
| } |
| |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)) |
| static struct net_device_ops wlan_drv_ops = { |
| .ndo_open = hdd_open, |
| .ndo_stop = hdd_stop, |
| .ndo_uninit = hdd_uninit, |
| .ndo_start_xmit = hdd_hard_start_xmit, |
| .ndo_tx_timeout = hdd_tx_timeout, |
| .ndo_get_stats = hdd_stats, |
| .ndo_do_ioctl = hdd_ioctl, |
| .ndo_set_mac_address = hdd_set_mac_address, |
| .ndo_select_queue = hdd_select_queue, |
| #ifdef WLAN_FEATURE_PACKET_FILTERING |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3,1,0)) |
| .ndo_set_rx_mode = hdd_set_multicast_list, |
| #else |
| .ndo_set_multicast_list = hdd_set_multicast_list, |
| #endif //LINUX_VERSION_CODE |
| #endif |
| }; |
| #ifdef CONFIG_CFG80211 |
| static struct net_device_ops wlan_mon_drv_ops = { |
| .ndo_open = hdd_mon_open, |
| .ndo_stop = hdd_stop, |
| .ndo_uninit = hdd_uninit, |
| .ndo_start_xmit = hdd_mon_hard_start_xmit, |
| .ndo_tx_timeout = hdd_tx_timeout, |
| .ndo_get_stats = hdd_stats, |
| .ndo_do_ioctl = hdd_ioctl, |
| .ndo_set_mac_address = hdd_set_mac_address, |
| }; |
| #endif |
| |
| #endif |
| |
| void hdd_set_station_ops( struct net_device *pWlanDev ) |
| { |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)) |
| pWlanDev->tx_queue_len = NET_DEV_TX_QUEUE_LEN, |
| pWlanDev->netdev_ops = &wlan_drv_ops; |
| #else |
| pWlanDev->open = hdd_open; |
| pWlanDev->stop = hdd_stop; |
| pWlanDev->uninit = hdd_uninit; |
| pWlanDev->hard_start_xmit = NULL; |
| pWlanDev->tx_timeout = hdd_tx_timeout; |
| pWlanDev->get_stats = hdd_stats; |
| pWlanDev->do_ioctl = hdd_ioctl; |
| pWlanDev->tx_queue_len = NET_DEV_TX_QUEUE_LEN; |
| pWlanDev->set_mac_address = hdd_set_mac_address; |
| #endif |
| } |
| |
| hdd_adapter_t* hdd_alloc_station_adapter( hdd_context_t *pHddCtx, tSirMacAddr macAddr, char* name ) |
| { |
| struct net_device *pWlanDev = NULL; |
| hdd_adapter_t *pAdapter = NULL; |
| #ifdef CONFIG_CFG80211 |
| /* |
| * cfg80211 initialization and registration.... |
| */ |
| pWlanDev = alloc_netdev_mq(sizeof( hdd_adapter_t ), name, ether_setup, NUM_TX_QUEUES); |
| |
| #else |
| //Allocate the net_device and private data (station ctx) |
| pWlanDev = alloc_etherdev_mq(sizeof( hdd_adapter_t ), NUM_TX_QUEUES); |
| |
| #endif |
| |
| if(pWlanDev != NULL) |
| { |
| |
| //Save the pointer to the net_device in the HDD adapter |
| pAdapter = (hdd_adapter_t*) netdev_priv( pWlanDev ); |
| |
| #ifndef CONFIG_CFG80211 |
| //Init the net_device structure |
| ether_setup(pWlanDev); |
| #endif |
| |
| vos_mem_zero( pAdapter, sizeof( hdd_adapter_t ) ); |
| |
| pAdapter->dev = pWlanDev; |
| pAdapter->pHddCtx = pHddCtx; |
| pAdapter->magic = WLAN_HDD_ADAPTER_MAGIC; |
| |
| init_completion(&pAdapter->session_open_comp_var); |
| init_completion(&pAdapter->session_close_comp_var); |
| init_completion(&pAdapter->disconnect_comp_var); |
| init_completion(&pAdapter->linkup_event_var); |
| init_completion(&pAdapter->cancel_rem_on_chan_var); |
| init_completion(&pAdapter->rem_on_chan_ready_event); |
| init_completion(&pAdapter->abortscan_event_var); |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)) |
| init_completion(&pAdapter->offchannel_tx_event); |
| #endif |
| #ifdef CONFIG_CFG80211 |
| init_completion(&pAdapter->tx_action_cnf_event); |
| #endif |
| init_completion(&pHddCtx->mc_sus_event_var); |
| init_completion(&pHddCtx->tx_sus_event_var); |
| |
| pAdapter->isLinkUpSvcNeeded = FALSE; |
| pAdapter->higherDtimTransition = eANI_BOOLEAN_TRUE; |
| //Init the net_device structure |
| strlcpy(pWlanDev->name, name, IFNAMSIZ); |
| |
| vos_mem_copy(pWlanDev->dev_addr, (void *)macAddr, sizeof(tSirMacAddr)); |
| vos_mem_copy( pAdapter->macAddressCurrent.bytes, macAddr, sizeof(tSirMacAddr)); |
| pWlanDev->watchdog_timeo = HDD_TX_TIMEOUT; |
| pWlanDev->hard_header_len += LIBRA_HW_NEEDED_HEADROOM; |
| |
| hdd_set_station_ops( pAdapter->dev ); |
| |
| pWlanDev->destructor = free_netdev; |
| #ifdef CONFIG_CFG80211 |
| pWlanDev->ieee80211_ptr = &pAdapter->wdev ; |
| pAdapter->wdev.wiphy = pHddCtx->wiphy; |
| pAdapter->wdev.netdev = pWlanDev; |
| #endif |
| /* set pWlanDev's parent to underlying device */ |
| SET_NETDEV_DEV(pWlanDev, pHddCtx->parent_dev); |
| } |
| |
| return pAdapter; |
| } |
| |
| VOS_STATUS hdd_register_interface( hdd_adapter_t *pAdapter, tANI_U8 rtnl_lock_held ) |
| { |
| struct net_device *pWlanDev = pAdapter->dev; |
| //hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station; |
| //hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter ); |
| //eHalStatus halStatus = eHAL_STATUS_SUCCESS; |
| |
| if( rtnl_lock_held ) |
| { |
| if (strchr(pWlanDev->name, '%')) { |
| if( dev_alloc_name(pWlanDev, pWlanDev->name) < 0 ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s:Failed:dev_alloc_name",__func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| } |
| if (register_netdevice(pWlanDev)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s:Failed:register_netdev",__func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| } |
| else |
| { |
| if(register_netdev(pWlanDev)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Failed:register_netdev",__func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| } |
| set_bit(NET_DEVICE_REGISTERED, &pAdapter->event_flags); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| eHalStatus hdd_smeCloseSessionCallback(void *pContext) |
| { |
| if(pContext != NULL) |
| { |
| clear_bit(SME_SESSION_OPENED, &((hdd_adapter_t*)pContext)->event_flags); |
| |
| /* need to make sure all of our scheduled work has completed. |
| * This callback is called from MC thread context, so it is safe to |
| * to call below flush workqueue API from here. |
| */ |
| flush_scheduled_work(); |
| complete(&((hdd_adapter_t*)pContext)->session_close_comp_var); |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| VOS_STATUS hdd_init_station_mode( hdd_adapter_t *pAdapter ) |
| { |
| struct net_device *pWlanDev = pAdapter->dev; |
| hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter ); |
| eHalStatus halStatus = eHAL_STATUS_SUCCESS; |
| VOS_STATUS status = VOS_STATUS_E_FAILURE; |
| int rc = 0; |
| |
| INIT_COMPLETION(pAdapter->session_open_comp_var); |
| //Open a SME session for future operation |
| halStatus = sme_OpenSession( pHddCtx->hHal, hdd_smeRoamCallback, pAdapter, |
| (tANI_U8 *)&pAdapter->macAddressCurrent, &pAdapter->sessionId ); |
| if ( !HAL_STATUS_SUCCESS( halStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "sme_OpenSession() failed with status code %08d [x%08lx]", |
| halStatus, halStatus ); |
| status = VOS_STATUS_E_FAILURE; |
| goto error_sme_open; |
| } |
| |
| //Block on a completion variable. Can't wait forever though. |
| rc = wait_for_completion_interruptible_timeout( |
| &pAdapter->session_open_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE)); |
| if (!rc) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "Session is not opened within timeout period code %08d", rc ); |
| status = VOS_STATUS_E_FAILURE; |
| goto error_sme_open; |
| } |
| |
| // Register wireless extensions |
| if( eHAL_STATUS_SUCCESS != (halStatus = hdd_register_wext(pWlanDev))) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "hdd_register_wext() failed with status code %08d [x%08lx]", |
| halStatus, halStatus ); |
| status = VOS_STATUS_E_FAILURE; |
| goto error_register_wext; |
| } |
| //Safe to register the hard_start_xmit function again |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)) |
| wlan_drv_ops.ndo_start_xmit = hdd_hard_start_xmit; |
| #else |
| pWlanDev->hard_start_xmit = hdd_hard_start_xmit; |
| #endif |
| |
| //Set the Connection State to Not Connected |
| pHddStaCtx->conn_info.connState = eConnectionState_NotConnected; |
| |
| //Set the default operation channel |
| pHddStaCtx->conn_info.operationChannel = pHddCtx->cfg_ini->OperatingChannel; |
| |
| /* Make the default Auth Type as OPEN*/ |
| pHddStaCtx->conn_info.authType = eCSR_AUTH_TYPE_OPEN_SYSTEM; |
| |
| if( VOS_STATUS_SUCCESS != ( status = hdd_init_tx_rx( pAdapter ) ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "hdd_init_tx_rx() failed with status code %08d [x%08lx]", |
| status, status ); |
| goto error_init_txrx; |
| } |
| |
| set_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags); |
| |
| if( VOS_STATUS_SUCCESS != ( status = hdd_wmm_adapter_init( pAdapter ) ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "hdd_wmm_adapter_init() failed with status code %08d [x%08lx]", |
| status, status ); |
| goto error_wmm_init; |
| } |
| |
| set_bit(WMM_INIT_DONE, &pAdapter->event_flags); |
| |
| return VOS_STATUS_SUCCESS; |
| |
| error_wmm_init: |
| clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags); |
| hdd_deinit_tx_rx(pAdapter); |
| error_init_txrx: |
| hdd_UnregisterWext(pWlanDev); |
| error_register_wext: |
| if(test_bit(SME_SESSION_OPENED, &pAdapter->event_flags)) |
| { |
| INIT_COMPLETION(pAdapter->session_close_comp_var); |
| if( eHAL_STATUS_SUCCESS == sme_CloseSession( pHddCtx->hHal, |
| pAdapter->sessionId, |
| hdd_smeCloseSessionCallback, pAdapter ) ) |
| { |
| //Block on a completion variable. Can't wait forever though. |
| wait_for_completion_interruptible_timeout( |
| &pAdapter->session_close_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE)); |
| } |
| } |
| error_sme_open: |
| return status; |
| } |
| |
| #ifdef CONFIG_CFG80211 |
| void hdd_cleanup_actionframe( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter ) |
| { |
| hdd_cfg80211_state_t *cfgState; |
| |
| cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| |
| if( NULL != cfgState->buf ) |
| { |
| int rc; |
| INIT_COMPLETION(pAdapter->tx_action_cnf_event); |
| rc = wait_for_completion_interruptible_timeout( |
| &pAdapter->tx_action_cnf_event, |
| msecs_to_jiffies(ACTION_FRAME_TX_TIMEOUT)); |
| if(!rc) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| ("ERROR: HDD Wait for Action Confirmation Failed!!\n")); |
| } |
| } |
| return; |
| } |
| #endif |
| |
| void hdd_deinit_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter ) |
| { |
| ENTER(); |
| switch ( pAdapter->device_mode ) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_DEVICE: |
| { |
| if(test_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags)) |
| { |
| hdd_deinit_tx_rx( pAdapter ); |
| clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags); |
| } |
| |
| if(test_bit(WMM_INIT_DONE, &pAdapter->event_flags)) |
| { |
| hdd_wmm_adapter_close( pAdapter ); |
| clear_bit(WMM_INIT_DONE, &pAdapter->event_flags); |
| } |
| |
| #ifdef CONFIG_CFG80211 |
| hdd_cleanup_actionframe(pHddCtx, pAdapter); |
| #endif |
| |
| break; |
| } |
| |
| case WLAN_HDD_SOFTAP: |
| case WLAN_HDD_P2P_GO: |
| #ifdef WLAN_SOFTAP_FEATURE |
| { |
| #ifdef CONFIG_CFG80211 |
| hdd_cleanup_actionframe(pHddCtx, pAdapter); |
| #endif |
| |
| hdd_unregister_hostapd(pAdapter); |
| hdd_set_conparam( 0 ); |
| #ifdef CONFIG_CFG80211 |
| wlan_hdd_set_monitor_tx_adapter( WLAN_HDD_GET_CTX(pAdapter), NULL ); |
| #endif |
| break; |
| } |
| |
| case WLAN_HDD_MONITOR: |
| { |
| #ifdef CONFIG_CFG80211 |
| hdd_adapter_t* pAdapterforTx = pAdapter->sessionCtx.monitor.pAdapterForTx; |
| #endif |
| if(test_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags)) |
| { |
| hdd_deinit_tx_rx( pAdapter ); |
| clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags); |
| } |
| #ifdef CONFIG_CFG80211 |
| if(NULL != pAdapterforTx) |
| { |
| hdd_cleanup_actionframe(pHddCtx, pAdapterforTx); |
| } |
| #endif |
| #endif //WLAN_SOFTAP_FEATURE |
| break; |
| } |
| |
| |
| default: |
| break; |
| } |
| |
| EXIT(); |
| } |
| |
| void hdd_cleanup_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter, tANI_U8 rtnl_held ) |
| { |
| struct net_device *pWlanDev = pAdapter->dev; |
| |
| if(test_bit(NET_DEVICE_REGISTERED, &pAdapter->event_flags)) { |
| if( rtnl_held ) |
| { |
| unregister_netdevice(pWlanDev); |
| } |
| else |
| { |
| unregister_netdev(pWlanDev); |
| } |
| // note that the pAdapter is no longer valid at this point |
| // since the memory has been reclaimed |
| } |
| |
| } |
| |
| VOS_STATUS hdd_enable_bmps_imps(hdd_context_t *pHddCtx) |
| { |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| |
| if(pHddCtx->cfg_ini->fIsBmpsEnabled) |
| { |
| sme_EnablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE); |
| } |
| |
| if(pHddCtx->cfg_ini->fIsAutoBmpsTimerEnabled) |
| { |
| sme_StartAutoBmpsTimer(pHddCtx->hHal); |
| } |
| |
| if (pHddCtx->cfg_ini->fIsImpsEnabled) |
| { |
| sme_EnablePowerSave (pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE); |
| } |
| |
| return status; |
| } |
| |
| VOS_STATUS hdd_disable_bmps_imps(hdd_context_t *pHddCtx, tANI_U8 session_type) |
| { |
| hdd_adapter_t *pAdapter = NULL; |
| eHalStatus halStatus; |
| VOS_STATUS status = VOS_STATUS_E_INVAL; |
| v_BOOL_t disableBmps = FALSE; |
| v_BOOL_t disableImps = FALSE; |
| |
| switch(session_type) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_SOFTAP: |
| #ifdef WLAN_FEATURE_P2P |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_GO: |
| #endif |
| //Exit BMPS -> Is Sta/P2P Client is already connected |
| pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION); |
| if((NULL != pAdapter)&& |
| hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) |
| { |
| disableBmps = TRUE; |
| } |
| |
| pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_P2P_CLIENT); |
| if((NULL != pAdapter)&& |
| hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) |
| { |
| disableBmps = TRUE; |
| } |
| |
| //Exit both Bmps and Imps incase of Go/SAP Mode |
| if((WLAN_HDD_SOFTAP == session_type) || |
| (WLAN_HDD_P2P_GO == session_type)) |
| { |
| disableBmps = TRUE; |
| disableImps = TRUE; |
| } |
| |
| if(TRUE == disableImps) |
| { |
| if (pHddCtx->cfg_ini->fIsImpsEnabled) |
| { |
| sme_DisablePowerSave (pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE); |
| } |
| } |
| |
| if(TRUE == disableBmps) |
| { |
| if(pHddCtx->cfg_ini->fIsBmpsEnabled) |
| { |
| halStatus = sme_DisablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE); |
| |
| if(eHAL_STATUS_SUCCESS != halStatus) |
| { |
| status = VOS_STATUS_E_FAILURE; |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Fail to Disable Power Save\n", __func__); |
| VOS_ASSERT(0); |
| return status; |
| } |
| } |
| |
| if(pHddCtx->cfg_ini->fIsAutoBmpsTimerEnabled) |
| { |
| halStatus = sme_StopAutoBmpsTimer(pHddCtx->hHal); |
| |
| if(eHAL_STATUS_SUCCESS != halStatus) |
| { |
| status = VOS_STATUS_E_FAILURE; |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Fail to Stop Auto Bmps Timer\n", __func__); |
| VOS_ASSERT(0); |
| return status; |
| } |
| } |
| } |
| |
| if((TRUE == disableBmps) || |
| (TRUE == disableImps)) |
| { |
| /* Now, get the chip into Full Power now */ |
| INIT_COMPLETION(pHddCtx->full_pwr_comp_var); |
| halStatus = sme_RequestFullPower(pHddCtx->hHal, hdd_full_pwr_cbk, |
| pHddCtx, eSME_FULL_PWR_NEEDED_BY_HDD); |
| |
| if(halStatus != eHAL_STATUS_SUCCESS) |
| { |
| if(halStatus == eHAL_STATUS_PMC_PENDING) |
| { |
| //Block on a completion variable. Can't wait forever though |
| wait_for_completion_interruptible_timeout( |
| &pHddCtx->full_pwr_comp_var, msecs_to_jiffies(1000)); |
| } |
| else |
| { |
| status = VOS_STATUS_E_FAILURE; |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Request for Full Power failed\n", __func__); |
| VOS_ASSERT(0); |
| return status; |
| } |
| } |
| |
| status = VOS_STATUS_SUCCESS; |
| } |
| |
| break; |
| } |
| return status; |
| } |
| |
| hdd_adapter_t* hdd_open_adapter( hdd_context_t *pHddCtx, tANI_U8 session_type, |
| char *iface_name, tSirMacAddr macAddr, |
| tANI_U8 rtnl_held ) |
| { |
| hdd_adapter_t *pAdapter = NULL; |
| hdd_adapter_list_node_t *pHddAdapterNode = NULL; |
| VOS_STATUS status = VOS_STATUS_E_FAILURE; |
| VOS_STATUS exitbmpsStatus; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s iface =%s type = %d\n",__func__,iface_name,session_type); |
| |
| //Disable BMPS incase of Concurrency |
| exitbmpsStatus = hdd_disable_bmps_imps(pHddCtx, session_type); |
| |
| if(VOS_STATUS_E_FAILURE == exitbmpsStatus) |
| { |
| //Fail to Exit BMPS |
| VOS_ASSERT(0); |
| return NULL; |
| } |
| |
| switch(session_type) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| #ifdef WLAN_FEATURE_P2P |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_DEVICE: |
| #endif |
| { |
| pAdapter = hdd_alloc_station_adapter( pHddCtx, macAddr, iface_name ); |
| |
| if( NULL == pAdapter ) |
| return NULL; |
| |
| #ifdef CONFIG_CFG80211 |
| pAdapter->wdev.iftype = (session_type == WLAN_HDD_P2P_CLIENT) ? |
| NL80211_IFTYPE_P2P_CLIENT: |
| NL80211_IFTYPE_STATION; |
| #endif |
| |
| pAdapter->device_mode = session_type; |
| |
| status = hdd_init_station_mode( pAdapter ); |
| if( VOS_STATUS_SUCCESS != status ) |
| goto err_free_netdev; |
| |
| status = hdd_register_interface( pAdapter, rtnl_held ); |
| if( VOS_STATUS_SUCCESS != status ) |
| { |
| hdd_deinit_adapter(pHddCtx, pAdapter); |
| goto err_free_netdev; |
| } |
| //Stop the Interface TX queue. |
| netif_tx_disable(pAdapter->dev); |
| //netif_tx_disable(pWlanDev); |
| netif_carrier_off(pAdapter->dev); |
| |
| break; |
| } |
| |
| #ifdef WLAN_FEATURE_P2P |
| case WLAN_HDD_P2P_GO: |
| #endif |
| case WLAN_HDD_SOFTAP: |
| { |
| pAdapter = hdd_wlan_create_ap_dev( pHddCtx, macAddr, (tANI_U8 *)iface_name ); |
| if( NULL == pAdapter ) |
| return NULL; |
| |
| #ifdef CONFIG_CFG80211 |
| pAdapter->wdev.iftype = (session_type == WLAN_HDD_SOFTAP) ? |
| NL80211_IFTYPE_AP: |
| NL80211_IFTYPE_P2P_GO; |
| #endif |
| pAdapter->device_mode = session_type; |
| |
| status = hdd_init_ap_mode(pAdapter); |
| if( VOS_STATUS_SUCCESS != status ) |
| goto err_free_netdev; |
| |
| status = hdd_register_hostapd( pAdapter, rtnl_held ); |
| if( VOS_STATUS_SUCCESS != status ) |
| { |
| hdd_deinit_adapter(pHddCtx, pAdapter); |
| goto err_free_netdev; |
| } |
| |
| netif_tx_disable(pAdapter->dev); |
| netif_carrier_off(pAdapter->dev); |
| |
| hdd_set_conparam( 1 ); |
| break; |
| } |
| case WLAN_HDD_MONITOR: |
| { |
| #ifdef CONFIG_CFG80211 |
| pAdapter = hdd_alloc_station_adapter( pHddCtx, macAddr, iface_name ); |
| if( NULL == pAdapter ) |
| return NULL; |
| |
| pAdapter->wdev.iftype = NL80211_IFTYPE_MONITOR; |
| pAdapter->device_mode = session_type; |
| status = hdd_register_interface( pAdapter, rtnl_held ); |
| #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) |
| pAdapter->dev->netdev_ops = &wlan_mon_drv_ops; |
| #else |
| pAdapter->dev->open = hdd_mon_open; |
| pAdapter->dev->hard_start_xmit = hdd_mon_hard_start_xmit; |
| #endif |
| hdd_init_tx_rx( pAdapter ); |
| set_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags); |
| //Set adapter to be used for data tx. It will use either GO or softap. |
| pAdapter->sessionCtx.monitor.pAdapterForTx = |
| hdd_get_adapter(pAdapter->pHddCtx, WLAN_HDD_SOFTAP); |
| #ifdef WLAN_FEATURE_P2P |
| if (NULL == pAdapter->sessionCtx.monitor.pAdapterForTx) |
| { |
| pAdapter->sessionCtx.monitor.pAdapterForTx = |
| hdd_get_adapter(pAdapter->pHddCtx, WLAN_HDD_P2P_GO); |
| } |
| #endif |
| /* This workqueue will be used to transmit management packet over |
| * monitor interface. */ |
| INIT_WORK(&pAdapter->sessionCtx.monitor.pAdapterForTx->monTxWorkQueue, |
| hdd_mon_tx_work_queue); |
| #endif |
| } |
| break; |
| #ifdef ANI_MANF_DIAG |
| case WLAN_HDD_FTM: |
| { |
| pAdapter = hdd_alloc_station_adapter( pHddCtx, macAddr, iface_name ); |
| |
| if( NULL == pAdapter ) |
| return NULL; |
| /* Assign NL80211_IFTYPE_STATION as interface type to resolve Kernel Warning |
| * message while loading driver in FTM mode. */ |
| pAdapter->wdev.iftype = NL80211_IFTYPE_STATION; |
| pAdapter->device_mode = session_type; |
| status = hdd_register_interface( pAdapter, rtnl_held ); |
| } |
| break; |
| #endif |
| default: |
| { |
| VOS_ASSERT(0); |
| return NULL; |
| } |
| } |
| |
| |
| if( VOS_STATUS_SUCCESS == status ) |
| { |
| //Add it to the hdd's session list. |
| pHddAdapterNode = vos_mem_malloc( sizeof( hdd_adapter_list_node_t ) ); |
| if( NULL == pHddAdapterNode ) |
| { |
| status = VOS_STATUS_E_NOMEM; |
| } |
| else |
| { |
| pHddAdapterNode->pAdapter = pAdapter; |
| status = hdd_add_adapter_back ( pHddCtx, |
| pHddAdapterNode ); |
| } |
| } |
| |
| if( VOS_STATUS_SUCCESS != status ) |
| { |
| if( NULL != pAdapter ) |
| { |
| hdd_cleanup_adapter( pHddCtx, pAdapter, rtnl_held ); |
| pAdapter = NULL; |
| } |
| if( NULL != pHddAdapterNode ) |
| { |
| vos_mem_free( pHddAdapterNode ); |
| } |
| |
| goto resume_bmps; |
| } |
| |
| if(VOS_STATUS_SUCCESS == status) |
| { |
| wlan_hdd_set_concurrency_mode(pHddCtx, session_type); |
| |
| #ifdef FEATURE_WLAN_NON_INTEGRATED_SOC |
| /* If there are concurrent session enable SW frame translation |
| * for all registered STA |
| * This is not required in case of PRIMA as HW frame translation |
| * is disabled in PRIMA*/ |
| if (vos_concurrent_sessions_running()) |
| { |
| WLANTL_ConfigureSwFrameTXXlationForAll(pHddCtx->pvosContext, TRUE); |
| } |
| #endif |
| //Initialize the WoWL service |
| if(!hdd_init_wowl(pAdapter)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_init_wowl failed",__func__); |
| goto err_free_netdev; |
| } |
| } |
| return pAdapter; |
| |
| err_free_netdev: |
| free_netdev(pAdapter->dev); |
| wlan_hdd_release_intf_addr( pHddCtx, |
| pAdapter->macAddressCurrent.bytes ); |
| |
| resume_bmps: |
| //If bmps disabled enable it |
| if(VOS_STATUS_SUCCESS == exitbmpsStatus) |
| { |
| hdd_enable_bmps_imps(pHddCtx); |
| } |
| return NULL; |
| } |
| |
| VOS_STATUS hdd_close_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter, |
| tANI_U8 rtnl_held ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode, *pCurrent, *pNext; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pCurrent ); |
| if( VOS_STATUS_SUCCESS != status ) |
| return status; |
| |
| while ( pCurrent->pAdapter != pAdapter ) |
| { |
| status = hdd_get_next_adapter ( pHddCtx, pCurrent, &pNext ); |
| if( VOS_STATUS_SUCCESS != status ) |
| break; |
| |
| pCurrent = pNext; |
| } |
| pAdapterNode = pCurrent; |
| if( VOS_STATUS_SUCCESS == status ) |
| { |
| wlan_hdd_clear_concurrency_mode(pHddCtx, pAdapter->device_mode); |
| hdd_cleanup_adapter( pHddCtx, pAdapterNode->pAdapter, rtnl_held ); |
| hdd_remove_adapter( pHddCtx, pAdapterNode ); |
| vos_mem_free( pAdapterNode ); |
| |
| #ifdef FEATURE_WLAN_NON_INTEGRATED_SOC |
| /* If there is no concurrent session disable SW frame translation |
| * for all registered STA */ |
| /* This is not required in case of PRIMA as HW frame translation |
| * is disabled in PRIMA*/ |
| if (!vos_concurrent_sessions_running()) |
| { |
| WLANTL_ConfigureSwFrameTXXlationForAll(pHddCtx->pvosContext, FALSE); |
| } |
| #endif |
| |
| /* If there is a single session of STA/P2P client, re-enable BMPS */ |
| if ((!vos_concurrent_sessions_running()) && |
| ((pHddCtx->no_of_sessions[VOS_STA_MODE] >= 1) || |
| (pHddCtx->no_of_sessions[VOS_P2P_CLIENT_MODE] >= 1))) |
| { |
| hdd_enable_bmps_imps(pHddCtx); |
| } |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| VOS_STATUS hdd_close_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pHddAdapterNode; |
| VOS_STATUS status; |
| |
| ENTER(); |
| |
| do |
| { |
| status = hdd_remove_front_adapter( pHddCtx, &pHddAdapterNode ); |
| if( pHddAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| hdd_cleanup_adapter( pHddCtx, pHddAdapterNode->pAdapter, FALSE ); |
| vos_mem_free( pHddAdapterNode ); |
| } |
| }while( NULL != pHddAdapterNode && VOS_STATUS_E_EMPTY != status ); |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| void wlan_hdd_reset_prob_rspies(hdd_adapter_t* pHostapdAdapter) |
| { |
| v_U8_t addIE[1] = {0}; |
| |
| if ( eHAL_STATUS_FAILURE == ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal, |
| WNI_CFG_PROBE_RSP_ADDNIE_DATA1,(tANI_U8*)addIE, 0, NULL, |
| eANI_BOOLEAN_FALSE) ) |
| { |
| hddLog(LOGE, |
| "Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_DATA1 to CCM\n"); |
| } |
| |
| if ( eHAL_STATUS_FAILURE == ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal, |
| WNI_CFG_PROBE_RSP_ADDNIE_DATA2, (tANI_U8*)addIE, 0, NULL, |
| eANI_BOOLEAN_FALSE) ) |
| { |
| hddLog(LOGE, |
| "Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_DATA2 to CCM\n"); |
| } |
| |
| if ( eHAL_STATUS_FAILURE == ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal, |
| WNI_CFG_PROBE_RSP_ADDNIE_DATA3, (tANI_U8*)addIE, 0, NULL, |
| eANI_BOOLEAN_FALSE) ) |
| { |
| hddLog(LOGE, |
| "Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_DATA3 to CCM\n"); |
| } |
| } |
| |
| VOS_STATUS hdd_stop_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter ) |
| { |
| eHalStatus halStatus = eHAL_STATUS_SUCCESS; |
| hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter); |
| union iwreq_data wrqu; |
| |
| ENTER(); |
| |
| switch(pAdapter->device_mode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_DEVICE: |
| if( hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR( pAdapter )) ) |
| { |
| if (pWextState->roamProfile.BSSType == eCSR_BSS_TYPE_START_IBSS) |
| halStatus = sme_RoamDisconnect(pHddCtx->hHal, |
| pAdapter->sessionId, |
| eCSR_DISCONNECT_REASON_IBSS_LEAVE); |
| else |
| halStatus = sme_RoamDisconnect(pHddCtx->hHal, |
| pAdapter->sessionId, |
| eCSR_DISCONNECT_REASON_UNSPECIFIED); |
| //success implies disconnect command got queued up successfully |
| if(halStatus == eHAL_STATUS_SUCCESS) |
| { |
| wait_for_completion_interruptible_timeout(&pAdapter->disconnect_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT)); |
| } |
| memset(&wrqu, '\0', sizeof(wrqu)); |
| wrqu.ap_addr.sa_family = ARPHRD_ETHER; |
| memset(wrqu.ap_addr.sa_data,'\0',ETH_ALEN); |
| wireless_send_event(pAdapter->dev, SIOCGIWAP, &wrqu, NULL); |
| } |
| else |
| { |
| hdd_abort_mac_scan(pHddCtx); |
| } |
| |
| if (test_bit(SME_SESSION_OPENED, &pAdapter->event_flags)) |
| { |
| INIT_COMPLETION(pAdapter->session_close_comp_var); |
| if (eHAL_STATUS_SUCCESS == |
| sme_CloseSession(pHddCtx->hHal, pAdapter->sessionId, |
| hdd_smeCloseSessionCallback, pAdapter)) |
| { |
| //Block on a completion variable. Can't wait forever though. |
| wait_for_completion_interruptible_timeout( |
| &pAdapter->session_close_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE)); |
| } |
| } |
| |
| break; |
| |
| case WLAN_HDD_SOFTAP: |
| case WLAN_HDD_P2P_GO: |
| //Any softap specific cleanup here... |
| mutex_lock(&pHddCtx->sap_lock); |
| if (test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags)) |
| { |
| VOS_STATUS status; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| //Stop Bss. |
| status = WLANSAP_StopBss(pHddCtx->pvosContext); |
| if (VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hdd_hostapd_state_t *pHostapdState = |
| WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter); |
| |
| status = vos_wait_single_event(&pHostapdState->vosEvent, 10000); |
| |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(LOGE, "%s: failure waiting for WLANSAP_StopBss", |
| __FUNCTION__); |
| } |
| } |
| else |
| { |
| hddLog(LOGE, "%s: failure in WLANSAP_StopBss", __FUNCTION__); |
| } |
| clear_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags); |
| |
| if (eHAL_STATUS_FAILURE == |
| ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG, |
| 0, NULL, eANI_BOOLEAN_FALSE)) |
| { |
| hddLog(LOGE, |
| "%s: Failed to set WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG", |
| __FUNCTION__); |
| } |
| |
| if ( eHAL_STATUS_FAILURE == ccmCfgSetInt((WLAN_HDD_GET_CTX(pAdapter))->hHal, |
| WNI_CFG_ASSOC_RSP_ADDNIE_FLAG, 0, NULL, |
| eANI_BOOLEAN_FALSE) ) |
| { |
| hddLog(LOGE, |
| "Could not pass on WNI_CFG_ASSOC_RSP_ADDNIE_FLAG to CCM"); |
| } |
| |
| // Reset WNI_CFG_PROBE_RSP Flags |
| wlan_hdd_reset_prob_rspies(pAdapter); |
| kfree(pAdapter->sessionCtx.ap.beacon); |
| pAdapter->sessionCtx.ap.beacon = NULL; |
| } |
| mutex_unlock(&pHddCtx->sap_lock); |
| break; |
| case WLAN_HDD_MONITOR: |
| break; |
| default: |
| break; |
| } |
| |
| EXIT(); |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| VOS_STATUS hdd_stop_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| netif_tx_disable(pAdapter->dev); |
| netif_carrier_off(pAdapter->dev); |
| |
| hdd_stop_adapter( pHddCtx, pAdapter ); |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| VOS_STATUS hdd_reset_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| netif_tx_disable(pAdapter->dev); |
| netif_carrier_off(pAdapter->dev); |
| |
| //Record whether STA is associated |
| pAdapter->sessionCtx.station.bSendDisconnect = |
| hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR( pAdapter )) ? |
| VOS_TRUE : VOS_FALSE; |
| |
| hdd_deinit_tx_rx(pAdapter); |
| hdd_wmm_adapter_close(pAdapter); |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| VOS_STATUS hdd_start_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| v_MACADDR_t bcastMac = VOS_MAC_ADDR_BROADCAST_INITIALIZER; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| switch(pAdapter->device_mode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_DEVICE: |
| hdd_init_station_mode(pAdapter); |
| /* Open the gates for HDD to receive Wext commands */ |
| pAdapter->isLinkUpSvcNeeded = FALSE; |
| pHddCtx->scan_info.mScanPending = FALSE; |
| pHddCtx->scan_info.waitScanResult = FALSE; |
| |
| //Trigger the initial scan |
| hdd_wlan_initial_scan(pAdapter); |
| |
| //Indicate disconnect event to supplicant if associated previously |
| if(pAdapter->sessionCtx.station.bSendDisconnect) |
| { |
| union iwreq_data wrqu; |
| memset(&wrqu, '\0', sizeof(wrqu)); |
| wrqu.ap_addr.sa_family = ARPHRD_ETHER; |
| memset(wrqu.ap_addr.sa_data,'\0',ETH_ALEN); |
| wireless_send_event(pAdapter->dev, SIOCGIWAP, &wrqu, NULL); |
| pAdapter->sessionCtx.station.bSendDisconnect = VOS_FALSE; |
| |
| #ifdef CONFIG_CFG80211 |
| /* indicate disconnected event to nl80211 */ |
| cfg80211_disconnected(pAdapter->dev, WLAN_REASON_UNSPECIFIED, |
| NULL, 0, GFP_KERNEL); |
| #endif |
| } |
| break; |
| |
| case WLAN_HDD_SOFTAP: |
| /* softAP can handle SSR */ |
| break; |
| |
| case WLAN_HDD_P2P_GO: |
| #ifdef CONFIG_CFG80211 |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s [SSR] send restart supplicant", |
| __func__); |
| /* event supplicant to restart */ |
| cfg80211_del_sta(pAdapter->dev, |
| (const u8 *)&bcastMac.bytes[0], GFP_KERNEL); |
| #endif |
| break; |
| |
| case WLAN_HDD_MONITOR: |
| /* monitor interface start */ |
| break; |
| default: |
| break; |
| } |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| VOS_STATUS hdd_reconnect_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| v_U32_t roamId; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( (WLAN_HDD_INFRA_STATION == pAdapter->device_mode) || |
| (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ) |
| { |
| hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter); |
| |
| pHddStaCtx->conn_info.connState = eConnectionState_NotConnected; |
| init_completion(&pAdapter->disconnect_comp_var); |
| sme_RoamDisconnect(pHddCtx->hHal, pAdapter->sessionId, |
| eCSR_DISCONNECT_REASON_UNSPECIFIED); |
| |
| wait_for_completion_interruptible_timeout( |
| &pAdapter->disconnect_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT)); |
| |
| pWextState->roamProfile.csrPersona = pAdapter->device_mode; |
| pHddCtx->isAmpAllowed = VOS_FALSE; |
| sme_RoamConnect(pHddCtx->hHal, |
| pAdapter->sessionId, &(pWextState->roamProfile), |
| &roamId); |
| } |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| v_U8_t hdd_is_ssr_required( void) |
| { |
| return isSsrRequired; |
| } |
| |
| void hdd_set_ssr_required( v_U8_t value) |
| { |
| isSsrRequired = value; |
| } |
| |
| VOS_STATUS hdd_get_front_adapter( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t** ppAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_peek_front ( &pHddCtx->hddAdapters, |
| (hdd_list_node_t**) ppAdapterNode ); |
| spin_unlock(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_get_next_adapter( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t* pAdapterNode, |
| hdd_adapter_list_node_t** pNextAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_peek_next ( &pHddCtx->hddAdapters, |
| (hdd_list_node_t*) pAdapterNode, |
| (hdd_list_node_t**)pNextAdapterNode ); |
| |
| spin_unlock(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_remove_adapter( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t* pAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_remove_node ( &pHddCtx->hddAdapters, |
| &pAdapterNode->node ); |
| spin_unlock(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_remove_front_adapter( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t** ppAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_remove_front( &pHddCtx->hddAdapters, |
| (hdd_list_node_t**) ppAdapterNode ); |
| spin_unlock(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_add_adapter_back( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t* pAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_insert_back ( &pHddCtx->hddAdapters, |
| (hdd_list_node_t*) pAdapterNode ); |
| spin_unlock(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_add_adapter_front( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t* pAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_insert_front ( &pHddCtx->hddAdapters, |
| (hdd_list_node_t*) pAdapterNode ); |
| spin_unlock(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| hdd_adapter_t * hdd_get_adapter_by_macaddr( hdd_context_t *pHddCtx, |
| tSirMacAddr macAddr ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( pAdapter && vos_mem_compare( pAdapter->macAddressCurrent.bytes, |
| macAddr, sizeof(tSirMacAddr) ) ) |
| { |
| return pAdapter; |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| return NULL; |
| |
| } |
| |
| hdd_adapter_t * hdd_get_adapter_by_name( hdd_context_t *pHddCtx, tANI_U8 *name ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( pAdapter && !strncmp( pAdapter->dev->name, (const char *)name, |
| IFNAMSIZ ) ) |
| { |
| return pAdapter; |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| return NULL; |
| |
| } |
| |
| hdd_adapter_t * hdd_get_adapter( hdd_context_t *pHddCtx, device_mode_t mode ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( pAdapter && (mode == pAdapter->device_mode) ) |
| { |
| return pAdapter; |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| return NULL; |
| |
| } |
| |
| //Remove this function later |
| hdd_adapter_t * hdd_get_mon_adapter( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( pAdapter && WLAN_HDD_MONITOR == pAdapter->device_mode ) |
| { |
| return pAdapter; |
| } |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| return NULL; |
| |
| } |
| |
| #ifdef CONFIG_CFG80211 |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_set_monitor_tx_adapter() - |
| |
| This API initializes the adapter to be used while transmitting on monitor |
| adapter. |
| |
| \param - pHddCtx - Pointer to the HDD context. |
| pAdapter - Adapter that will used for TX. This can be NULL. |
| \return - None. |
| --------------------------------------------------------------------------*/ |
| void wlan_hdd_set_monitor_tx_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter ) |
| { |
| hdd_adapter_t *pMonAdapter; |
| |
| pMonAdapter = hdd_get_adapter( pHddCtx, WLAN_HDD_MONITOR ); |
| |
| if( NULL != pMonAdapter ) |
| { |
| pMonAdapter->sessionCtx.monitor.pAdapterForTx = pAdapter; |
| } |
| } |
| #endif |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_select_queue() - |
| |
| This API returns the operating channel of the requested device mode |
| |
| \param - pHddCtx - Pointer to the HDD context. |
| - mode - Device mode for which operating channel is required |
| suported modes - WLAN_HDD_INFRA_STATION, WLAN_HDD_P2P_CLIENT |
| WLAN_HDD_SOFTAP, WLAN_HDD_P2P_GO. |
| \return - channel number. "0" id the requested device is not found OR it is not connected. |
| --------------------------------------------------------------------------*/ |
| v_U8_t hdd_get_operating_channel( hdd_context_t *pHddCtx, device_mode_t mode ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| v_U8_t operatingChannel = 0; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( mode == pAdapter->device_mode ) |
| { |
| switch(pAdapter->device_mode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_P2P_CLIENT: |
| if( hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR( pAdapter )) ) |
| operatingChannel = (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.operationChannel; |
| break; |
| case WLAN_HDD_SOFTAP: |
| case WLAN_HDD_P2P_GO: |
| /*softap connection info */ |
| if(test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags)) |
| operatingChannel = (WLAN_HDD_GET_AP_CTX_PTR(pAdapter))->operatingChannel; |
| break; |
| default: |
| break; |
| } |
| |
| break; //Found the device of interest. break the loop |
| } |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| return operatingChannel; |
| } |
| |
| #ifdef WLAN_FEATURE_PACKET_FILTERING |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_set_multicast_list() - |
| |
| This used to set the multicast address list. |
| |
| \param - dev - Pointer to the WLAN device. |
| - skb - Pointer to OS packet (sk_buff). |
| \return - success/fail |
| |
| --------------------------------------------------------------------------*/ |
| static void hdd_set_multicast_list(struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx; |
| int mc_count; |
| int i = 0; |
| struct netdev_hw_addr *ha; |
| pHddCtx = (hdd_context_t*)pAdapter->pHddCtx; |
| if (NULL == pHddCtx) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: HDD context is Null", __FUNCTION__); |
| return; |
| } |
| |
| if (dev->flags & IFF_ALLMULTI) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: allow all multicast frames", __FUNCTION__); |
| pHddCtx->mc_addr_list.mc_cnt = 0; |
| } |
| else |
| { |
| mc_count = netdev_mc_count(dev); |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: mc_count = %u", __FUNCTION__, mc_count); |
| if (mc_count > WLAN_HDD_MAX_MC_ADDR_LIST) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: No free filter available; allow all multicast frames", __FUNCTION__); |
| pHddCtx->mc_addr_list.mc_cnt = 0; |
| return; |
| } |
| |
| pHddCtx->mc_addr_list.mc_cnt = mc_count; |
| |
| netdev_for_each_mc_addr(ha, dev) { |
| if (i == mc_count) |
| break; |
| memset(&(pHddCtx->mc_addr_list.addr[i][0]), 0, ETH_ALEN); |
| memcpy(&(pHddCtx->mc_addr_list.addr[i][0]), ha->addr, ETH_ALEN); |
| hddLog(VOS_TRACE_LEVEL_INFO, "\n%s: mlist[%d] = %02x:%02x:%02x:%02x:%02x:%02x", |
| __func__, i, |
| pHddCtx->mc_addr_list.addr[i][0], pHddCtx->mc_addr_list.addr[i][1], |
| pHddCtx->mc_addr_list.addr[i][2], pHddCtx->mc_addr_list.addr[i][3], |
| pHddCtx->mc_addr_list.addr[i][4], pHddCtx->mc_addr_list.addr[i][5]); |
| i++; |
| } |
| } |
| return; |
| } |
| #endif |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_select_queue() - |
| |
| This function is registered with the Linux OS for network |
| core to decide which queue to use first. |
| |
| \param - dev - Pointer to the WLAN device. |
| - skb - Pointer to OS packet (sk_buff). |
| \return - ac, Queue Index/access category corresponding to UP in IP header |
| |
| --------------------------------------------------------------------------*/ |
| v_U16_t hdd_select_queue(struct net_device *dev, |
| struct sk_buff *skb) |
| { |
| return hdd_wmm_select_queue(dev, skb); |
| } |
| |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_wlan_initial_scan() - |
| |
| This function triggers the initial scan |
| |
| \param - pAdapter - Pointer to the HDD adapter. |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_wlan_initial_scan(hdd_adapter_t *pAdapter) |
| { |
| tCsrScanRequest scanReq; |
| tCsrChannelInfo channelInfo; |
| eHalStatus halStatus; |
| unsigned long scanId; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| vos_mem_zero(&scanReq, sizeof(tCsrScanRequest)); |
| vos_mem_set(&scanReq.bssid, sizeof(tCsrBssid), 0xff); |
| scanReq.BSSType = eCSR_BSS_TYPE_ANY; |
| |
| if(sme_Is11dSupported(pHddCtx->hHal)) |
| { |
| halStatus = sme_ScanGetBaseChannels( pHddCtx->hHal, &channelInfo ); |
| if ( HAL_STATUS_SUCCESS( halStatus ) ) |
| { |
| scanReq.ChannelInfo.ChannelList = vos_mem_malloc(channelInfo.numOfChannels); |
| if( !scanReq.ChannelInfo.ChannelList ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s kmalloc failed", __func__); |
| vos_mem_free(channelInfo.ChannelList); |
| return; |
| } |
| vos_mem_copy(scanReq.ChannelInfo.ChannelList, channelInfo.ChannelList, |
| channelInfo.numOfChannels); |
| scanReq.ChannelInfo.numOfChannels = channelInfo.numOfChannels; |
| vos_mem_free(channelInfo.ChannelList); |
| } |
| |
| scanReq.scanType = eSIR_PASSIVE_SCAN; |
| scanReq.requestType = eCSR_SCAN_REQUEST_11D_SCAN; |
| scanReq.maxChnTime = pHddCtx->cfg_ini->nPassiveMaxChnTime; |
| scanReq.minChnTime = pHddCtx->cfg_ini->nPassiveMinChnTime; |
| } |
| else |
| { |
| scanReq.scanType = eSIR_ACTIVE_SCAN; |
| scanReq.requestType = eCSR_SCAN_REQUEST_FULL_SCAN; |
| scanReq.maxChnTime = pHddCtx->cfg_ini->nActiveMaxChnTime; |
| scanReq.minChnTime = pHddCtx->cfg_ini->nActiveMinChnTime; |
| } |
| |
| halStatus = sme_ScanRequest(pHddCtx->hHal, pAdapter->sessionId, &scanReq, &scanId, NULL, NULL); |
| if ( !HAL_STATUS_SUCCESS( halStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: sme_ScanRequest failed status code %d", |
| __func__, halStatus ); |
| } |
| |
| if(sme_Is11dSupported(pHddCtx->hHal)) |
| vos_mem_free(scanReq.ChannelInfo.ChannelList); |
| } |
| |
| struct fullPowerContext |
| { |
| struct completion completion; |
| unsigned int magic; |
| }; |
| #define POWER_CONTEXT_MAGIC 0x504F5752 //POWR |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_full_power_callback() - HDD full power callback function |
| |
| This is the function invoked by SME to inform the result of a full power |
| request issued by HDD |
| |
| \param - callbackcontext - Pointer to cookie |
| \param - status - result of request |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| static void hdd_full_power_callback(void *callbackContext, eHalStatus status) |
| { |
| struct fullPowerContext *pContext = callbackContext; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: context = %p, status = %d", pContext, status); |
| |
| if (NULL == callbackContext) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Bad param, context [%p]", |
| __FUNCTION__, callbackContext); |
| return; |
| } |
| |
| /* there is a race condition that exists between this callback function |
| and the caller since the caller could time out either before or |
| while this code is executing. we'll assume the timeout hasn't |
| occurred, but we'll verify that right before we save our work */ |
| |
| if (POWER_CONTEXT_MAGIC != pContext->magic) |
| { |
| /* the caller presumably timed out so there is nothing we can do */ |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "%s: Invalid context, magic [%08x]", |
| __FUNCTION__, pContext->magic); |
| return; |
| } |
| |
| /* the race is on. caller could have timed out immediately after |
| we verified the magic, but if so, caller will wait a short time |
| for us to notify the caller, so the context will stay valid */ |
| complete(&pContext->completion); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_wlan_exit() - HDD WLAN exit function |
| |
| This is the driver exit point (invoked during rmmod) |
| |
| \param - pHddCtx - Pointer to the HDD Context |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_wlan_exit(hdd_context_t *pHddCtx) |
| { |
| eHalStatus halStatus; |
| v_CONTEXT_t pVosContext = pHddCtx->pvosContext; |
| VOS_STATUS vosStatus; |
| #ifdef ANI_BUS_TYPE_SDIO |
| struct sdio_func *sdio_func_dev = NULL; |
| #endif // ANI_BUS_TYPE_SDIO |
| #ifdef CONFIG_CFG80211 |
| struct wiphy *wiphy = pHddCtx->wiphy; |
| #endif |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| hdd_adapter_t* pAdapter; |
| #endif |
| struct fullPowerContext powerContext; |
| long lrc; |
| |
| ENTER(); |
| |
| // Unloading, restart logic is no more required. |
| wlan_hdd_restart_deinit(pHddCtx); |
| |
| #ifdef CONFIG_CFG80211 |
| #ifdef WLAN_SOFTAP_FEATURE |
| if (VOS_STA_SAP_MODE != hdd_get_conparam()) |
| #endif |
| { |
| #ifdef ANI_MANF_DIAG |
| if (VOS_FTM_MODE != hdd_get_conparam()) |
| #endif /* ANI_MANF_DIAG */ |
| { |
| hdd_adapter_t* pAdapter = hdd_get_adapter(pHddCtx, |
| WLAN_HDD_INFRA_STATION); |
| if (pAdapter == NULL) |
| pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_P2P_CLIENT); |
| |
| if (pAdapter != NULL) |
| { |
| wlan_hdd_cfg80211_pre_voss_stop(pAdapter); |
| hdd_UnregisterWext(pAdapter->dev); |
| } |
| } |
| } |
| #endif |
| |
| #ifdef ANI_MANF_DIAG |
| if (VOS_FTM_MODE == hdd_get_conparam()) |
| { |
| wlan_hdd_ftm_close(pHddCtx); |
| goto free_hdd_ctx; |
| } |
| #endif |
| //Stop the Interface TX queue. |
| //netif_tx_disable(pWlanDev); |
| //netif_carrier_off(pWlanDev); |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| // unregister suspend/resume callbacks |
| if(pHddCtx->cfg_ini->nEnableSuspend) |
| { |
| unregister_wlan_suspend(); |
| } |
| #endif |
| |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| #ifdef WLAN_SOFTAP_FEATURE |
| if (VOS_STA_SAP_MODE == hdd_get_conparam()) |
| { |
| pAdapter = hdd_get_adapter(pHddCtx, |
| WLAN_HDD_SOFTAP); |
| } |
| else |
| { |
| #endif |
| #ifdef ANI_MANF_DIAG |
| if (VOS_FTM_MODE != hdd_get_conparam()) |
| #endif /* ANI_MANF_DIAG */ |
| { |
| pAdapter = hdd_get_adapter(pHddCtx, |
| WLAN_HDD_INFRA_STATION); |
| } |
| #ifdef WLAN_SOFTAP_FEATURE |
| } |
| #endif |
| /* DeRegister with platform driver as client for Suspend/Resume */ |
| vosStatus = hddDeregisterPmOps(pHddCtx); |
| if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDeregisterPmOps failed",__func__); |
| VOS_ASSERT(0); |
| } |
| |
| vosStatus = hddDevTmUnregisterNotifyCallback(pHddCtx); |
| if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDevTmUnregisterNotifyCallback failed",__func__); |
| } |
| #endif //FEATURE_WLAN_INTEGRATED_SOC |
| |
| // Cancel any outstanding scan requests. We are about to close all |
| // of our adapters, but an adapter structure is what SME passes back |
| // to our callback function. Hence if there are any outstanding scan |
| // requests then there is a race condition between when the adapter |
| // is closed and when the callback is invoked. We try to resolve that |
| // race condition here by canceling any outstanding scans before we |
| // close the adapters. |
| // Note that the scans may be cancelled in an asynchronous manner, so |
| // ideally there needs to be some kind of synchronization. Rather than |
| // introduce a new synchronization here, we will utilize the fact that |
| // we are about to Request Full Power, and since that is synchronized, |
| // the expectation is that by the time Request Full Power has completed, |
| // all scans will be cancelled. |
| hdd_abort_mac_scan( pHddCtx ); |
| |
| //Disable IMPS/BMPS as we do not want the device to enter any power |
| //save mode during shutdown |
| sme_DisablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE); |
| sme_DisablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE); |
| sme_DisablePowerSave(pHddCtx->hHal, ePMC_UAPSD_MODE_POWER_SAVE); |
| |
| //Ensure that device is in full power as we will touch H/W during vos_Stop |
| init_completion(&powerContext.completion); |
| powerContext.magic = POWER_CONTEXT_MAGIC; |
| |
| halStatus = sme_RequestFullPower(pHddCtx->hHal, hdd_full_power_callback, |
| &powerContext, eSME_FULL_PWR_NEEDED_BY_HDD); |
| |
| if (eHAL_STATUS_SUCCESS != halStatus) |
| { |
| if (eHAL_STATUS_PMC_PENDING == halStatus) |
| { |
| /* request was sent -- wait for the response */ |
| lrc = wait_for_completion_interruptible_timeout( |
| &powerContext.completion, |
| msecs_to_jiffies(WLAN_WAIT_TIME_POWER)); |
| /* either we have a response or we timed out |
| either way, first invalidate our magic */ |
| powerContext.magic = 0; |
| if (lrc <= 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: %s while requesting full power", |
| __FUNCTION__, (0 == lrc) ? "timeout" : "interrupt"); |
| /* there is a race condition such that the callback |
| function could be executing at the same time we are. of |
| primary concern is if the callback function had already |
| verified the "magic" but hasn't yet set the completion |
| variable. Since the completion variable is on our |
| stack, we'll delay just a bit to make sure the data is |
| still valid if that is the case */ |
| msleep(50); |
| } |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Request for Full Power failed, status %d", |
| __FUNCTION__, halStatus); |
| VOS_ASSERT(0); |
| /* continue -- need to clean up as much as possible */ |
| } |
| } |
| |
| // Unregister the Net Device Notifier |
| unregister_netdevice_notifier(&hdd_netdev_notifier); |
| |
| hdd_stop_all_adapters( pHddCtx ); |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| sdio_func_dev = libra_getsdio_funcdev(); |
| |
| if(sdio_func_dev == NULL) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: sdio_func_dev is NULL!",__func__); |
| VOS_ASSERT(0); |
| return; |
| } |
| |
| sd_claim_host(sdio_func_dev); |
| |
| /* Disable SDIO IRQ since we are exiting */ |
| libra_enable_sdio_irq(sdio_func_dev, 0); |
| |
| sd_release_host(sdio_func_dev); |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| #ifdef WLAN_BTAMP_FEATURE |
| vosStatus = WLANBAP_Stop(pVosContext); |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to stop BAP",__func__); |
| } |
| #endif //WLAN_BTAMP_FEATURE |
| |
| //Stop all the modules |
| vosStatus = vos_stop( pVosContext ); |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to stop VOSS",__func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| } |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| vosStatus = WLANBAL_Stop( pVosContext ); |
| |
| hddLog(VOS_TRACE_LEVEL_ERROR,"WLAN BAL STOP\n"); |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to stop BAL",__func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| } |
| |
| msleep(50); |
| //Put the chip is standby before asserting deep sleep |
| vosStatus = WLANBAL_SuspendChip( pVosContext ); |
| |
| hddLog(VOS_TRACE_LEVEL_ERROR,"WLAN Suspend Chip\n"); |
| |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to suspend chip ",__func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| } |
| //Invoke SAL stop |
| vosStatus = WLANSAL_Stop( pVosContext ); |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to stop SAL",__func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| } |
| |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| //Assert Deep sleep signal now to put Libra HW in lowest power state |
| vosStatus = vos_chipAssertDeepSleep( NULL, NULL, NULL ); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| |
| //Vote off any PMIC voltage supplies |
| vos_chipPowerDown(NULL, NULL, NULL); |
| |
| vos_chipVoteOffXOBuffer(NULL, NULL, NULL); |
| |
| //Clean up HDD Nlink Service |
| send_btc_nlink_msg(WLAN_MODULE_DOWN_IND, 0); |
| nl_srv_exit(); |
| |
| //This requires pMac access, Call this before vos_close(). |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| hdd_unregister_mcast_bcast_filter(pHddCtx); |
| #endif |
| |
| //Close the scheduler before calling vos_close to make sure no thread is |
| // scheduled after the each module close is called i.e after all the data |
| // structures are freed. |
| vosStatus = vos_sched_close( pVosContext ); |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to close VOSS Scheduler",__func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| } |
| |
| #ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK |
| /* Destroy the wake lock */ |
| wake_lock_destroy(&pHddCtx->rx_wake_lock); |
| #endif |
| |
| //Close VOSS |
| //This frees pMac(HAL) context. There should not be any call that requires pMac access after this. |
| vos_close(pVosContext); |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| vosStatus = WLANBAL_Close(pVosContext); |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to close BAL",__func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| } |
| hddLog(VOS_TRACE_LEVEL_ERROR,"Returned WLAN BAL CLOSE\n\n\n\n"); |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| //Close Watchdog |
| if(pHddCtx->cfg_ini->fIsLogpEnabled) |
| vos_watchdog_close(pVosContext); |
| |
| /* Cancel the vote for XO Core ON. |
| * This is done here to ensure there is no race condition since MC, TX and WD threads have |
| * exited at this point |
| */ |
| hddLog(VOS_TRACE_LEVEL_WARN, "In module exit: Cancel the vote for XO Core ON" |
| " when WLAN is turned OFF\n"); |
| if (vos_chipVoteXOCore(NULL, NULL, NULL, VOS_FALSE) != VOS_STATUS_SUCCESS) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "Could not cancel the vote for XO Core ON." |
| " Not returning failure." |
| " Power consumed will be high\n"); |
| } |
| |
| hdd_close_all_adapters( pHddCtx ); |
| |
| |
| //Free up dynamically allocated members inside HDD Adapter |
| kfree(pHddCtx->cfg_ini); |
| pHddCtx->cfg_ini= NULL; |
| |
| /* free the power on lock from platform driver */ |
| if (free_riva_power_on_lock("wlan")) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to free power on lock", |
| __func__); |
| } |
| |
| #ifdef ANI_MANF_DIAG |
| free_hdd_ctx: |
| #endif |
| #ifdef CONFIG_CFG80211 |
| wiphy_unregister(wiphy) ; |
| wiphy_free(wiphy) ; |
| #else |
| vos_mem_free( pHddCtx ); |
| #endif |
| if (hdd_is_ssr_required()) |
| { |
| /* WDI timeout had happened during unload, so SSR is needed here */ |
| subsystem_restart("riva"); |
| msleep(5000); |
| } |
| hdd_set_ssr_required (VOS_FALSE); |
| } |
| |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_update_config_from_nv() - Function to update the contents of |
| the running configuration with parameters taken from NV storage |
| |
| \param - pHddCtx - Pointer to the HDD global context |
| |
| \return - VOS_STATUS_SUCCESS if successful |
| |
| --------------------------------------------------------------------------*/ |
| static VOS_STATUS hdd_update_config_from_nv(hdd_context_t* pHddCtx) |
| { |
| #ifndef FEATURE_WLAN_INTEGRATED_SOC |
| eHalStatus halStatus; |
| #endif |
| |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| v_BOOL_t itemIsValid = VOS_FALSE; |
| VOS_STATUS status; |
| v_MACADDR_t macFromNV[VOS_MAX_CONCURRENCY_PERSONA]; |
| v_U8_t macLoop; |
| |
| /*If the NV is valid then get the macaddress from nv else get it from qcom_cfg.ini*/ |
| status = vos_nv_getValidity(VNV_FIELD_IMAGE, &itemIsValid); |
| if(status != VOS_STATUS_SUCCESS) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR," vos_nv_getValidity() failed\n "); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| if (itemIsValid == VOS_TRUE) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH," Reading the Macaddress from NV\n "); |
| status = vos_nv_readMultiMacAddress((v_U8_t *)&macFromNV[0].bytes[0], |
| VOS_MAX_CONCURRENCY_PERSONA); |
| if(status != VOS_STATUS_SUCCESS) |
| { |
| /* Get MAC from NV fail, not update CFG info |
| * INI MAC value will be used for MAC setting */ |
| hddLog(VOS_TRACE_LEVEL_ERROR," vos_nv_readMacAddress() failed\n "); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| /* If first MAC is not valid, treat all others are not valid |
| * Then all MACs will be got from ini file */ |
| if(vos_is_macaddr_zero(&macFromNV[0])) |
| { |
| /* MAC address in NV file is not configured yet */ |
| hddLog(VOS_TRACE_LEVEL_WARN, "Invalid MAC in NV file"); |
| return VOS_STATUS_E_INVAL; |
| } |
| |
| /* Get MAC address from NV, update CFG info */ |
| for(macLoop = 0; macLoop < VOS_MAX_CONCURRENCY_PERSONA; macLoop++) |
| { |
| if(vos_is_macaddr_zero(&macFromNV[macLoop])) |
| { |
| printk(KERN_ERR "not valid MAC from NV for %d", macLoop); |
| /* This MAC is not valid, skip it |
| * This MAC will be got from ini file */ |
| } |
| else |
| { |
| vos_mem_copy((v_U8_t *)&pHddCtx->cfg_ini->intfMacAddr[macLoop].bytes[0], |
| (v_U8_t *)&macFromNV[macLoop].bytes[0], |
| VOS_MAC_ADDR_SIZE); |
| } |
| } |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "NV ITEM, MAC Not valid"); |
| return VOS_STATUS_E_FAILURE; |
| } |
| #endif /* FEATURE_WLAN_INTEGRATED_SOC */ |
| |
| #ifndef FEATURE_WLAN_INTEGRATED_SOC |
| #if 1 /* need to fix for concurrency */ |
| // Set the MAC Address |
| // Currently this is used by HAL to add self sta. Remove this once self sta is added as part of session open. |
| halStatus = ccmCfgSetStr( pHddCtx->hHal, WNI_CFG_STA_ID, |
| (v_U8_t *)&pHddCtx->cfg_ini->intfMacAddr[0], |
| sizeof( pHddCtx->cfg_ini->intfMacAddr[0]), |
| hdd_set_mac_addr_cb, VOS_FALSE ); |
| |
| if (!HAL_STATUS_SUCCESS( halStatus )) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Failed to set MAC Address. " |
| "HALStatus is %08d [x%08x]",__func__, halStatus, halStatus ); |
| return VOS_STATUS_E_FAILURE; |
| } |
| #endif |
| #endif |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_post_voss_start_config() - HDD post voss start config helper |
| |
| \param - pAdapter - Pointer to the HDD |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| VOS_STATUS hdd_post_voss_start_config(hdd_context_t* pHddCtx) |
| { |
| eHalStatus halStatus; |
| v_U32_t listenInterval; |
| |
| #ifdef FEATURE_WLAN_NON_INTEGRATED_SOC |
| /* In the non-integrated architecture we update the configuration from |
| the INI file and from NV after vOSS has been started |
| */ |
| |
| // Apply the cfg.ini to cfg.dat |
| if (FALSE == hdd_update_config_dat(pHddCtx)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: config update failed",__func__ ); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| // Apply the NV to cfg.dat |
| if (VOS_STATUS_SUCCESS != hdd_update_config_from_nv(pHddCtx)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: config update from NV failed", __func__ ); |
| return VOS_STATUS_E_FAILURE; |
| } |
| #endif // FEATURE_WLAN_NON_INTEGRATED_SOC |
| |
| // Send ready indication to the HDD. This will kick off the MAC |
| // into a 'running' state and should kick off an initial scan. |
| halStatus = sme_HDDReadyInd( pHddCtx->hHal ); |
| if ( !HAL_STATUS_SUCCESS( halStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%S: sme_HDDReadyInd() failed with status " |
| "code %08d [x%08x]",__func__, halStatus, halStatus ); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| // Set default LI into HDD context, |
| // otherwise under some race condition, HDD will set 0 LI value into RIVA, |
| // And RIVA will crash |
| wlan_cfgGetInt(pHddCtx->hHal, WNI_CFG_LISTEN_INTERVAL, &listenInterval); |
| pHddCtx->hdd_actual_LI_value = listenInterval; |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| |
| #ifndef ANI_MANF_DIAG |
| // Routine to initialize the PMU |
| void wlan_hdd_enable_deepsleep(v_VOID_t * pVosContext) |
| { |
| /*-------------- Need to fix this correctly while doing Deepsleep testing |
| tANI_U32 regValue = 0; |
| |
| regValue = QWLAN_PMU_LDO_CTRL_REG_PMU_ANA_DEEP_SLEEP_EN_MASK | |
| QWLAN_PMU_LDO_CTRL_REG_PMU_ANA_1P23_LPM_AON_MASK_MASK | |
| QWLAN_PMU_LDO_CTRL_REG_PMU_ANA_1P23_LPM_SW_MASK_MASK | |
| QWLAN_PMU_LDO_CTRL_REG_PMU_ANA_2P3_LPM_MASK_MASK; |
| |
| WLANBAL_WriteRegister(pVosContext, QWLAN_PMU_LDO_CTRL_REG_REG, regValue); |
| ---------------------*/ |
| |
| return; |
| } |
| #endif |
| #endif |
| |
| /* wake lock APIs for HDD */ |
| void hdd_prevent_suspend(void) |
| { |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,5)) |
| wake_lock(&wlan_wake_lock); |
| #else |
| wcnss_prevent_suspend(); |
| #endif |
| } |
| |
| void hdd_allow_suspend(void) |
| { |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,5)) |
| wake_unlock(&wlan_wake_lock); |
| #else |
| wcnss_allow_suspend(); |
| #endif |
| } |
| |
| void hdd_allow_suspend_timeout(v_U32_t timeout) |
| { |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,5)) |
| wake_lock_timeout(&wlan_wake_lock, timeout); |
| #else |
| /* Do nothing as there is no API in wcnss for timeout*/ |
| #endif |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_exchange_version_and_caps() - HDD function to exchange version and capability |
| information between Host and Riva |
| |
| This function gets reported version of FW |
| It also finds the version of Riva headers used to compile the host |
| It compares the above two and prints a warning if they are different |
| It gets the SW and HW version string |
| Finally, it exchanges capabilities between host and Riva i.e. host and riva exchange a msg |
| indicating the features they support through a bitmap |
| |
| \param - pHddCtx - Pointer to HDD context |
| |
| \return - void |
| |
| --------------------------------------------------------------------------*/ |
| |
| void hdd_exchange_version_and_caps(hdd_context_t *pHddCtx) |
| { |
| |
| tSirVersionType versionCompiled; |
| tSirVersionType versionReported; |
| tSirVersionString versionString; |
| tANI_U8 fwFeatCapsMsgSupported = 0; |
| VOS_STATUS vstatus; |
| |
| /* retrieve and display WCNSS version information */ |
| do { |
| |
| vstatus = sme_GetWcnssWlanCompiledVersion(pHddCtx->hHal, |
| &versionCompiled); |
| if (!VOS_IS_STATUS_SUCCESS(vstatus)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to retrieve WCNSS WLAN compiled version", |
| __FUNCTION__); |
| break; |
| } |
| |
| vstatus = sme_GetWcnssWlanReportedVersion(pHddCtx->hHal, |
| &versionReported); |
| if (!VOS_IS_STATUS_SUCCESS(vstatus)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to retrieve WCNSS WLAN reported version", |
| __FUNCTION__); |
| break; |
| } |
| |
| if ((versionCompiled.major != versionReported.major) || |
| (versionCompiled.minor != versionReported.minor) || |
| (versionCompiled.version != versionReported.version) || |
| (versionCompiled.revision != versionReported.revision)) |
| { |
| pr_err("%s: WCNSS WLAN Version %u.%u.%u.%u, " |
| "Host expected %u.%u.%u.%u\n", |
| WLAN_MODULE_NAME, |
| (int)versionReported.major, |
| (int)versionReported.minor, |
| (int)versionReported.version, |
| (int)versionReported.revision, |
| (int)versionCompiled.major, |
| (int)versionCompiled.minor, |
| (int)versionCompiled.version, |
| (int)versionCompiled.revision); |
| } |
| else |
| { |
| pr_info("%s: WCNSS WLAN version %u.%u.%u.%u\n", |
| WLAN_MODULE_NAME, |
| (int)versionReported.major, |
| (int)versionReported.minor, |
| (int)versionReported.version, |
| (int)versionReported.revision); |
| } |
| |
| vstatus = sme_GetWcnssSoftwareVersion(pHddCtx->hHal, |
| versionString, |
| sizeof(versionString)); |
| if (!VOS_IS_STATUS_SUCCESS(vstatus)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to retrieve WCNSS software version string", |
| __FUNCTION__); |
| break; |
| } |
| |
| pr_info("%s: WCNSS software version %s\n", |
| WLAN_MODULE_NAME, versionString); |
| |
| vstatus = sme_GetWcnssHardwareVersion(pHddCtx->hHal, |
| versionString, |
| sizeof(versionString)); |
| if (!VOS_IS_STATUS_SUCCESS(vstatus)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to retrieve WCNSS hardware version string", |
| __FUNCTION__); |
| break; |
| } |
| |
| pr_info("%s: WCNSS hardware version %s\n", |
| WLAN_MODULE_NAME, versionString); |
| |
| /* 1.Check if FW version is greater than 0.1.1.0. Only then send host-FW capability exchange message |
| 2.Host-FW capability exchange message is only present on riva 1.1 so |
| send the message only if it the riva is 1.1 |
| minor numbers for different riva branches: |
| 0 -> (1.0)Mainline Build |
| 1 -> (1.1)Mainline Build |
| 2->(1.04) Stability Build |
| */ |
| if (((versionReported.major>0) || (versionReported.minor>1) || |
| ((versionReported.minor>=1) && (versionReported.version>=1))) |
| && ((versionReported.major == 1) && (versionReported.minor >= 1))) |
| fwFeatCapsMsgSupported = 1; |
| |
| if (fwFeatCapsMsgSupported) |
| sme_featureCapsExchange(pHddCtx->hHal); |
| |
| } while (0); |
| |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_wlan_startup() - HDD init function |
| |
| This is the driver startup code executed once a WLAN device has been detected |
| |
| \param - dev - Pointer to the underlying device |
| |
| \return - 0 for success -1 for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| int hdd_wlan_startup(struct device *dev ) |
| { |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter = NULL; |
| hdd_adapter_t *pP2pAdapter = NULL; |
| hdd_context_t *pHddCtx = NULL; |
| v_CONTEXT_t pVosContext= NULL; |
| #ifdef WLAN_BTAMP_FEATURE |
| VOS_STATUS vStatus = VOS_STATUS_SUCCESS; |
| WLANBAP_ConfigType btAmpConfig; |
| hdd_config_t *pConfig; |
| #endif |
| int ret; |
| #ifdef CONFIG_CFG80211 |
| struct wiphy *wiphy; |
| #endif |
| #ifdef ANI_BUS_TYPE_SDIO |
| struct sdio_func *sdio_func_dev = dev_to_sdio_func(dev); |
| #endif //ANI_BUS_TYPE_SDIO |
| |
| ENTER(); |
| #ifdef CONFIG_CFG80211 |
| /* |
| * cfg80211: wiphy allocation |
| */ |
| wiphy = wlan_hdd_cfg80211_init(sizeof(hdd_context_t)) ; |
| |
| if(wiphy == NULL) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: cfg80211 init failed", __func__); |
| return -1; |
| } |
| |
| pHddCtx = wiphy_priv(wiphy); |
| |
| #else |
| |
| pHddCtx = vos_mem_malloc ( sizeof( hdd_context_t ) ); |
| if(pHddCtx == NULL) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: cfg80211 init failed", __func__); |
| return -1; |
| } |
| |
| #endif |
| //Initialize the adapter context to zeros. |
| vos_mem_zero(pHddCtx, sizeof( hdd_context_t )); |
| |
| #ifdef CONFIG_CFG80211 |
| pHddCtx->wiphy = wiphy; |
| #endif |
| hdd_prevent_suspend(); |
| pHddCtx->isLoadUnloadInProgress = TRUE; |
| |
| vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE); |
| |
| /*Get vos context here bcoz vos_open requires it*/ |
| pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); |
| |
| //Save the Global VOSS context in adapter context for future. |
| pHddCtx->pvosContext = pVosContext; |
| |
| //Save the adapter context in global context for future. |
| ((VosContextType*)(pVosContext))->pHDDContext = (v_VOID_t*)pHddCtx; |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| // Set the private data for the device to our adapter. |
| libra_sdio_setprivdata (sdio_func_dev, pHddCtx); |
| atomic_set(&pHddCtx->sdio_claim_count, 0); |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| pHddCtx->parent_dev = dev; |
| |
| init_completion(&pHddCtx->full_pwr_comp_var); |
| init_completion(&pHddCtx->standby_comp_var); |
| init_completion(&pHddCtx->req_bmps_comp_var); |
| init_completion(&pHddCtx->scan_info.scan_req_completion_event); |
| |
| hdd_list_init( &pHddCtx->hddAdapters, MAX_NUMBER_OF_ADAPTERS ); |
| |
| // Load all config first as TL config is needed during vos_open |
| pHddCtx->cfg_ini = (hdd_config_t*) kmalloc(sizeof(hdd_config_t), GFP_KERNEL); |
| if(pHddCtx->cfg_ini == NULL) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed kmalloc hdd_config_t",__func__); |
| goto err_free_hdd_context; |
| } |
| |
| vos_mem_zero(pHddCtx->cfg_ini, sizeof( hdd_config_t )); |
| |
| // Read and parse the qcom_cfg.ini file |
| status = hdd_parse_config_ini( pHddCtx ); |
| if ( VOS_STATUS_SUCCESS != status ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: error parsing %s", |
| __func__, WLAN_INI_FILE); |
| goto err_config; |
| } |
| |
| #ifdef CONFIG_CFG80211 |
| /* |
| * cfg80211: Initialization and registration ... |
| */ |
| if (0 < wlan_hdd_cfg80211_register(dev, wiphy, pHddCtx->cfg_ini)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: wlan_hdd_cfg80211_register return failure", __func__); |
| goto err_wiphy_reg; |
| } |
| #endif |
| |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| // Update WDI trace levels based upon the cfg.ini |
| hdd_wdi_trace_enable(eWLAN_MODULE_DAL, |
| pHddCtx->cfg_ini->wdiTraceEnableDAL); |
| hdd_wdi_trace_enable(eWLAN_MODULE_DAL_CTRL, |
| pHddCtx->cfg_ini->wdiTraceEnableCTL); |
| hdd_wdi_trace_enable(eWLAN_MODULE_DAL_DATA, |
| pHddCtx->cfg_ini->wdiTraceEnableDAT); |
| hdd_wdi_trace_enable(eWLAN_MODULE_PAL, |
| pHddCtx->cfg_ini->wdiTraceEnablePAL); |
| #endif /* FEATURE_WLAN_INTEGRATED_SOC */ |
| |
| #ifdef ANI_MANF_DIAG |
| if(VOS_FTM_MODE == hdd_get_conparam()) |
| { |
| if ( VOS_STATUS_SUCCESS != wlan_hdd_ftm_open(pHddCtx) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: wlan_hdd_ftm_open Failed",__func__); |
| goto err_free_hdd_context; |
| } |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: FTM driver loaded success fully",__func__); |
| return VOS_STATUS_SUCCESS; |
| } |
| #endif |
| |
| //Open watchdog module |
| if(pHddCtx->cfg_ini->fIsLogpEnabled) |
| { |
| status = vos_watchdog_open(pVosContext, |
| &((VosContextType*)pVosContext)->vosWatchdog, sizeof(VosWatchdogContext)); |
| |
| if(!VOS_IS_STATUS_SUCCESS( status )) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_watchdog_open failed",__func__); |
| #ifdef CONFIG_CFG80211 |
| goto err_wiphy_reg; |
| #else |
| goto err_config; |
| #endif |
| } |
| } |
| |
| pHddCtx->isLogpInProgress = FALSE; |
| vos_set_logp_in_progress(VOS_MODULE_ID_VOSS, FALSE); |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| status = WLANBAL_Open(pHddCtx->pvosContext); |
| if(!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to open BAL",__func__); |
| goto err_wdclose; |
| } |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| status = vos_chipVoteOnXOBuffer(NULL, NULL, NULL); |
| if(!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Failed to configure 19.2 MHz Clock", __func__); |
| #ifdef ANI_BUS_TYPE_SDIO |
| goto err_balclose; |
| #else |
| goto err_wdclose; |
| #endif |
| } |
| |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| status = WLANSAL_Start(pHddCtx->pvosContext); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Failed to start SAL",__func__); |
| goto err_clkvote; |
| } |
| |
| /* Start BAL */ |
| status = WLANBAL_Start(pHddCtx->pvosContext); |
| |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to start BAL",__func__); |
| goto err_salstop; |
| } |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| #ifdef MSM_PLATFORM_7x30 |
| /* FIXME: Volans 2.0 configuration. Reconfigure 1.3v SW supply to 1.3v. It will be configured to |
| * 1.4v in vos_ChipPowerup() routine above |
| */ |
| #endif |
| |
| status = vos_open( &pVosContext, 0); |
| if ( !VOS_IS_STATUS_SUCCESS( status )) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_open failed",__func__); |
| goto err_balstop; |
| } |
| |
| /* Save the hal context in Adapter */ |
| pHddCtx->hHal = (tHalHandle)vos_get_context( VOS_MODULE_ID_SME, pVosContext ); |
| |
| if ( NULL == pHddCtx->hHal ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HAL context is null",__func__); |
| goto err_vosclose; |
| } |
| |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| /* Vos preStart is calling */ |
| /* vos preStart which does cfg download should be called before set sme config which accesses/sets some cfgs */ |
| status = vos_preStart( pHddCtx->pvosContext ); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_preStart failed",__func__); |
| goto err_vosclose; |
| } |
| #endif |
| |
| // Set the SME configuration parameters... |
| status = hdd_set_sme_config( pHddCtx ); |
| |
| if ( VOS_STATUS_SUCCESS != status ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed hdd_set_sme_config",__func__); |
| goto err_vosclose; |
| } |
| |
| //Initialize the WMM module |
| status = hdd_wmm_init(pHddCtx); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: hdd_wmm_init failed", __FUNCTION__); |
| goto err_vosclose; |
| } |
| |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| /* In the integrated architecture we update the configuration from |
| the INI file and from NV before vOSS has been started so that |
| the final contents are available to send down to the cCPU */ |
| |
| // Apply the cfg.ini to cfg.dat |
| if (FALSE == hdd_update_config_dat(pHddCtx)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: config update failed",__func__ ); |
| goto err_vosclose; |
| } |
| |
| // Apply the NV to cfg.dat |
| /* Prima Update MAC address only at here */ |
| if (VOS_STATUS_SUCCESS != hdd_update_config_from_nv(pHddCtx)) |
| { |
| #ifdef WLAN_AUTOGEN_MACADDR_FEATURE |
| /* There was not a valid set of MAC Addresses in NV. See if the |
| default addresses were modified by the cfg.ini settings. If so, |
| we'll use them, but if not, we'll autogenerate a set of MAC |
| addresses based upon the device serial number */ |
| |
| static const v_MACADDR_t default_address = |
| {{0x00, 0x0A, 0xF5, 0x89, 0x89, 0xFF}}; |
| unsigned int serialno; |
| int i; |
| |
| serialno = wcnss_get_serial_number(); |
| if ((0 != serialno) && |
| (0 == memcmp(&default_address, &pHddCtx->cfg_ini->intfMacAddr[0], |
| sizeof(default_address)))) |
| { |
| /* cfg.ini has the default address, invoke autogen logic */ |
| |
| /* MAC address has 3 bytes of OUI so we have a maximum of 3 |
| bytes of the serial number that can be used to generate |
| the other 3 bytes of the MAC address. Mask off all but |
| the lower 3 bytes (this will also make sure we don't |
| overflow in the next step) */ |
| serialno &= 0x00FFFFFF; |
| |
| /* we need a unique address for each session */ |
| serialno *= VOS_MAX_CONCURRENCY_PERSONA; |
| |
| /* autogen all addresses */ |
| for (i = 0; i < VOS_MAX_CONCURRENCY_PERSONA; i++) |
| { |
| /* start with the entire default address */ |
| pHddCtx->cfg_ini->intfMacAddr[i] = default_address; |
| /* then replace the lower 3 bytes */ |
| pHddCtx->cfg_ini->intfMacAddr[i].bytes[3] = (serialno >> 16) & 0xFF; |
| pHddCtx->cfg_ini->intfMacAddr[i].bytes[4] = (serialno >> 8) & 0xFF; |
| pHddCtx->cfg_ini->intfMacAddr[i].bytes[5] = serialno & 0xFF; |
| |
| serialno++; |
| } |
| |
| pr_info("wlan: Invalid MAC addresses in NV, autogenerated " |
| MAC_ADDRESS_STR, |
| MAC_ADDR_ARRAY(pHddCtx->cfg_ini->intfMacAddr[0].bytes)); |
| } |
| else |
| #endif //WLAN_AUTOGEN_MACADDR_FEATURE |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Invalid MAC address in NV, using MAC from ini file " |
| MAC_ADDRESS_STR, __func__, |
| MAC_ADDR_ARRAY(pHddCtx->cfg_ini->intfMacAddr[0].bytes)); |
| } |
| } |
| { |
| eHalStatus halStatus; |
| // Set the MAC Address |
| // Currently this is used by HAL to add self sta. Remove this once self sta is added as part of session open. |
| halStatus = cfgSetStr( pHddCtx->hHal, WNI_CFG_STA_ID, |
| (v_U8_t *)&pHddCtx->cfg_ini->intfMacAddr[0], |
| sizeof( pHddCtx->cfg_ini->intfMacAddr[0]) ); |
| |
| if (!HAL_STATUS_SUCCESS( halStatus )) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Failed to set MAC Address. " |
| "HALStatus is %08d [x%08x]",__func__, halStatus, halStatus ); |
| return VOS_STATUS_E_FAILURE; |
| } |
| } |
| #endif // FEATURE_WLAN_INTEGRATED_SOC |
| |
| /*Start VOSS which starts up the SME/MAC/HAL modules and everything else |
| Note: Firmware image will be read and downloaded inside vos_start API */ |
| status = vos_start( pHddCtx->pvosContext ); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_start failed",__func__); |
| goto err_vosclose; |
| } |
| |
| /* Exchange capability info between Host and FW and also get versioning info from FW */ |
| hdd_exchange_version_and_caps(pHddCtx); |
| |
| status = hdd_post_voss_start_config( pHddCtx ); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_post_voss_start_config failed", |
| __func__); |
| goto err_vosstop; |
| } |
| |
| #ifdef WLAN_SOFTAP_FEATURE |
| if (VOS_STA_SAP_MODE == hdd_get_conparam()) |
| { |
| pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_SOFTAP, "softap.%d", |
| wlan_hdd_get_intf_addr(pHddCtx), FALSE ); |
| } |
| else |
| { |
| #endif |
| pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_INFRA_STATION, "wlan%d", |
| wlan_hdd_get_intf_addr(pHddCtx), FALSE ); |
| if (pAdapter != NULL) |
| { |
| #ifdef WLAN_FEATURE_P2P |
| if ( pHddCtx->cfg_ini->isP2pDeviceAddrAdministrated ) |
| { |
| vos_mem_copy( pHddCtx->p2pDeviceAddress.bytes, |
| pHddCtx->cfg_ini->intfMacAddr[0].bytes, |
| sizeof(tSirMacAddr)); |
| /* Generate the P2P Device Address. This consists of the device's |
| * primary MAC address with the locally administered bit set. |
| */ |
| pHddCtx->p2pDeviceAddress.bytes[0] |= 0x02; |
| } |
| else |
| { |
| tANI_U8* p2p_dev_addr = wlan_hdd_get_intf_addr(pHddCtx); |
| if (p2p_dev_addr != NULL) |
| { |
| vos_mem_copy(&pHddCtx->p2pDeviceAddress.bytes[0], |
| p2p_dev_addr, VOS_MAC_ADDR_SIZE); |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to allocate mac_address for p2p_device", |
| __FUNCTION__); |
| goto err_close_adapter; |
| } |
| } |
| |
| pP2pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_P2P_DEVICE, "p2p%d", |
| &pHddCtx->p2pDeviceAddress.bytes[0], FALSE ); |
| if ( NULL == pP2pAdapter ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to do hdd_open_adapter for P2P Device Interface", |
| __FUNCTION__); |
| goto err_close_adapter; |
| } |
| #endif |
| } |
| #ifdef WLAN_SOFTAP_FEATURE |
| } |
| #endif |
| |
| if( pAdapter == NULL ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: hdd_open_adapter failed",__func__); |
| #ifdef ANI_BUS_TYPE_SDIO |
| goto err_balstop; |
| #else |
| goto err_clkvote; |
| #endif |
| } |
| |
| #ifdef WLAN_BTAMP_FEATURE |
| vStatus = WLANBAP_Open(pVosContext); |
| if(!VOS_IS_STATUS_SUCCESS(vStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to open BAP",__func__); |
| goto err_close_adapter; |
| } |
| |
| vStatus = BSL_Init(pVosContext); |
| if(!VOS_IS_STATUS_SUCCESS(vStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to Init BSL",__func__); |
| goto err_bap_close; |
| } |
| vStatus = WLANBAP_Start(pVosContext); |
| if (!VOS_IS_STATUS_SUCCESS(vStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to start TL",__func__); |
| goto err_bap_close; |
| } |
| |
| pConfig = pHddCtx->cfg_ini; |
| btAmpConfig.ucPreferredChannel = pConfig->preferredChannel; |
| status = WLANBAP_SetConfig(&btAmpConfig); |
| |
| #endif //WLAN_BTAMP_FEATURE |
| |
| #ifdef FEATURE_WLAN_SCAN_PNO |
| /*SME must send channel update configuration to RIVA*/ |
| sme_UpdateChannelConfig(pHddCtx->hHal); |
| #endif |
| |
| #ifdef FEATURE_WLAN_INTEGRATED_SOC |
| /* Register with platform driver as client for Suspend/Resume */ |
| status = hddRegisterPmOps(pHddCtx); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddRegisterPmOps failed",__func__); |
| #ifdef WLAN_BTAMP_FEATURE |
| goto err_bap_stop; |
| #else |
| goto err_close_adapter; |
| #endif //WLAN_BTAMP_FEATURE |
| } |
| |
| /* Register TM level change handler function to the platform */ |
| status = hddDevTmRegisterNotifyCallback(pHddCtx); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDevTmRegisterNotifyCallback failed",__func__); |
| goto err_unregister_pmops; |
| } |
| #endif |
| |
| /* register for riva power on lock to platform driver */ |
| if (req_riva_power_on_lock("wlan")) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: req riva power on lock failed", |
| __func__); |
| goto err_unregister_pmops; |
| } |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| // Register suspend/resume callbacks |
| if(pHddCtx->cfg_ini->nEnableSuspend) |
| { |
| register_wlan_suspend(); |
| } |
| #endif |
| |
| // register net device notifier for device change notification |
| ret = register_netdevice_notifier(&hdd_netdev_notifier); |
| |
| if(ret < 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: register_netdevice_notifier failed",__func__); |
| goto err_free_power_on_lock; |
| } |
| |
| //Initialize the nlink service |
| if(nl_srv_init() != 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%S: nl_srv_init failed",__func__); |
| goto err_reg_netdev; |
| } |
| |
| //Initialize the BTC service |
| if(btc_activate_service(pHddCtx) != 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: btc_activate_service failed",__func__); |
| goto err_nl_srv; |
| } |
| |
| #ifdef PTT_SOCK_SVC_ENABLE |
| //Initialize the PTT service |
| if(ptt_sock_activate_svc(pHddCtx) != 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: ptt_sock_activate_svc failed",__func__); |
| goto err_nl_srv; |
| } |
| #endif |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| hdd_register_mcast_bcast_filter(pHddCtx); |
| #endif |
| #ifdef CONFIG_CFG80211 |
| #ifdef WLAN_SOFTAP_FEATURE |
| if (VOS_STA_SAP_MODE != hdd_get_conparam()) |
| #endif |
| { |
| wlan_hdd_cfg80211_post_voss_start(pP2pAdapter); |
| wlan_hdd_cfg80211_post_voss_start(pAdapter); |
| } |
| #endif |
| |
| mutex_init(&pHddCtx->sap_lock); |
| |
| pHddCtx->isLoadUnloadInProgress = FALSE; |
| |
| #ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK |
| /* Initialize the wake lcok */ |
| wake_lock_init(&pHddCtx->rx_wake_lock, |
| WAKE_LOCK_SUSPEND, |
| "qcom_rx_wakelock"); |
| #endif |
| |
| vos_event_init(&pHddCtx->scan_info.scan_finished_event); |
| pHddCtx->scan_info.scan_pending_option = WEXT_SCAN_PENDING_GIVEUP; |
| |
| vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, FALSE); |
| hdd_allow_suspend(); |
| |
| // Initialize the restart logic |
| wlan_hdd_restart_init(pHddCtx); |
| |
| goto success; |
| |
| err_nl_srv: |
| nl_srv_exit(); |
| |
| err_reg_netdev: |
| unregister_netdevice_notifier(&hdd_netdev_notifier); |
| |
| err_free_power_on_lock: |
| free_riva_power_on_lock("wlan"); |
| |
| err_unregister_pmops: |
| hddDevTmUnregisterNotifyCallback(pHddCtx); |
| hddDeregisterPmOps(pHddCtx); |
| |
| #ifdef WLAN_BTAMP_FEATURE |
| err_bap_stop: |
| WLANBAP_Stop(pVosContext); |
| #endif |
| |
| #ifdef WLAN_BTAMP_FEATURE |
| err_bap_close: |
| WLANBAP_Close(pVosContext); |
| #endif |
| |
| err_close_adapter: |
| hdd_close_all_adapters( pHddCtx ); |
| |
| err_vosstop: |
| vos_stop(pVosContext); |
| |
| err_vosclose: |
| status = vos_sched_close( pVosContext ); |
| if (!VOS_IS_STATUS_SUCCESS(status)) { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to close VOSS Scheduler", __func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( status ) ); |
| } |
| vos_close(pVosContext ); |
| |
| err_balstop: |
| #ifdef ANI_BUS_TYPE_SDIO |
| #ifndef ANI_MANF_DIAG |
| wlan_hdd_enable_deepsleep(pHddCtx->pvosContext); |
| #endif |
| |
| WLANBAL_Stop(pHddCtx->pvosContext); |
| WLANBAL_SuspendChip(pHddCtx->pvosContext); |
| #endif |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| err_salstop: |
| WLANSAL_Stop(pHddCtx->pvosContext); |
| |
| #endif |
| err_clkvote: |
| vos_chipVoteOffXOBuffer(NULL, NULL, NULL); |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| err_balclose: |
| WLANBAL_Close(pHddCtx->pvosContext); |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| err_wdclose: |
| if(pHddCtx->cfg_ini->fIsLogpEnabled) |
| vos_watchdog_close(pVosContext); |
| |
| #ifdef CONFIG_CFG80211 |
| err_wiphy_reg: |
| wiphy_unregister(wiphy) ; |
| #endif |
| |
| err_config: |
| kfree(pHddCtx->cfg_ini); |
| pHddCtx->cfg_ini= NULL; |
| |
| err_free_hdd_context: |
| hdd_allow_suspend(); |
| #ifdef CONFIG_CFG80211 |
| wiphy_free(wiphy) ; |
| //kfree(wdev) ; |
| #else |
| vos_mem_free( pHddCtx ); |
| #endif |
| VOS_BUG(1); |
| |
| return -1; |
| |
| success: |
| EXIT(); |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_driver_init() - Core Driver Init Function |
| |
| This is the driver entry point - called in different timeline depending |
| on whether the driver is statically or dynamically linked |
| |
| \param - None |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int hdd_driver_init( void) |
| { |
| VOS_STATUS status; |
| v_CONTEXT_t pVosContext = NULL; |
| #ifdef ANI_BUS_TYPE_SDIO |
| struct sdio_func *sdio_func_dev = NULL; |
| unsigned int attempts = 0; |
| #endif // ANI_BUS_TYPE_SDIO |
| struct device *dev = NULL; |
| int ret_status = 0; |
| |
| ENTER(); |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,5)) |
| wake_lock_init(&wlan_wake_lock, WAKE_LOCK_SUSPEND, "wlan"); |
| #endif |
| |
| pr_info("%s: loading driver v%s\n", WLAN_MODULE_NAME, |
| QWLAN_VERSIONSTR TIMER_MANAGER_STR MEMORY_DEBUG_STR); |
| |
| //Power Up Libra WLAN card first if not already powered up |
| status = vos_chipPowerUp(NULL,NULL,NULL); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Libra WLAN not Powered Up. " |
| "exiting", __func__); |
| return -1; |
| } |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| //SDIO Polling should be turned on for card detection. When using Android Wi-Fi GUI |
| //users need not trigger SDIO polling explicitly. However when loading drivers via |
| //command line (Adb shell), users must turn on SDIO polling prior to loading WLAN. |
| do { |
| sdio_func_dev = libra_getsdio_funcdev(); |
| if (NULL == sdio_func_dev) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Libra WLAN not detected yet.",__func__); |
| attempts++; |
| } |
| else { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Libra WLAN detecton succeeded",__func__); |
| dev = &sdio_func_dev->dev; |
| break; |
| } |
| |
| if(attempts == 7) |
| break; |
| |
| msleep(250); |
| |
| }while (attempts < 7); |
| |
| //Retry to detect the card again by Powering Down the chip and Power up the chip |
| //again. This retry is done to recover from CRC Error |
| if (NULL == sdio_func_dev) { |
| |
| attempts = 0; |
| |
| //Vote off any PMIC voltage supplies |
| vos_chipPowerDown(NULL, NULL, NULL); |
| |
| msleep(1000); |
| |
| //Power Up Libra WLAN card first if not already powered up |
| status = vos_chipPowerUp(NULL,NULL,NULL); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Retry Libra WLAN not Powered Up. " |
| "exiting", __func__); |
| return -1; |
| } |
| |
| do { |
| sdio_func_dev = libra_getsdio_funcdev(); |
| if (NULL == sdio_func_dev) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Retry Libra WLAN not detected yet.",__func__); |
| attempts++; |
| } |
| else { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Retry Libra WLAN detecton succeeded",__func__); |
| dev = &sdio_func_dev->dev; |
| break; |
| } |
| |
| if(attempts == 2) |
| break; |
| |
| msleep(1000); |
| |
| }while (attempts < 3); |
| } |
| |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| #ifdef ANI_BUS_TYPE_PCI |
| |
| dev = wcnss_wlan_get_device(); |
| |
| #endif // ANI_BUS_TYPE_PCI |
| |
| #ifdef ANI_BUS_TYPE_PLATFORM |
| dev = wcnss_wlan_get_device(); |
| #endif // ANI_BUS_TYPE_PLATFORM |
| |
| |
| do { |
| if (NULL == dev) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: WLAN device not found!!",__func__); |
| ret_status = -1; |
| break; |
| } |
| |
| #ifdef MEMORY_DEBUG |
| vos_mem_init(); |
| #endif |
| |
| #ifdef TIMER_MANAGER |
| vos_timer_manager_init(); |
| #endif |
| |
| /* Preopen VOSS so that it is ready to start at least SAL */ |
| status = vos_preOpen(&pVosContext); |
| |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed to preOpen VOSS", __func__); |
| ret_status = -1; |
| break; |
| } |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| /* Now Open SAL */ |
| status = WLANSAL_Open(pVosContext, 0); |
| |
| if(!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed to open SAL", __func__); |
| |
| /* If unable to open, cleanup and return failure */ |
| vos_preClose( &pVosContext ); |
| ret_status = -1; |
| break; |
| } |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| #ifndef MODULE |
| /* For statically linked driver, call hdd_set_conparam to update curr_con_mode |
| */ |
| hdd_set_conparam((v_UINT_t)con_mode); |
| #endif |
| |
| // Call our main init function |
| if(hdd_wlan_startup(dev)) { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: WLAN Driver Initialization failed", |
| __func__); |
| #ifdef ANI_BUS_TYPE_SDIO |
| WLANSAL_Close(pVosContext); |
| #endif // ANI_BUS_TYPE_SDIO |
| vos_preClose( &pVosContext ); |
| ret_status = -1; |
| break; |
| } |
| |
| /* Cancel the vote for XO Core ON |
| * This is done here for safety purposes in case we re-initialize without turning |
| * it OFF in any error scenario. |
| */ |
| hddLog(VOS_TRACE_LEVEL_INFO, "In module init: Ensure Force XO Core is OFF" |
| " when WLAN is turned ON so Core toggles" |
| " unless we enter PSD"); |
| if (vos_chipVoteXOCore(NULL, NULL, NULL, VOS_FALSE) != VOS_STATUS_SUCCESS) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "Could not cancel XO Core ON vote. Not returning failure." |
| " Power consumed will be high\n"); |
| } |
| } while (0); |
| |
| if (0 != ret_status) |
| { |
| //Assert Deep sleep signal now to put Libra HW in lowest power state |
| status = vos_chipAssertDeepSleep( NULL, NULL, NULL ); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( status) ); |
| |
| //Vote off any PMIC voltage supplies |
| vos_chipPowerDown(NULL, NULL, NULL); |
| #ifdef TIMER_MANAGER |
| vos_timer_exit(); |
| #endif |
| #ifdef MEMORY_DEBUG |
| vos_mem_exit(); |
| #endif |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,5)) |
| wake_lock_destroy(&wlan_wake_lock); |
| #endif |
| pr_err("%s: driver load failure\n", WLAN_MODULE_NAME); |
| } |
| else |
| { |
| //Send WLAN UP indication to Nlink Service |
| send_btc_nlink_msg(WLAN_MODULE_UP_IND, 0); |
| |
| pr_info("%s: driver loaded\n", WLAN_MODULE_NAME); |
| |
| } |
| |
| EXIT(); |
| |
| return ret_status; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_module_init() - Init Function |
| |
| This is the driver entry point (invoked when module is loaded using insmod) |
| |
| \param - None |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| #ifdef MODULE |
| static int __init hdd_module_init ( void) |
| { |
| return hdd_driver_init(); |
| } |
| #else /* #ifdef MODULE */ |
| static int __init hdd_module_init ( void) |
| { |
| /* Driver initialization is delayed to fwpath_changed_handler */ |
| return 0; |
| } |
| #endif /* #ifdef MODULE */ |
| |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_driver_exit() - Exit function |
| |
| This is the driver exit point (invoked when module is unloaded using rmmod |
| or con_mode was changed by userspace) |
| |
| \param - None |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| static void hdd_driver_exit(void) |
| { |
| hdd_context_t *pHddCtx = NULL; |
| v_CONTEXT_t pVosContext = NULL; |
| |
| pr_info("%s: unloading driver v%s\n", WLAN_MODULE_NAME, QWLAN_VERSIONSTR); |
| |
| //Get the global vos context |
| pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); |
| |
| if(!pVosContext) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__); |
| goto done; |
| } |
| |
| //Get the HDD context. |
| pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext ); |
| |
| if(!pHddCtx) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: module exit called before probe",__func__); |
| } |
| else |
| { |
| /* module exit should never proceed if SSR is not completed */ |
| while(isWDresetInProgress()){ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s:SSR in Progress; block rmmod for 1 second!!!",__func__); |
| msleep(1000); |
| } |
| |
| pHddCtx->isLoadUnloadInProgress = TRUE; |
| vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE); |
| |
| //Do all the cleanup before deregistering the driver |
| hdd_wlan_exit(pHddCtx); |
| } |
| |
| #ifdef ANI_BUS_TYPE_SDIO |
| WLANSAL_Close(pVosContext); |
| #endif // ANI_BUS_TYPE_SDIO |
| |
| vos_preClose( &pVosContext ); |
| |
| #ifdef TIMER_MANAGER |
| vos_timer_exit(); |
| #endif |
| #ifdef MEMORY_DEBUG |
| vos_mem_exit(); |
| #endif |
| |
| done: |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,5)) |
| wake_lock_destroy(&wlan_wake_lock); |
| #endif |
| pr_info("%s: driver unloaded\n", WLAN_MODULE_NAME); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_module_exit() - Exit function |
| |
| This is the driver exit point (invoked when module is unloaded using rmmod) |
| |
| \param - None |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| static void __exit hdd_module_exit(void) |
| { |
| hdd_driver_exit(); |
| } |
| |
| #ifdef MODULE |
| static int fwpath_changed_handler(const char *kmessage, |
| struct kernel_param *kp) |
| { |
| /* nothing to do when driver is DLKM */ |
| return 0; |
| } |
| |
| static int con_mode_handler(const char *kmessage, |
| struct kernel_param *kp) |
| { |
| return 0; |
| } |
| #else /* #ifdef MODULE */ |
| /**--------------------------------------------------------------------------- |
| |
| \brief fwpath_changed_handler() - Handler Function |
| |
| This is the driver entry point |
| - delayed driver initialization when driver is statically linked |
| - invoked when module parameter fwpath is modified from userpspace to signal |
| initializing the WLAN driver |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int fwpath_changed_handler(const char *kmessage, |
| struct kernel_param *kp) |
| { |
| int ret_status; |
| |
| if (!wlan_hdd_inited) { |
| ret_status = hdd_driver_init(); |
| wlan_hdd_inited = ret_status ? 0 : 1; |
| return ret_status; |
| } |
| |
| hdd_driver_exit(); |
| |
| msleep(200); |
| |
| ret_status = hdd_driver_init(); |
| wlan_hdd_inited = ret_status ? 0 : 1; |
| return ret_status; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief con_mode_handler() - |
| |
| Handler function for module param con_mode when it is changed by userspace |
| Dynamically linked - do nothing |
| Statically linked - exit and init driver, as in rmmod and insmod |
| |
| \param - |
| |
| \return - |
| |
| --------------------------------------------------------------------------*/ |
| static int con_mode_handler(const char *kmessage, |
| struct kernel_param *kp) |
| { |
| int ret = param_set_int(kmessage, kp); |
| |
| if (ret) |
| return ret; |
| |
| return fwpath_changed_handler(kmessage, kp); |
| } |
| #endif /* #ifdef MODULE */ |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_get_conparam() - |
| |
| This is the driver exit point (invoked when module is unloaded using rmmod) |
| |
| \param - None |
| |
| \return - tVOS_CON_MODE |
| |
| --------------------------------------------------------------------------*/ |
| tVOS_CON_MODE hdd_get_conparam ( void ) |
| { |
| #ifdef MODULE |
| return (tVOS_CON_MODE)con_mode; |
| #else |
| return (tVOS_CON_MODE)curr_con_mode; |
| #endif |
| } |
| void hdd_set_conparam ( v_UINT_t newParam ) |
| { |
| con_mode = newParam; |
| #ifndef MODULE |
| curr_con_mode = con_mode; |
| #endif |
| } |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_softap_sta_deauth() - function |
| |
| This to take counter measure to handle deauth req from HDD |
| |
| \param - pAdapter - Pointer to the HDD |
| |
| \param - enable - boolean value |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| |
| void hdd_softap_sta_deauth(hdd_adapter_t *pAdapter, v_U8_t *pDestMacAddress) |
| { |
| v_U8_t STAId; |
| v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext; |
| #ifdef FEATURE_WLAN_NON_INTEGRATED_SOC |
| tHalHandle hHalHandle; |
| #endif |
| |
| ENTER(); |
| |
| hddLog( LOGE, "hdd_softap_sta_deauth:(0x%x, false)", (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| |
| //Ignore request to deauth bcmc station |
| if( pDestMacAddress[0] & 0x1 ) |
| return; |
| |
| WLANSAP_DeauthSta(pVosContext,pDestMacAddress); |
| |
| /*Get the Station ID*/ |
| #ifdef FEATURE_WLAN_NON_INTEGRATED_SOC |
| hHalHandle = (tHalHandle ) vos_get_context(VOS_MODULE_ID_HAL, pVosContext); |
| if (eHAL_STATUS_SUCCESS == |
| halTable_FindStaidByAddr(hHalHandle, (tANI_U8 *)pDestMacAddress, |
| &STAId)) |
| { |
| hdd_softap_DeregisterSTA(pAdapter, STAId); |
| } |
| #else |
| if (VOS_STATUS_SUCCESS == |
| hdd_softap_GetStaId(pAdapter, (v_MACADDR_t *)pDestMacAddress, |
| &STAId)) |
| { |
| hdd_softap_DeregisterSTA(pAdapter, STAId); |
| } |
| #endif |
| |
| EXIT(); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_softap_sta_disassoc() - function |
| |
| This to take counter measure to handle deauth req from HDD |
| |
| \param - pAdapter - Pointer to the HDD |
| |
| \param - enable - boolean value |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| |
| void hdd_softap_sta_disassoc(hdd_adapter_t *pAdapter,v_U8_t *pDestMacAddress) |
| { |
| v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext; |
| |
| ENTER(); |
| |
| hddLog( LOGE, "hdd_softap_sta_disassoc:(0x%x, false)", (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| |
| //Ignore request to disassoc bcmc station |
| if( pDestMacAddress[0] & 0x1 ) |
| return; |
| |
| WLANSAP_DisassocSta(pVosContext,pDestMacAddress); |
| } |
| |
| void hdd_softap_tkip_mic_fail_counter_measure(hdd_adapter_t *pAdapter,v_BOOL_t enable) |
| { |
| v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext; |
| |
| ENTER(); |
| |
| hddLog( LOGE, "hdd_softap_tkip_mic_fail_counter_measure:(0x%x, false)", (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| |
| WLANSAP_SetCounterMeasure(pVosContext, (v_BOOL_t)enable); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief hdd_get__concurrency_mode() - |
| * |
| * |
| * \param - None |
| * |
| * \return - CONCURRENCY MODE |
| * |
| * --------------------------------------------------------------------------*/ |
| tVOS_CONCURRENCY_MODE hdd_get_concurrency_mode ( void ) |
| { |
| v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL ); |
| hdd_context_t *pHddCtx; |
| |
| if (NULL != pVosContext) |
| { |
| pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext); |
| if (NULL != pHddCtx) |
| { |
| return (tVOS_CONCURRENCY_MODE)pHddCtx->concurrency_mode; |
| } |
| } |
| |
| /* we are in an invalid state :( */ |
| hddLog(LOGE, "%s: Invalid context", __FUNCTION__); |
| return VOS_STA; |
| } |
| |
| /* Decide whether to allow/not the apps power collapse. |
| * Allow apps power collapse if we are in connected state. |
| * if not, allow only if we are in IMPS */ |
| v_BOOL_t hdd_is_apps_power_collapse_allowed(hdd_context_t* pHddCtx) |
| { |
| tPmcState pmcState = pmcGetPmcState(pHddCtx->hHal); |
| hdd_config_t *pConfig = pHddCtx->cfg_ini; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter = NULL; |
| VOS_STATUS status; |
| |
| #ifdef WLAN_SOFTAP_FEATURE |
| if (VOS_STA_SAP_MODE == hdd_get_conparam()) |
| return TRUE; |
| #endif |
| |
| /*loop through all adapters. TBD fix for Concurrency */ |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| if ( (WLAN_HDD_INFRA_STATION == pAdapter->device_mode) |
| || (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ) |
| { |
| if ((pConfig->fIsImpsEnabled || pConfig->fIsBmpsEnabled) |
| && (pmcState != IMPS && pmcState != BMPS |
| && pmcState != STOPPED && pmcState != STANDBY)) |
| { |
| return FALSE; |
| } |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| return TRUE; |
| } |
| |
| void wlan_hdd_set_concurrency_mode(hdd_context_t *pHddCtx, tVOS_CON_MODE mode) |
| { |
| switch(mode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| #ifdef WLAN_FEATURE_P2P |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_GO: |
| #endif |
| case WLAN_HDD_SOFTAP: |
| pHddCtx->concurrency_mode |= (1 << mode); |
| pHddCtx->no_of_sessions[mode]++; |
| break; |
| default: |
| break; |
| |
| } |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: concurrency_mode = 0x%x NumberofSessions for mode %d = %d", |
| __func__,pHddCtx->concurrency_mode,mode,pHddCtx->no_of_sessions[mode]); |
| } |
| |
| |
| void wlan_hdd_clear_concurrency_mode(hdd_context_t *pHddCtx, tVOS_CON_MODE mode) |
| { |
| switch(mode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| #ifdef WLAN_FEATURE_P2P |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_GO: |
| #endif |
| case WLAN_HDD_SOFTAP: |
| pHddCtx->no_of_sessions[mode]--; |
| if (!(pHddCtx->no_of_sessions[mode])) |
| pHddCtx->concurrency_mode &= (~(1 << mode)); |
| break; |
| default: |
| break; |
| } |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: concurrency_mode = 0x%x NumberofSessions for mode %d = %d", |
| __func__,pHddCtx->concurrency_mode,mode,pHddCtx->no_of_sessions[mode]); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_restart_init |
| * |
| * This function initalizes restart timer/flag. An internal function. |
| * |
| * \param - pHddCtx |
| * |
| * \return - None |
| * |
| * --------------------------------------------------------------------------*/ |
| |
| static void wlan_hdd_restart_init(hdd_context_t *pHddCtx) |
| { |
| /* Initialize */ |
| pHddCtx->hdd_restart_retries = 0; |
| atomic_set(&pHddCtx->isRestartInProgress, 0); |
| vos_timer_init(&pHddCtx->hdd_restart_timer, |
| VOS_TIMER_TYPE_SW, |
| wlan_hdd_restart_timer_cb, |
| pHddCtx); |
| } |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_restart_deinit |
| * |
| * This function cleans up the resources used. An internal function. |
| * |
| * \param - pHddCtx |
| * |
| * \return - None |
| * |
| * --------------------------------------------------------------------------*/ |
| |
| static void wlan_hdd_restart_deinit(hdd_context_t* pHddCtx) |
| { |
| |
| VOS_STATUS vos_status; |
| /* Block any further calls */ |
| atomic_set(&pHddCtx->isRestartInProgress, 1); |
| /* Cleanup */ |
| vos_status = vos_timer_stop( &pHddCtx->hdd_restart_timer ); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGW, FL("Failed to stop HDD restart timer")); |
| vos_status = vos_timer_destroy(&pHddCtx->hdd_restart_timer); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGW, FL("Failed to destroy HDD restart timer")); |
| |
| } |
| |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_framework_restart |
| * |
| * This function uses a cfg80211 API to start a framework initiated WLAN |
| * driver module unload/load. |
| * |
| * Also this API keep retrying (WLAN_HDD_RESTART_RETRY_MAX_CNT). |
| * |
| * |
| * \param - pHddCtx |
| * |
| * \return - VOS_STATUS_SUCCESS: Success |
| * VOS_STATUS_E_EMPTY: Adapter is Empty |
| * VOS_STATUS_E_NOMEM: No memory |
| |
| * --------------------------------------------------------------------------*/ |
| |
| static VOS_STATUS wlan_hdd_framework_restart(hdd_context_t *pHddCtx) |
| { |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| int len = (sizeof (struct ieee80211_mgmt)); |
| struct ieee80211_mgmt *mgmt = NULL; |
| |
| /* Prepare the DEAUTH managment frame with reason code */ |
| mgmt = kzalloc(len, GFP_KERNEL); |
| if(mgmt == NULL) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: memory allocatoin failed (%d bytes)", __func__, len); |
| return VOS_STATUS_E_NOMEM; |
| } |
| mgmt->u.deauth.reason_code = WLAN_REASON_DISASSOC_LOW_ACK; |
| |
| /* Iterate over all adapters/devices */ |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| do |
| { |
| if( (status == VOS_STATUS_SUCCESS) && |
| pAdapterNode && |
| pAdapterNode->pAdapter) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "restarting the driver(intf:\'%s\' mode:%d :try %d)", |
| pAdapterNode->pAdapter->dev->name, |
| pAdapterNode->pAdapter->device_mode, |
| pHddCtx->hdd_restart_retries + 1); |
| /* |
| * CFG80211 event to restart the driver |
| * |
| * 'cfg80211_send_unprot_deauth' sends a |
| * NL80211_CMD_UNPROT_DEAUTHENTICATE event to supplicant at any state |
| * of SME(Linux Kernel) state machine. |
| * |
| * Reason code WLAN_REASON_DISASSOC_LOW_ACK is currently used to restart |
| * the driver. |
| * |
| */ |
| |
| cfg80211_send_unprot_deauth(pAdapterNode->pAdapter->dev, (u_int8_t*)mgmt, len ); |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } while((NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status)); |
| |
| |
| /* Free the allocated management frame */ |
| kfree(mgmt); |
| |
| /* Retry until we unload or reach max count */ |
| if(++pHddCtx->hdd_restart_retries < WLAN_HDD_RESTART_RETRY_MAX_CNT) |
| vos_timer_start(&pHddCtx->hdd_restart_timer, WLAN_HDD_RESTART_RETRY_DELAY_MS); |
| |
| return status; |
| |
| } |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_restart_timer_cb |
| * |
| * Restart timer callback. An internal function. |
| * |
| * \param - User data: |
| * |
| * \return - None |
| * |
| * --------------------------------------------------------------------------*/ |
| |
| void wlan_hdd_restart_timer_cb(v_PVOID_t usrDataForCallback) |
| { |
| hdd_context_t *pHddCtx = usrDataForCallback; |
| wlan_hdd_framework_restart(pHddCtx); |
| return; |
| |
| } |
| |
| |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_restart_driver |
| * |
| * This function sends an event to supplicant to restart the WLAN driver. |
| * |
| * This function is called from vos_wlanRestart. |
| * |
| * \param - pHddCtx |
| * |
| * \return - VOS_STATUS_SUCCESS: Success |
| * VOS_STATUS_E_EMPTY: Adapter is Empty |
| * VOS_STATUS_E_ALREADY: Request already in progress |
| |
| * --------------------------------------------------------------------------*/ |
| VOS_STATUS wlan_hdd_restart_driver(hdd_context_t *pHddCtx) |
| { |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| |
| /* A tight check to make sure reentrancy */ |
| if(atomic_xchg(&pHddCtx->isRestartInProgress, 1)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "%s: WLAN restart is already in progress", __func__); |
| |
| return VOS_STATUS_E_ALREADY; |
| } |
| /* when WLAN driver is statically linked, then invoke SSR by sending |
| * the reset interrupt. If it is DLKM, then use restart API |
| */ |
| #ifdef MODULE |
| status = wlan_hdd_framework_restart(pHddCtx); |
| #else |
| wcnss_reset_intr(); |
| #endif |
| |
| return status; |
| } |
| |
| |
| //Register the module init/exit functions |
| module_init(hdd_module_init); |
| module_exit(hdd_module_exit); |
| |
| MODULE_LICENSE("Dual BSD/GPL"); |
| MODULE_AUTHOR("Qualcomm Atheros, Inc."); |
| MODULE_DESCRIPTION("WLAN HOST DEVICE DRIVER"); |
| |
| module_param_call(con_mode, con_mode_handler, param_get_int, &con_mode, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| |
| module_param_call(fwpath, fwpath_changed_handler, param_get_string, fwpath, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |